Merge "Fix NPE due to a null package name of system enforcing admin." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 7d9e95b..59a7cbc 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,6 +21,7 @@
// !!! KEEP THIS LIST ALPHABETICAL !!!
"aconfig_mediacodec_flags_java_lib",
"android.adaptiveauth.flags-aconfig-java",
+ "android.app.appfunctions.flags-aconfig-java",
"android.app.contextualsearch.flags-aconfig-java",
"android.app.flags-aconfig-java",
"android.app.ondeviceintelligence-aconfig-java",
@@ -172,6 +173,7 @@
// Window
aconfig_declarations {
name: "com.android.window.flags.window-aconfig",
+ exportable: true,
package: "com.android.window.flags",
container: "system",
srcs: ["core/java/android/window/flags/*.aconfig"],
@@ -1382,6 +1384,21 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// AppFunctions
+aconfig_declarations {
+ name: "android.app.appfunctions.flags-aconfig",
+ exportable: true,
+ package: "android.app.appfunctions.flags",
+ container: "system",
+ srcs: ["core/java/android/app/appfunctions/flags/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.appfunctions.flags-aconfig-java",
+ aconfig_declarations: "android.app.appfunctions.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Adaptive Auth
aconfig_declarations {
name: "android.adaptiveauth.flags-aconfig",
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index 06ead06..9b1016e 100644
--- a/INPUT_OWNERS
+++ b/INPUT_OWNERS
@@ -1,4 +1,5 @@
# Bug component: 136048
+# Please assign bugs to android-framework-input-triage@.
arpitks@google.com
asmitapoddar@google.com
hcutts@google.com
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 2587969..5f32ba0 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -43,29 +43,14 @@
],
out: [
"ravenwood.jar",
-
- // Following files are created just as FYI.
- "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",
],
}
framework_minus_apex_cmd = "$(location hoststubgen) " +
"@$(location :ravenwood-standard-options) " +
-
"--debug-log $(location hoststubgen_framework-minus-apex.log) " +
- "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " +
- "--supported-api-list-file $(location hoststubgen_framework-minus-apex_apis.csv) " +
-
"--out-impl-jar $(location ravenwood.jar) " +
-
- "--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) " +
"--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) "
@@ -133,13 +118,26 @@
// Build framework-minus-apex.ravenwood-base without sharding.
// We extract the various dump files from this one, rather than the sharded ones, because
// some dumps use the output from other classes (e.g. base classes) which may not be in the
-// same shard.
+// same shard. Also some of the dump files ("apis") may be slow even when sharded, because
+// the output contains the information from all the input classes, rather than the output classes.
// Not using sharding is fine for this module because it's only used for collecting the
// dump / stats files, which don't have to happen regularly.
java_genrule {
name: "framework-minus-apex.ravenwood-base_all",
defaults: ["framework-minus-apex.ravenwood-base_defaults"],
- cmd: framework_minus_apex_cmd,
+ cmd: framework_minus_apex_cmd +
+ "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " +
+ "--supported-api-list-file $(location hoststubgen_framework-minus-apex_apis.csv) " +
+
+ "--gen-keep-all-file $(location hoststubgen_framework-minus-apex_keep_all.txt) " +
+ "--gen-input-dump-file $(location hoststubgen_framework-minus-apex_dump.txt) ",
+
+ out: [
+ "hoststubgen_framework-minus-apex_keep_all.txt",
+ "hoststubgen_framework-minus-apex_dump.txt",
+ "hoststubgen_framework-minus-apex_stats.csv",
+ "hoststubgen_framework-minus-apex_apis.csv",
+ ],
}
// Marge all the sharded jars
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 49384cd..5db0772 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -137,13 +137,14 @@
"name": "CtsStrictJavaPackagesTestCases"
}
],
- "postsubmit-ravenwood": [
+ "ravenwood-presubmit": [
{
"name": "CtsUtilTestCasesRavenwood",
- "host": true,
- "file_patterns": [
- "[Rr]avenwood"
- ]
+ "host": true
+ },
+ {
+ "name": "RavenwoodBivalentTest",
+ "host": true
}
],
"postsubmit-managedprofile-stress": [
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index d7b1c9a2..f20b170 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -43,7 +43,6 @@
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -143,7 +142,7 @@
// Always use the same server for consistency across the benchmarks.
server = config.serverFactory().newServer(
- ChannelType.CHANNEL, config.messageSize(), config.protocol().getProtocols(),
+ config.messageSize(), config.protocol().getProtocols(),
ciphers(config));
server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
@@ -197,7 +196,6 @@
*/
@Test
@Parameters(method = "getParams")
- @Ignore("b/351034205")
public void time(Config config) throws Exception {
reset();
setup(config);
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
index 0655f45..ba2acb8 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
@@ -43,10 +43,10 @@
factories.clientFactory, channelType, port, protocols, ciphers);
}
- public ServerEndpoint newServer(ChannelType channelType, int messageSize,
+ public ServerEndpoint newServer(int messageSize,
String[] protocols, String[] ciphers) throws IOException {
return new ServerEndpoint(factories.serverFactory, factories.serverSocketFactory,
- channelType, messageSize, protocols, ciphers);
+ messageSize, protocols, ciphers);
}
private static final class Factories {
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
index 3631c3f..1e4f124 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
@@ -34,8 +34,6 @@
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
-import org.conscrypt.ChannelType;
-
/**
* A simple socket-based test server.
*/
@@ -63,7 +61,6 @@
}
private final ServerSocket serverSocket;
- private final ChannelType channelType;
private final SSLSocketFactory socketFactory;
private final int messageSize;
private final String[] protocols;
@@ -78,11 +75,10 @@
private volatile Future<?> processFuture;
ServerEndpoint(SSLSocketFactory socketFactory, SSLServerSocketFactory serverSocketFactory,
- ChannelType channelType, int messageSize, String[] protocols,
+ int messageSize, String[] protocols,
String[] cipherSuites) throws IOException {
- this.serverSocket = channelType.newServerSocket(serverSocketFactory);
+ this.serverSocket = serverSocketFactory.createServerSocket();
this.socketFactory = socketFactory;
- this.channelType = channelType;
this.messageSize = messageSize;
this.protocols = protocols;
this.cipherSuites = cipherSuites;
@@ -134,7 +130,7 @@
if (stopping) {
return;
}
- socket = channelType.accept(serverSocket, socketFactory);
+ socket = (SSLSocket) serverSocket.accept();
socket.setEnabledProtocols(protocols);
socket.setEnabledCipherSuites(cipherSuites);
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index 8916a3c..af3c405 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -43,7 +43,6 @@
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -131,8 +130,7 @@
final ChannelType channelType = config.channelType();
- server = config.serverFactory().newServer(
- channelType, config.messageSize(),
+ server = config.serverFactory().newServer(config.messageSize(),
new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
server.setMessageProcessor(new MessageProcessor() {
@Override
@@ -202,7 +200,6 @@
@Test
@Parameters(method = "getParams")
- @Ignore("b/351034205")
public void throughput(Config config) throws Exception {
setup(config);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
index c34936f..fbe67a4 100644
--- a/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
@@ -19,7 +19,6 @@
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
-import android.graphics.Typeface;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
@@ -121,34 +120,13 @@
public void testSetFontVariationSettings() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final Paint paint = new Paint(PAINT);
+ final Random random = new Random(0);
while (state.keepRunning()) {
state.pauseTiming();
- paint.setTypeface(null);
- paint.setFontVariationSettings(null);
- Typeface.clearTypefaceCachesForTestingPurpose();
+ int weight = random.nextInt(1000);
state.resumeTiming();
- paint.setFontVariationSettings("'wght' 450");
+ paint.setFontVariationSettings("'wght' " + weight);
}
- Typeface.clearTypefaceCachesForTestingPurpose();
}
-
- @Test
- public void testSetFontVariationSettings_Cached() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Paint paint = new Paint(PAINT);
- Typeface.clearTypefaceCachesForTestingPurpose();
-
- while (state.keepRunning()) {
- state.pauseTiming();
- paint.setTypeface(null);
- paint.setFontVariationSettings(null);
- state.resumeTiming();
-
- paint.setFontVariationSettings("'wght' 450");
- }
-
- Typeface.clearTypefaceCachesForTestingPurpose();
- }
-
}
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 856dba3..9eea712 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -45,3 +45,8 @@
"trace_configs/trace_config_multi_user.textproto",
],
}
+
+prebuilt_etc {
+ name: "trace_config_multi_user.textproto",
+ src: ":multi_user_trace_config",
+}
diff --git a/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java b/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
index c00c8d5..06cd942 100644
--- a/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
+++ b/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
@@ -36,7 +36,7 @@
@RunWith(AndroidJUnit4.class)
public final class SettingsProviderPerfTest {
- private static final String NAMESPACE = "test@namespace";
+ private static final String NAMESPACE = "testing";
private static final String SETTING_NAME1 = "test:setting1";
private static final String SETTING_NAME2 = "test-setting2";
private static final String UNSET_SETTING = "test_unset_setting";
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 18ee6f2..0f3b1c3 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -319,7 +319,7 @@
*/
int mSystemUiUid;
- static boolean isTimeTickAlarm(Alarm a) {
+ private static boolean isTimeTickAlarm(Alarm a) {
return a.uid == Process.SYSTEM_UID && TIME_TICK_TAG.equals(a.listenerTag);
}
@@ -357,6 +357,7 @@
}
// TODO(b/172085676): Move inside alarm store.
+ @GuardedBy("mLock")
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
new SparseArray<>();
private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray =
@@ -2616,6 +2617,13 @@
mInFlightListeners.add(callback);
}
}
+
+ /** @see AlarmManagerInternal#getNextAlarmTriggerTimeForUser(int) */
+ @Override
+ public long getNextAlarmTriggerTimeForUser(@UserIdInt int userId) {
+ final AlarmManager.AlarmClockInfo nextAlarm = getNextAlarmClockImpl(userId);
+ return nextAlarm != null ? nextAlarm.getTriggerTime() : 0;
+ }
}
boolean hasUseExactAlarmInternal(String packageName, int uid) {
@@ -3947,6 +3955,9 @@
if (!RemovedAlarm.isLoggable(reason)) {
continue;
}
+ if (isTimeTickAlarm(removed)) {
+ Slog.wtf(TAG, "Removed TIME_TICK alarm");
+ }
RingBuffer<RemovedAlarm> bufferForUid = mRemovalHistory.get(removed.uid);
if (bufferForUid == null) {
bufferForUid = new RingBuffer<>(RemovedAlarm.class, REMOVAL_HISTORY_SIZE_PER_UID);
@@ -4441,6 +4452,11 @@
public void run() {
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
+ synchronized (mLock) {
+ mLastTimeChangeClockTime = mInjector.getCurrentTimeMillis();
+ mLastTimeChangeRealtime = mInjector.getElapsedRealtimeMillis();
+ }
+
while (true) {
int result = mInjector.waitForAlarm();
final long nowRTC = mInjector.getCurrentTimeMillis();
@@ -4464,10 +4480,9 @@
expectedClockTime = lastTimeChangeClockTime
+ (nowELAPSED - mLastTimeChangeRealtime);
}
- if (lastTimeChangeClockTime == 0 || nowRTC < (expectedClockTime - 1000)
+ if (nowRTC < (expectedClockTime - 1000)
|| nowRTC > (expectedClockTime + 1000)) {
- // The change is by at least +/- 1000 ms (or this is the first change),
- // let's do it!
+ // The change is by at least +/- 1000 ms, let's do it!
if (DEBUG_BATCH) {
Slog.v(TAG, "Time changed notification from kernel; rebatching");
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
index 0073335..020b510 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -17,11 +17,9 @@
package com.android.server.alarm;
import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
-import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
import android.app.AlarmManager;
import android.util.IndentingPrintWriter;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -88,11 +86,6 @@
if (removed.alarmClock != null && mOnAlarmClockRemoved != null) {
mOnAlarmClockRemoved.run();
}
- if (isTimeTickAlarm(removed)) {
- // This code path is not invoked when delivering alarms, only when removing
- // alarms due to the caller cancelling it or getting uninstalled, etc.
- Slog.wtf(TAG, "Removed TIME_TICK alarm");
- }
removedAlarms.add(removed);
}
}
diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING
index fd571c9..b78659c 100644
--- a/core/TEST_MAPPING
+++ b/core/TEST_MAPPING
@@ -1,18 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.inputmethod"
- },
- {
- "include-filter": "com.android.internal.inputmethod"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ],
+ "name": "FrameworksCoreTests_inputmethod",
"file_patterns": [
"core/java/com/android/internal/inputmethod/.*",
"core/java/android/view/inputmethod/.*",
diff --git a/core/api/current.txt b/core/api/current.txt
index ea039a7..7f2b004 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6844,9 +6844,6 @@
method public android.app.Notification.MessagingStyle.Message setData(String, android.net.Uri);
}
- @FlaggedApi("android.app.api_rich_ongoing") public abstract static class Notification.RichOngoingStyle extends android.app.Notification.Style {
- }
-
public abstract static class Notification.Style {
ctor @Deprecated public Notification.Style();
method public android.app.Notification build();
@@ -10684,6 +10681,7 @@
field public static final String ACTIVITY_SERVICE = "activity";
field public static final String ALARM_SERVICE = "alarm";
field public static final String APPWIDGET_SERVICE = "appwidget";
+ field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String APP_FUNCTION_SERVICE = "app_function";
field public static final String APP_OPS_SERVICE = "appops";
field public static final String APP_SEARCH_SERVICE = "app_search";
field public static final String AUDIO_SERVICE = "audio";
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 90de7ab..4fb35c3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7312,7 +7312,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void finish(int finishTask) {
if (DEBUG_FINISH_ACTIVITY) {
- Log.d("Instrumentation", "finishActivity: finishTask=" + finishTask, new Throwable());
+ Log.d(Instrumentation.TAG, "finishActivity: finishTask=" + finishTask, new Throwable());
}
if (mParent == null) {
int resultCode;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 74e9583..be70de2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,7 @@
package android.app;
+import static android.app.Instrumentation.DEBUG_FINISH_ACTIVITY;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -80,6 +81,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.Singleton;
import android.util.Size;
import android.view.WindowInsetsController.Appearance;
@@ -6011,6 +6013,10 @@
* Finishes all activities in this task and removes it from the recent tasks list.
*/
public void finishAndRemoveTask() {
+ if (DEBUG_FINISH_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "AppTask#finishAndRemoveTask: task="
+ + getTaskInfo(), new Throwable());
+ }
try {
mAppTaskImpl.finishAndRemoveTask();
} catch (RemoteException e) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 89efa9b..d318812 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -961,6 +961,17 @@
@Nullable VoiceInteractionManagerProvider provider);
/**
+ * Get whether or not the previous user's packages will be killed before the user is
+ * stopped during a user switch.
+ *
+ * <p> The primary use case of this method is for {@link com.android.server.SystemService}
+ * classes to call this API in their
+ * {@link com.android.server.SystemService#onUserSwitching} method implementation to prevent
+ * restarting any of the previous user's processes that will be killed during the user switch.
+ */
+ public abstract boolean isEarlyPackageKillEnabledForUserSwitch(int fromUserId, int toUserId);
+
+ /**
* Sets whether the current foreground user (and its profiles) should be stopped after switched
* out.
*/
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 55ce90d..c876921 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -226,7 +226,7 @@
in IBinder activityToken, int flags);
boolean isAssistDataAllowed();
boolean requestAssistDataForTask(in IAssistDataReceiver receiver, int taskId,
- in String callingPackageName, String callingAttributionTag);
+ in String callingPackageName, String callingAttributionTag, boolean fetchStructure);
/**
* Notify the system that the keyguard is going away.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index be27046..45852c7 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -98,7 +98,7 @@
*/
public static final String REPORT_KEY_STREAMRESULT = "stream";
- private static final String TAG = "Instrumentation";
+ static final String TAG = "Instrumentation";
private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ef09dc4..db979a5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -10979,18 +10979,6 @@
}
/**
- * An object that can apply a rich ongoing notification style to a {@link Notification.Builder}
- * object.
- */
- @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
- public abstract static class RichOngoingStyle extends Notification.Style {
- /**
- * @hide
- */
- public RichOngoingStyle() {}
- }
-
- /**
* Notification style for custom views that are decorated by the system
*
* <p>Instead of providing a notification that is completely custom, a developer can set this
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index fd4d8e9..0cc210b 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1836,9 +1836,10 @@
// have shared library asset paths appended if there are any.
if (r.getImpl() != null) {
final ResourcesImpl oldImpl = r.getImpl();
+ final AssetManager oldAssets = oldImpl.getAssets();
// ResourcesImpl constructor will help to append shared library asset paths.
- if (oldImpl.getAssets().isUpToDate()) {
- final ResourcesImpl newImpl = new ResourcesImpl(oldImpl.getAssets(),
+ if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) {
+ final ResourcesImpl newImpl = new ResourcesImpl(oldAssets,
oldImpl.getMetrics(), oldImpl.getConfiguration(),
oldImpl.getDisplayAdjustments());
r.setImpl(newImpl);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e73f471..114a2c4 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.adservices.AdServicesFrameworkInitializer;
@@ -28,6 +30,8 @@
import android.app.admin.IDevicePolicyManager;
import android.app.ambientcontext.AmbientContextManager;
import android.app.ambientcontext.IAmbientContextManager;
+import android.app.appfunctions.AppFunctionManager;
+import android.app.appfunctions.IAppFunctionManager;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
import android.app.contentsuggestions.ContentSuggestionsManager;
@@ -925,6 +929,21 @@
return new CompanionDeviceManager(service, ctx.getOuterContext());
}});
+ if (enableAppFunctionManager()) {
+ registerService(Context.APP_FUNCTION_SERVICE, AppFunctionManager.class,
+ new CachedServiceFetcher<>() {
+ @Override
+ public AppFunctionManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IAppFunctionManager service;
+ //TODO(b/357551503): If the feature not present avoid look up every time
+ service = IAppFunctionManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.APP_FUNCTION_SERVICE));
+ return new AppFunctionManager(service, ctx.getOuterContext());
+ }
+ });
+ }
+
registerService(Context.VIRTUAL_DEVICE_SERVICE, VirtualDeviceManager.class,
new CachedServiceFetcher<VirtualDeviceManager>() {
@Override
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 0deb842..b7f672c 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -153,18 +153,7 @@
"file_patterns": ["(/|^)ContextImpl.java"]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.content.ContextTest"
- }
- ],
+ "name": "FrameworksCoreTests_context",
"file_patterns": ["(/|^)ContextImpl.java"]
},
{
@@ -177,35 +166,13 @@
]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.app.KeyguardManagerTest"
- }
- ],
+ "name": "FrameworksCoreTests_keyguard_manager",
"file_patterns": [
"(/|^)KeyguardManager.java"
]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.app.PropertyInvalidatedCacheTests"
- }
- ],
+ "name": "FrameworksCoreTests_property_invalidated_cache",
"file_patterns": [
"(/|^)PropertyInvalidatedCache.java"
]
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
new file mode 100644
index 0000000..a01e373
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -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 android.app.appfunctions;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Provides app functions related functionalities.
+ *
+ * <p>App function is a specific piece of functionality that an app offers to the system. These
+ * functionalities can be integrated into various system features.
+ *
+ * @hide
+ */
+@SystemService(Context.APP_FUNCTION_SERVICE)
+public final class AppFunctionManager {
+ private final IAppFunctionManager mService;
+ private final Context mContext;
+
+ /**
+ * TODO(b/357551503): add comments when implement this class
+ *
+ * @hide
+ */
+ public AppFunctionManager(IAppFunctionManager mService, Context context) {
+ this.mService = mService;
+ this.mContext = context;
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
similarity index 72%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to core/java/android/app/appfunctions/IAppFunctionManager.aidl
index 460de8c..018bc75 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
@@ -14,18 +14,11 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package android.app.appfunctions;
/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
-
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
-}
+* Interface between an app and the server implementation service (AppFunctionManagerService).
+* @hide
+*/
+oneway interface IAppFunctionManager {
+}
\ No newline at end of file
diff --git a/core/java/android/app/appfunctions/OWNERS b/core/java/android/app/appfunctions/OWNERS
new file mode 100644
index 0000000..c6827cc
--- /dev/null
+++ b/core/java/android/app/appfunctions/OWNERS
@@ -0,0 +1,6 @@
+avayvod@google.com
+oadesina@google.com
+toki@google.com
+tonymak@google.com
+mingweiliao@google.com
+anothermark@google.com
diff --git a/core/java/android/app/appfunctions/flags/flags.aconfig b/core/java/android/app/appfunctions/flags/flags.aconfig
new file mode 100644
index 0000000..367effc
--- /dev/null
+++ b/core/java/android/app/appfunctions/flags/flags.aconfig
@@ -0,0 +1,11 @@
+package: "android.app.appfunctions.flags"
+container: "system"
+
+flag {
+ name: "enable_app_function_manager"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "machine_learning"
+ description: "This flag the new App Function manager system service."
+ bug: "357551503"
+}
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9aebfc8..9dccc9a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -16,6 +16,7 @@
package android.content;
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import static android.content.flags.Flags.FLAG_ENABLE_BIND_PACKAGE_ISOLATED_PROCESS;
import android.annotation.AttrRes;
@@ -51,6 +52,7 @@
import android.app.IServiceConnection;
import android.app.VrManager;
import android.app.ambientcontext.AmbientContextManager;
+import android.app.appfunctions.AppFunctionManager;
import android.app.people.PeopleManager;
import android.app.time.TimeManager;
import android.companion.virtual.VirtualDeviceManager;
@@ -6310,6 +6312,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve an
+ * {@link AppFunctionManager} for
+ * executing app functions.
+ *
+ * @see #getSystemService(String)
+ */
+ @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+ public static final String APP_FUNCTION_SERVICE = "app_function";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve an
* {@link android.content.integrity.AppIntegrityManager}.
* @hide
*/
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index 41a4288..e353a01 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -22,24 +22,7 @@
"file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.content.ContextTest"
- },
- {
- "include-filter": "android.content.ComponentCallbacksControllerTest"
- },
- {
- "include-filter": "android.content.ContextWrapperTest"
- }
- ],
+ "name": "FrameworksCoreTests_android_content",
"file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java", "(/|^)ComponentCallbacksController.java"]
},
{
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index b0ab11f..1fab3cf 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -171,6 +171,17 @@
"include-filter": "android.content.pm.cts.PackageManagerShellCommandMultiUserTest"
}
]
+ },
+ {
+ "name":"CtsPackageInstallerCUJTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
}
]
}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 59fca3b..3a33ef9 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -161,6 +161,16 @@
}
flag {
+ name: "fix_avatar_picker_not_responding_for_new_user"
+ namespace: "multiuser"
+ description: "Avatar picker is not responding after selecting photo for new user."
+ bug: "358407488"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fix_get_user_property_cache"
namespace: "multiuser"
description: "Cache is not optimised for getUserProperty for values below 0, eg. UserHandler.USER_NULL or UserHandle.USER_ALL"
@@ -288,6 +298,13 @@
}
flag {
+ name: "stop_previous_user_apps"
+ namespace: "multiuser"
+ description: "Stop the previous user apps early in a user switch"
+ bug: "323200731"
+}
+
+flag {
name: "disable_private_space_items_on_home"
namespace: "profile_experiences"
description: "Disables adding items belonging to Private Space on Home Screen manually as well as automatically"
diff --git a/core/java/android/database/OWNERS b/core/java/android/database/OWNERS
index 53f5bb0..50b7015 100644
--- a/core/java/android/database/OWNERS
+++ b/core/java/android/database/OWNERS
@@ -1,6 +1,2 @@
include /SQLITE_OWNERS
-omakoto@google.com
-jsharkey@android.com
-yamasani@google.com
-
diff --git a/core/java/android/database/sqlite/TEST_MAPPING b/core/java/android/database/sqlite/TEST_MAPPING
index 9dcf4e5..659cf6c 100644
--- a/core/java/android/database/sqlite/TEST_MAPPING
+++ b/core/java/android/database/sqlite/TEST_MAPPING
@@ -1,18 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-filter": "android.database.sqlite.SQLiteRawStatementTest"
- }
- ],
+ "name": "FrameworksCoreTests_sqlite",
"file_patterns": [
"(/|^)SQLiteRawStatement.java",
"(/|^)SQLiteDatabase.java",
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 678bd6b..de1cac4 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -415,7 +415,7 @@
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext, sensorId,
+ return new BiometricTestSession(mContext, getSensorProperties(), sensorId,
(context, sensorId1, callback) -> mService
.createTestSession(sensorId1, callback, context.getOpPackageName()));
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 9007b62..b11961c 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -638,17 +638,17 @@
* Set caller's component name for getting logo icon/description. This should only be used
* by ConfirmDeviceCredentialActivity, see b/337082634 for more context.
*
- * @param componentNameForConfirmDeviceCredentialActivity set the component name for
- * ConfirmDeviceCredentialActivity.
+ * @param realCaller set the component name of real caller for
+ * ConfirmDeviceCredentialActivity.
* @return This builder.
* @hide
*/
@NonNull
@RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
- public Builder setComponentNameForConfirmDeviceCredentialActivity(
- ComponentName componentNameForConfirmDeviceCredentialActivity) {
- mPromptInfo.setComponentNameForConfirmDeviceCredentialActivity(
- componentNameForConfirmDeviceCredentialActivity);
+ public Builder setRealCallerForConfirmDeviceCredentialActivity(ComponentName realCaller) {
+ mPromptInfo.setRealCallerForConfirmDeviceCredentialActivity(realCaller);
+ mPromptInfo.setClassNameIfItIsConfirmDeviceCredentialActivity(
+ mContext.getClass().getName());
return this;
}
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
index 027d101..8bd3528 100644
--- a/core/java/android/hardware/biometrics/BiometricTestSession.java
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -27,12 +27,15 @@
import android.util.ArraySet;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
* {@link android.hardware.fingerprint.FingerprintManager}.
+ *
* @hide
*/
@TestApi
@@ -48,21 +51,29 @@
@NonNull ITestSessionCallback callback) throws RemoteException;
}
- private final Context mContext;
private final int mSensorId;
- private final ITestSession mTestSession;
+ private final List<ITestSession> mTestSessionsForAllSensors = new ArrayList<>();
+ private ITestSession mTestSession;
// Keep track of users that were tested, which need to be cleaned up when finishing.
- @NonNull private final ArraySet<Integer> mTestedUsers;
+ @NonNull
+ private final ArraySet<Integer> mTestedUsers;
// Track the users currently cleaning up, and provide a latch that gets notified when all
// users have finished cleaning up. This is an imperfect system, as there can technically be
// multiple cleanups per user. Theoretically we should track the cleanup's BaseClientMonitor's
// unique ID, but it's complicated to plumb it through. This should be fine for now.
- @Nullable private CountDownLatch mCloseLatch;
- @NonNull private final ArraySet<Integer> mUsersCleaningUp;
+ @Nullable
+ private CountDownLatch mCloseLatch;
+ @NonNull
+ private final ArraySet<Integer> mUsersCleaningUp;
- private final ITestSessionCallback mCallback = new ITestSessionCallback.Stub() {
+ private class TestSessionCallbackIml extends ITestSessionCallback.Stub {
+ private final int mSensorId;
+ private TestSessionCallbackIml(int sensorId) {
+ mSensorId = sensorId;
+ }
+
@Override
public void onCleanupStarted(int userId) {
Log.d(getTag(), "onCleanupStarted, sensor: " + mSensorId + ", userId: " + userId);
@@ -76,19 +87,30 @@
mUsersCleaningUp.remove(userId);
if (mUsersCleaningUp.isEmpty() && mCloseLatch != null) {
+ Log.d(getTag(), "counting down");
mCloseLatch.countDown();
}
}
- };
+ }
/**
* @hide
*/
- public BiometricTestSession(@NonNull Context context, int sensorId,
- @NonNull TestSessionProvider testSessionProvider) throws RemoteException {
- mContext = context;
+ public BiometricTestSession(@NonNull Context context, List<SensorProperties> sensors,
+ int sensorId, @NonNull TestSessionProvider testSessionProvider) throws RemoteException {
mSensorId = sensorId;
- mTestSession = testSessionProvider.createTestSession(context, sensorId, mCallback);
+ // When any of the sensors should create the test session, all the other sensors should
+ // set test hal enabled too.
+ for (SensorProperties sensor : sensors) {
+ final int id = sensor.getSensorId();
+ final ITestSession session = testSessionProvider.createTestSession(context, id,
+ new TestSessionCallbackIml(id));
+ mTestSessionsForAllSensors.add(session);
+ if (id == sensorId) {
+ mTestSession = session;
+ }
+ }
+
mTestedUsers = new ArraySet<>();
mUsersCleaningUp = new ArraySet<>();
setTestHalEnabled(true);
@@ -107,8 +129,11 @@
@RequiresPermission(TEST_BIOMETRIC)
private void setTestHalEnabled(boolean enabled) {
try {
- Log.w(getTag(), "setTestHalEnabled, sensor: " + mSensorId + " enabled: " + enabled);
- mTestSession.setTestHalEnabled(enabled);
+ for (ITestSession session : mTestSessionsForAllSensors) {
+ Log.w(getTag(), "setTestHalEnabled, sensor: " + session.getSensorId() + " enabled: "
+ + enabled);
+ session.setTestHalEnabled(enabled);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -175,10 +200,12 @@
/**
* Simulates an acquired message from the HAL.
*
- * @param userId User that this command applies to.
+ * @param userId User that this command applies to.
* @param acquireInfo See
- * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationAcquired(int)} and
- * {@link FingerprintManager.AuthenticationCallback#onAuthenticationAcquired(int)}
+ * {@link
+ * BiometricPrompt.AuthenticationCallback#onAuthenticationAcquired(int)} and
+ * {@link
+ * FingerprintManager.AuthenticationCallback#onAuthenticationAcquired(int)}
*/
@RequiresPermission(TEST_BIOMETRIC)
public void notifyAcquired(int userId, int acquireInfo) {
@@ -192,10 +219,12 @@
/**
* Simulates an error message from the HAL.
*
- * @param userId User that this command applies to.
+ * @param userId User that this command applies to.
* @param errorCode See
- * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int, CharSequence)} and
- * {@link FingerprintManager.AuthenticationCallback#onAuthenticationError(int, CharSequence)}
+ * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)} and
+ * {@link FingerprintManager.AuthenticationCallback#onAuthenticationError(int,
+ * CharSequence)}
*/
@RequiresPermission(TEST_BIOMETRIC)
public void notifyError(int userId, int errorCode) {
@@ -220,8 +249,20 @@
Log.w(getTag(), "Cleanup already in progress for user: " + userId);
}
- mUsersCleaningUp.add(userId);
- mTestSession.cleanupInternalState(userId);
+ for (ITestSession session : mTestSessionsForAllSensors) {
+ mUsersCleaningUp.add(userId);
+ Log.d(getTag(), "cleanupInternalState for sensor: " + session.getSensorId());
+ mCloseLatch = new CountDownLatch(1);
+ session.cleanupInternalState(userId);
+
+ try {
+ Log.d(getTag(), "Awaiting latch...");
+ mCloseLatch.await(3, TimeUnit.SECONDS);
+ Log.d(getTag(), "Finished awaiting");
+ } catch (InterruptedException e) {
+ Log.e(getTag(), "Latch interrupted", e);
+ }
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -234,18 +275,9 @@
// Cleanup can be performed using the test HAL, since it always responds to enumerate with
// zero enrollments.
if (!mTestedUsers.isEmpty()) {
- mCloseLatch = new CountDownLatch(1);
for (int user : mTestedUsers) {
cleanupInternalState(user);
}
-
- try {
- Log.d(getTag(), "Awaiting latch...");
- mCloseLatch.await(3, TimeUnit.SECONDS);
- Log.d(getTag(), "Finished awaiting");
- } catch (InterruptedException e) {
- Log.e(getTag(), "Latch interrupted", e);
- }
}
if (!mUsersCleaningUp.isEmpty()) {
diff --git a/core/java/android/hardware/biometrics/ITestSession.aidl b/core/java/android/hardware/biometrics/ITestSession.aidl
index df9f504..bd99606 100644
--- a/core/java/android/hardware/biometrics/ITestSession.aidl
+++ b/core/java/android/hardware/biometrics/ITestSession.aidl
@@ -59,4 +59,8 @@
// HAL is disabled (e.g. to clean up after a test).
@EnforcePermission("TEST_BIOMETRIC")
void cleanupInternalState(int userId);
+
+ // Get the sensor id of the current test session.
+ @EnforcePermission("TEST_BIOMETRIC")
+ int getSensorId();
}
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 901f6b7..df5d864 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -57,7 +57,8 @@
private boolean mIsForLegacyFingerprintManager = false;
private boolean mShowEmergencyCallButton = false;
private boolean mUseParentProfileForDeviceCredential = false;
- private ComponentName mComponentNameForConfirmDeviceCredentialActivity = null;
+ private ComponentName mRealCallerForConfirmDeviceCredentialActivity = null;
+ private String mClassNameIfItIsConfirmDeviceCredentialActivity = null;
public PromptInfo() {
@@ -89,8 +90,9 @@
mIsForLegacyFingerprintManager = in.readBoolean();
mShowEmergencyCallButton = in.readBoolean();
mUseParentProfileForDeviceCredential = in.readBoolean();
- mComponentNameForConfirmDeviceCredentialActivity = in.readParcelable(
+ mRealCallerForConfirmDeviceCredentialActivity = in.readParcelable(
ComponentName.class.getClassLoader(), ComponentName.class);
+ mClassNameIfItIsConfirmDeviceCredentialActivity = in.readString();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -136,7 +138,8 @@
dest.writeBoolean(mIsForLegacyFingerprintManager);
dest.writeBoolean(mShowEmergencyCallButton);
dest.writeBoolean(mUseParentProfileForDeviceCredential);
- dest.writeParcelable(mComponentNameForConfirmDeviceCredentialActivity, 0);
+ dest.writeParcelable(mRealCallerForConfirmDeviceCredentialActivity, 0);
+ dest.writeString(mClassNameIfItIsConfirmDeviceCredentialActivity);
}
// LINT.IfChange
@@ -155,7 +158,7 @@
return true;
} else if (mShowEmergencyCallButton) {
return true;
- } else if (mComponentNameForConfirmDeviceCredentialActivity != null) {
+ } else if (mRealCallerForConfirmDeviceCredentialActivity != null) {
return true;
}
return false;
@@ -321,10 +324,8 @@
mShowEmergencyCallButton = showEmergencyCallButton;
}
- public void setComponentNameForConfirmDeviceCredentialActivity(
- ComponentName componentNameForConfirmDeviceCredentialActivity) {
- mComponentNameForConfirmDeviceCredentialActivity =
- componentNameForConfirmDeviceCredentialActivity;
+ public void setRealCallerForConfirmDeviceCredentialActivity(ComponentName realCaller) {
+ mRealCallerForConfirmDeviceCredentialActivity = realCaller;
}
public void setUseParentProfileForDeviceCredential(
@@ -332,6 +333,14 @@
mUseParentProfileForDeviceCredential = useParentProfileForDeviceCredential;
}
+ /**
+ * Set the class name of ConfirmDeviceCredentialActivity.
+ */
+ void setClassNameIfItIsConfirmDeviceCredentialActivity(String className) {
+ mClassNameIfItIsConfirmDeviceCredentialActivity = className;
+ }
+
+
// Getters
/**
@@ -455,8 +464,15 @@
return mShowEmergencyCallButton;
}
- public ComponentName getComponentNameForConfirmDeviceCredentialActivity() {
- return mComponentNameForConfirmDeviceCredentialActivity;
+ public ComponentName getRealCallerForConfirmDeviceCredentialActivity() {
+ return mRealCallerForConfirmDeviceCredentialActivity;
}
+ /**
+ * Get the class name of ConfirmDeviceCredentialActivity. Returns null if the direct caller is
+ * not ConfirmDeviceCredentialActivity.
+ */
+ public String getClassNameIfItIsConfirmDeviceCredentialActivity() {
+ return mClassNameIfItIsConfirmDeviceCredentialActivity;
+ }
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 0c55ed5..9bd4860 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,8 +17,6 @@
package android.hardware.camera2.params;
-import static com.android.internal.util.Preconditions.*;
-
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -32,8 +30,6 @@
import android.hardware.camera2.CameraDevice.CameraDeviceSetup;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.params.InputConfiguration;
-import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.media.ImageReader;
import android.os.Parcel;
@@ -46,6 +42,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -95,8 +92,8 @@
public @interface SessionMode {};
// Camera capture session related parameters.
- private List<OutputConfiguration> mOutputConfigurations;
- private CameraCaptureSession.StateCallback mStateCallback;
+ private final @NonNull List<OutputConfiguration> mOutputConfigurations;
+ private CameraCaptureSession.StateCallback mStateCallback = null;
private int mSessionType;
private Executor mExecutor = null;
private InputConfiguration mInputConfig = null;
@@ -268,7 +265,8 @@
*/
@Override
public int hashCode() {
- return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(), mInputConfig.hashCode(),
+ return HashCodeHelpers.hashCode(mOutputConfigurations.hashCode(),
+ Objects.hashCode(mInputConfig),
mSessionType);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 903e916..7f1cac0 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -172,7 +172,7 @@
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext, sensorId,
+ return new BiometricTestSession(mContext, getSensorProperties(), sensorId,
(context, sensorId1, callback) -> mService
.createTestSession(sensorId1, callback, context.getOpPackageName()));
} catch (RemoteException e) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index de39847..ff737a4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -58,8 +58,6 @@
import static android.view.inputmethod.Flags.ctrlShiftShortcut;
import static android.view.inputmethod.Flags.predictiveBackIme;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
@@ -500,36 +498,53 @@
public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
/**
- * Enum flag to be used for {@link #setBackDisposition(int)}.
+ * The disposition mode that indicates the expected affordance for the back button.
*
* @hide
*/
- @Retention(SOURCE)
- @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
- BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
- prefix = "BACK_DISPOSITION_")
+ @IntDef(prefix = { "BACK_DISPOSITION_" }, value = {
+ BACK_DISPOSITION_DEFAULT,
+ BACK_DISPOSITION_WILL_NOT_DISMISS,
+ BACK_DISPOSITION_WILL_DISMISS,
+ BACK_DISPOSITION_ADJUST_NOTHING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
public @interface BackDispositionMode {}
/**
+ * The IME is active, and ready to accept touch/key events. It may or may not be visible.
+ *
* @hide
- * The IME is active. It may or may not be visible.
*/
- public static final int IME_ACTIVE = 0x1;
+ public static final int IME_ACTIVE = 1 << 0;
/**
- * @hide
* The IME is perceptibly visible to the user.
+ *
+ * @hide
*/
- public static final int IME_VISIBLE = 0x2;
+ public static final int IME_VISIBLE = 1 << 1;
/**
- * @hide
* The IME is visible, but not yet perceptible to the user (e.g. fading in)
* by {@link android.view.WindowInsetsController}.
*
* @see InputMethodManager#reportPerceptible
+ * @hide
*/
- public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x4;
+ public static final int IME_VISIBLE_IMPERCEPTIBLE = 1 << 2;
+
+ /**
+ * The IME window visibility state.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "IME_" }, value = {
+ IME_ACTIVE,
+ IME_VISIBLE,
+ IME_VISIBLE_IMPERCEPTIBLE,
+ })
+ public @interface ImeWindowVisibility {}
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
@@ -1342,7 +1357,8 @@
mImeSurfaceRemoverRunnable = null;
}
- private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
+ private void setImeWindowStatus(@ImeWindowVisibility int visibilityFlags,
+ @BackDispositionMode int backDisposition) {
mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
}
@@ -3301,7 +3317,7 @@
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
- setImeWindowStatus(0, mBackDisposition);
+ setImeWindowStatus(0 /* visibilityFlags */, mBackDisposition);
if (android.view.inputmethod.Flags.refactorInsetsController()) {
// The ImeInsetsSourceProvider need the statsToken when dispatching the control. We
// send the token here, so that another request in the provider can be cancelled.
@@ -4476,6 +4492,7 @@
};
}
+ @ImeWindowVisibility
private int mapToImeWindowStatus() {
return IME_ACTIVE
| (isInputViewShown() ? IME_VISIBLE : 0);
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 2fde5e7..449a52f 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -73,11 +73,7 @@
"PowerComponents\\.java",
"[^/]*BatteryConsumer[^/]*\\.java"
],
- "name": "FrameworksCoreTests",
- "options": [
- { "include-filter": "com.android.internal.os.BatteryStatsTests" },
- { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
- ]
+ "name": "FrameworksCoreTests_battery_stats"
},
{
"file_patterns": [
@@ -132,12 +128,7 @@
},
{
"file_patterns": ["Environment[^/]*\\.java"],
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.os.EnvironmentTest"
- }
- ]
+ "name": "FrameworksCoreTests_environment"
}
],
"postsubmit": [
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index edb3a64..4a37e0a 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -520,8 +520,20 @@
* @param counterValue The counter value.
*/
public static void setCounter(@NonNull String counterName, long counterValue) {
- if (isTagEnabled(TRACE_TAG_APP)) {
- nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
+ setCounter(TRACE_TAG_APP, counterName, counterValue);
+ }
+
+ /**
+ * Writes trace message to indicate the value of a given counter under a given trace tag.
+ *
+ * @param traceTag The trace tag.
+ * @param counterName The counter name to appear in the trace.
+ * @param counterValue The counter value.
+ * @hide
+ */
+ public static void setCounter(long traceTag, @NonNull String counterName, long counterValue) {
+ if (isTagEnabled(traceTag)) {
+ nativeTraceCounter(traceTag, counterName, counterValue);
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 06c516a..28f2c25 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4824,6 +4824,7 @@
* <p>Note that this does not alter the user's pre-existing user restrictions.
*
* @param userId the id of the user to become admin
+ * @throws SecurityException if changing ADMIN status of the user is not allowed
* @hide
*/
@RequiresPermission(allOf = {
@@ -4844,6 +4845,7 @@
* <p>Note that this does not alter the user's pre-existing user restrictions.
*
* @param userId the id of the user to revoke admin rights from
+ * @throws SecurityException if changing ADMIN status of the user is not allowed
* @hide
*/
@RequiresPermission(allOf = {
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index a26c6f4..a95ce79 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -104,7 +104,7 @@
public static void serialize(@NonNull VibrationEffect effect, @NonNull Writer writer,
@Flags int flags) throws IOException {
// Serialize effect first to fail early.
- XmlSerializedVibration<VibrationEffect> serializedVibration =
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration =
toSerializedVibration(effect, flags);
TypedXmlSerializer xmlSerializer = Xml.newFastSerializer();
xmlSerializer.setFeature(XML_FEATURE_INDENT_OUTPUT, (flags & FLAG_PRETTY_PRINT) != 0);
@@ -114,9 +114,9 @@
xmlSerializer.endDocument();
}
- private static XmlSerializedVibration<VibrationEffect> toSerializedVibration(
+ private static XmlSerializedVibration<? extends VibrationEffect> toSerializedVibration(
VibrationEffect effect, @Flags int flags) throws SerializationFailedException {
- XmlSerializedVibration<VibrationEffect> serializedVibration;
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration;
int serializerFlags = 0;
if ((flags & FLAG_ALLOW_HIDDEN_APIS) != 0) {
serializerFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java
index 3a254c1..bdda42a 100644
--- a/core/java/android/security/net/config/SystemCertificateSource.java
+++ b/core/java/android/security/net/config/SystemCertificateSource.java
@@ -19,6 +19,8 @@
import android.os.Environment;
import android.os.UserHandle;
+import com.android.internal.util.ArrayUtils;
+
import java.io.File;
/**
@@ -45,7 +47,7 @@
}
File updatable_dir = new File("/apex/com.android.conscrypt/cacerts");
if (updatable_dir.exists()
- && !(updatable_dir.list().length == 0)) {
+ && !(ArrayUtils.isEmpty(updatable_dir.list()))) {
return updatable_dir;
}
return new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts");
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 06e53ac..133b3d1 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -267,10 +267,10 @@
private boolean mDozing;
private boolean mWindowless;
private boolean mPreviewMode;
- private volatile int mDozeScreenState = Display.STATE_UNKNOWN;
- private volatile @Display.StateReason int mDozeScreenStateReason = Display.STATE_REASON_UNKNOWN;
- private volatile int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
- private volatile float mDozeScreenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ private int mDozeScreenState = Display.STATE_UNKNOWN;
+ private @Display.StateReason int mDozeScreenStateReason = Display.STATE_REASON_UNKNOWN;
+ private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private float mDozeScreenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private boolean mDebug = false;
@@ -913,13 +913,15 @@
*/
@UnsupportedAppUsage
public void startDozing() {
- if (mCanDoze && !mDozing) {
- mDozing = true;
- updateDoze();
+ synchronized (this) {
+ if (mCanDoze && !mDozing) {
+ mDozing = true;
+ updateDoze();
+ }
}
}
- private void updateDoze() {
+ private synchronized void updateDoze() {
if (mDreamToken == null) {
Slog.w(mTag, "Updating doze without a dream token.");
return;
@@ -927,6 +929,9 @@
if (mDozing) {
try {
+ Slog.v(mTag, "UpdateDoze mDozeScreenState=" + mDozeScreenState
+ + " mDozeScreenBrightness=" + mDozeScreenBrightness
+ + " mDozeScreenBrightnessFloat=" + mDozeScreenBrightnessFloat);
if (startAndStopDozingInBackground()) {
mDreamManager.startDozingOneway(
mDreamToken, mDozeScreenState, mDozeScreenStateReason,
@@ -1048,10 +1053,12 @@
*/
@UnsupportedAppUsage
public void setDozeScreenState(int state, @Display.StateReason int reason) {
- if (mDozeScreenState != state) {
- mDozeScreenState = state;
- mDozeScreenStateReason = reason;
- updateDoze();
+ synchronized (this) {
+ if (mDozeScreenState != state) {
+ mDozeScreenState = state;
+ mDozeScreenStateReason = reason;
+ updateDoze();
+ }
}
}
@@ -1103,9 +1110,11 @@
if (brightness != PowerManager.BRIGHTNESS_DEFAULT) {
brightness = clampAbsoluteBrightness(brightness);
}
- if (mDozeScreenBrightness != brightness) {
- mDozeScreenBrightness = brightness;
- updateDoze();
+ synchronized (this) {
+ if (mDozeScreenBrightness != brightness) {
+ mDozeScreenBrightness = brightness;
+ updateDoze();
+ }
}
}
@@ -1141,9 +1150,12 @@
if (!Float.isNaN(brightness)) {
brightness = clampAbsoluteBrightnessFloat(brightness);
}
- if (!BrightnessSynchronizer.floatEquals(mDozeScreenBrightnessFloat, brightness)) {
- mDozeScreenBrightnessFloat = brightness;
- updateDoze();
+
+ synchronized (this) {
+ if (!BrightnessSynchronizer.floatEquals(mDozeScreenBrightnessFloat, brightness)) {
+ mDozeScreenBrightnessFloat = brightness;
+ updateDoze();
+ }
}
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 224379b..fc6c2e8 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -25,7 +25,6 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
@@ -76,8 +75,9 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -1074,7 +1074,7 @@
rt.manualRule.type = AutomaticZenRule.TYPE_OTHER;
rt.manualRule.condition = new Condition(
rt.manualRule.conditionId != null ? rt.manualRule.conditionId
- : Uri.EMPTY, "", STATE_TRUE);
+ : Uri.EMPTY, "", Condition.STATE_TRUE);
}
}
return rt;
@@ -2540,10 +2540,34 @@
}
public static class ZenRule implements Parcelable {
+
+ /** No manual override. Rule owner can decide its state. */
+ public static final int OVERRIDE_NONE = 0;
+ /**
+ * User has manually activated a mode. This will temporarily overrule the rule owner's
+ * decision to deactivate it (see {@link #reconsiderConditionOverride}).
+ */
+ public static final int OVERRIDE_ACTIVATE = 1;
+ /**
+ * User has manually deactivated an active mode, or setting ZEN_MODE_OFF (for the few apps
+ * still allowed to do that) snoozed the mode. This will temporarily overrule the rule
+ * owner's decision to activate it (see {@link #reconsiderConditionOverride}).
+ */
+ public static final int OVERRIDE_DEACTIVATE = 2;
+
+ @IntDef(prefix = { "OVERRIDE" }, value = {
+ OVERRIDE_NONE,
+ OVERRIDE_ACTIVATE,
+ OVERRIDE_DEACTIVATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConditionOverride {}
+
@UnsupportedAppUsage
public boolean enabled;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean snoozing; // user manually disabled this instance
+ @Deprecated
+ public boolean snoozing; // user manually disabled this instance. Obsolete with MODES_UI
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String name; // required for automatic
@UnsupportedAppUsage
@@ -2579,6 +2603,15 @@
// ZenPolicy, so we store them here, only for the manual rule.
@FlaggedApi(Flags.FLAG_MODES_UI)
int legacySuppressedEffects;
+ /**
+ * Signals a user's action to (temporarily or permanently) activate or deactivate this
+ * rule, overruling the condition set by the owner. This value is not stored to disk, as
+ * it shouldn't survive reboots or be involved in B&R. It might be reset by certain
+ * owner-provided state transitions as well.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_UI)
+ @ConditionOverride
+ int conditionOverride = OVERRIDE_NONE;
public ZenRule() { }
@@ -2620,6 +2653,7 @@
if (Flags.modesUi()) {
disabledOrigin = source.readInt();
legacySuppressedEffects = source.readInt();
+ conditionOverride = source.readInt();
}
}
}
@@ -2698,6 +2732,7 @@
if (Flags.modesUi()) {
dest.writeInt(disabledOrigin);
dest.writeInt(legacySuppressedEffects);
+ dest.writeInt(conditionOverride);
}
}
}
@@ -2708,9 +2743,16 @@
.append("id=").append(id)
.append(",state=").append(condition == null ? "STATE_FALSE"
: Condition.stateToString(condition.state))
- .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
- .append(",snoozing=").append(snoozing)
- .append(",name=").append(name)
+ .append(",enabled=").append(String.valueOf(enabled).toUpperCase());
+
+ if (Flags.modesUi()) {
+ sb.append(",conditionOverride=")
+ .append(conditionOverrideToString(conditionOverride));
+ } else {
+ sb.append(",snoozing=").append(snoozing);
+ }
+
+ sb.append(",name=").append(name)
.append(",zenMode=").append(Global.zenModeToString(zenMode))
.append(",conditionId=").append(conditionId)
.append(",pkg=").append(pkg)
@@ -2753,6 +2795,15 @@
return sb.append(']').toString();
}
+ private static String conditionOverrideToString(@ConditionOverride int value) {
+ return switch(value) {
+ case OVERRIDE_ACTIVATE -> "OVERRIDE_ACTIVATE";
+ case OVERRIDE_DEACTIVATE -> "OVERRIDE_DEACTIVATE";
+ case OVERRIDE_NONE -> "OVERRIDE_NONE";
+ default -> "UNKNOWN";
+ };
+ }
+
/** @hide */
// TODO: add configuration activity
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -2763,7 +2814,11 @@
proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
proto.write(ZenRuleProto.ENABLED, enabled);
proto.write(ZenRuleProto.ENABLER, enabler);
- proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
+ if (Flags.modesApi() && Flags.modesUi()) {
+ proto.write(ZenRuleProto.IS_SNOOZING, conditionOverride == OVERRIDE_DEACTIVATE);
+ } else {
+ proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
+ }
proto.write(ZenRuleProto.ZEN_MODE, zenMode);
if (conditionId != null) {
proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
@@ -2816,7 +2871,8 @@
if (Flags.modesUi()) {
finalEquals = finalEquals
&& other.disabledOrigin == disabledOrigin
- && other.legacySuppressedEffects == legacySuppressedEffects;
+ && other.legacySuppressedEffects == legacySuppressedEffects
+ && other.conditionOverride == conditionOverride;
}
}
@@ -2832,7 +2888,8 @@
zenDeviceEffects, modified, allowManualInvocation, iconResName,
triggerDescription, type, userModifiedFields,
zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
- deletionInstant, disabledOrigin, legacySuppressedEffects);
+ deletionInstant, disabledOrigin, legacySuppressedEffects,
+ conditionOverride);
} else {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
component, configurationActivity, pkg, id, enabler, zenPolicy,
@@ -2858,8 +2915,74 @@
}
}
+ // TODO: b/333527800 - Rename to isActive()
public boolean isAutomaticActive() {
- return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (!enabled || getPkg() == null) {
+ return false;
+ } else if (conditionOverride == OVERRIDE_ACTIVATE) {
+ return true;
+ } else if (conditionOverride == OVERRIDE_DEACTIVATE) {
+ return false;
+ } else {
+ return isTrueOrUnknown();
+ }
+ } else {
+ return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
+ }
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ @ConditionOverride
+ public int getConditionOverride() {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ return conditionOverride;
+ } else {
+ return snoozing ? OVERRIDE_DEACTIVATE : OVERRIDE_NONE;
+ }
+ }
+
+ public void setConditionOverride(@ConditionOverride int value) {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ conditionOverride = value;
+ } else {
+ if (value == OVERRIDE_ACTIVATE) {
+ Slog.wtf(TAG, "Shouldn't set OVERRIDE_ACTIVATE if MODES_UI is off");
+ } else if (value == OVERRIDE_DEACTIVATE) {
+ snoozing = true;
+ } else if (value == OVERRIDE_NONE) {
+ snoozing = false;
+ }
+ }
+ }
+
+ public void resetConditionOverride() {
+ setConditionOverride(OVERRIDE_NONE);
+ }
+
+ /**
+ * Possibly remove the override, depending on the rule owner's intended state.
+ *
+ * <p>This allows rule owners to "take over" manually-provided state with their smartness,
+ * but only once both agree.
+ *
+ * <p>For example, a manually activated rule wins over rule owner's opinion that it should
+ * be off, until the owner says it should be on, at which point it will turn off (without
+ * manual intervention) when the rule owner says it should be off. And symmetrically for
+ * manual deactivation (which used to be called "snoozing").
+ */
+ public void reconsiderConditionOverride() {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (conditionOverride == OVERRIDE_ACTIVATE && isTrueOrUnknown()) {
+ resetConditionOverride();
+ } else if (conditionOverride == OVERRIDE_DEACTIVATE && !isTrueOrUnknown()) {
+ resetConditionOverride();
+ }
+ } else {
+ if (snoozing && !isTrueOrUnknown()) {
+ snoozing = false;
+ }
+ }
}
public String getPkg() {
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index a37e227..05c2a9c 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -454,6 +454,8 @@
*/
public static class RuleDiff extends BaseDiff {
public static final String FIELD_ENABLED = "enabled";
+ public static final String FIELD_CONDITION_OVERRIDE = "conditionOverride";
+ @Deprecated
public static final String FIELD_SNOOZING = "snoozing";
public static final String FIELD_NAME = "name";
public static final String FIELD_ZEN_MODE = "zenMode";
@@ -507,8 +509,15 @@
if (from.enabled != to.enabled) {
addField(FIELD_ENABLED, new FieldDiff<>(from.enabled, to.enabled));
}
- if (from.snoozing != to.snoozing) {
- addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (from.conditionOverride != to.conditionOverride) {
+ addField(FIELD_CONDITION_OVERRIDE,
+ new FieldDiff<>(from.conditionOverride, to.conditionOverride));
+ }
+ } else {
+ if (from.snoozing != to.snoozing) {
+ addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
+ }
}
if (!Objects.equals(from.name, to.name)) {
addField(FIELD_NAME, new FieldDiff<>(from.name, to.name));
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 4d176f2..bb3f6c9 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -277,13 +277,3 @@
purpose: PURPOSE_BUGFIX
}
}
-
-flag {
- name: "typeface_cache_for_var_settings"
- namespace: "text"
- description: "Cache Typeface instance for font variation settings."
- bug: "355462362"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
\ No newline at end of file
diff --git a/core/java/android/util/TEST_MAPPING b/core/java/android/util/TEST_MAPPING
index c681f86..64b2e6e 100644
--- a/core/java/android/util/TEST_MAPPING
+++ b/core/java/android/util/TEST_MAPPING
@@ -1,27 +1,11 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.util.CharsetUtilsTest"
- },
- {
- "include-filter": "com.android.internal.util.FastDataTest"
- }
- ],
+ "name": "FrameworksCoreTests_util_data_charset",
"file_patterns": ["CharsetUtils|FastData"]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.util.XmlTest"
- },
- {
- "include-filter": "android.util.BinaryXmlTest"
- }
- ],
+ "name": "FrameworksCoreTests_xml",
"file_patterns": ["Xml"]
}
],
diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING
index 7668eec..3ae470a 100644
--- a/core/java/android/util/apk/TEST_MAPPING
+++ b/core/java/android/util/apk/TEST_MAPPING
@@ -1,12 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.util.apk.SourceStampVerifierTest"
- }
- ]
+ "name": "FrameworksCoreTests_util_apk"
}
],
"presubmit-large": [
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a7641c0..9e4b27d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3574,7 +3574,7 @@
checkPreconditions(sc);
if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
- "reparent", this, sc,
+ "setColor", this, sc,
"r=" + color[0] + " g=" + color[1] + " b=" + color[2]);
}
nativeSetColor(mNativeObject, sc.mNativeObject, color);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index dbd65de..3088fd3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16444,7 +16444,11 @@
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED) {
try {
- Trace.traceBegin(TRACE_TAG_VIEW, "View.onTouchListener#onTouch");
+ if (Trace.isTagEnabled(TRACE_TAG_VIEW)) {
+ Trace.traceBegin(TRACE_TAG_VIEW,
+ "View.onTouchListener#onTouch - " + getClass().getSimpleName()
+ + ", eventId - " + event.getId());
+ }
handled = li.mOnTouchListener.onTouch(this, event);
} finally {
Trace.traceEnd(TRACE_TAG_VIEW);
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 09306c7..288be9c 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -28,6 +28,7 @@
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.TypedValue;
+import android.view.WindowInsets;
import dalvik.system.CloseGuard;
@@ -881,12 +882,13 @@
}
/**
- * @return if a window animation has outsets applied to it.
+ * @return the edges to which outsets should be applied if run as a windoow animation.
*
* @hide
*/
- public boolean hasExtension() {
- return false;
+ @WindowInsets.Side.InsetsSide
+ public int getExtensionEdges() {
+ return 0x0;
}
/**
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 5aaa994..bbdc9d0 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -21,6 +21,7 @@
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
+import android.view.WindowInsets;
import java.util.ArrayList;
import java.util.List;
@@ -540,12 +541,12 @@
/** @hide */
@Override
- public boolean hasExtension() {
+ @WindowInsets.Side.InsetsSide
+ public int getExtensionEdges() {
+ int edge = 0x0;
for (Animation animation : mAnimations) {
- if (animation.hasExtension()) {
- return true;
- }
+ edge |= animation.getExtensionEdges();
}
- return false;
+ return edge;
}
}
diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java
index 210eb8a..1aeee07 100644
--- a/core/java/android/view/animation/ExtendAnimation.java
+++ b/core/java/android/view/animation/ExtendAnimation.java
@@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.graphics.Insets;
import android.util.AttributeSet;
+import android.view.WindowInsets;
/**
* An animation that controls the outset of an object.
@@ -151,9 +152,12 @@
/** @hide */
@Override
- public boolean hasExtension() {
- return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0
- || mFromInsets.bottom < 0;
+ @WindowInsets.Side.InsetsSide
+ public int getExtensionEdges() {
+ return (mFromInsets.left < 0 || mToInsets.left < 0 ? WindowInsets.Side.LEFT : 0)
+ | (mFromInsets.right < 0 || mToInsets.right < 0 ? WindowInsets.Side.RIGHT : 0)
+ | (mFromInsets.top < 0 || mToInsets.top < 0 ? WindowInsets.Side.TOP : 0)
+ | (mFromInsets.bottom < 0 || mToInsets.bottom < 0 ? WindowInsets.Side.BOTTOM : 0);
}
@Override
diff --git a/core/java/android/view/textclassifier/TEST_MAPPING b/core/java/android/view/textclassifier/TEST_MAPPING
index 2f9e737..050c651 100644
--- a/core/java/android/view/textclassifier/TEST_MAPPING
+++ b/core/java/android/view/textclassifier/TEST_MAPPING
@@ -1,15 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.textclassifier"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "FrameworksCoreTests_textclassifier"
},
{
"name": "CtsTextClassifierTestCases",
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index ac57c00..58b5757 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -16,6 +16,7 @@
package android.window;
+import android.os.Bundle;
import android.os.IBinder;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
@@ -24,14 +25,21 @@
/** @hide */
interface ITaskFragmentOrganizerController {
-
/**
* Registers a TaskFragmentOrganizer to manage TaskFragments. Registering a system
* organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer will have additional
* system capabilities.
+ *
+ * @param organizer The TaskFragmentOrganizer to register
+ * @param isSystemOrganizer If it is a system organizer
+ * @param outSavedState Returning the saved state (if any) that previously saved. This is
+ * useful when retrieve the state from the same TaskFragmentOrganizer
+ * that was killed by the system (e.g. to reclaim memory). Note that
+ * the save state is dropped and unable to retrieve once the system
+ * restarts or the organizer is unregistered.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true)")
- void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer);
+ void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer, out Bundle outSavedState);
/**
* Unregisters a previously registered TaskFragmentOrganizer.
@@ -39,6 +47,12 @@
void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
/**
+ * Saves the state in the system, where the state can be restored if the process of
+ * the TaskFragmentOrganizer is restarted.
+ */
+ void setSavedState(in ITaskFragmentOrganizer organizer, in Bundle savedState);
+
+ /**
* Notifies the server that the organizer has finished handling the given transaction. The
* server should apply the given {@link WindowContainerTransaction} for the necessary changes.
*/
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 8e429cb..027d323 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -165,17 +165,12 @@
*/
@CallSuper
public void registerOrganizer() {
- // TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed.
- try {
- getController().registerOrganizer(mInterface, false /* isSystemOrganizer */);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ registerOrganizer(false /* isSystemOrganizer */, null /* outSavedState */);
}
/**
* Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
- *
+ * <p>
* Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
* will have additional system capabilities, including: (1) it will receive SurfaceControl for
* the organized TaskFragment, and (2) it needs to update the
@@ -187,8 +182,31 @@
@RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
@FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
public void registerOrganizer(boolean isSystemOrganizer) {
+ registerOrganizer(isSystemOrganizer, null /* outSavedState */);
+ }
+
+ /**
+ * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
+ * <p>
+ * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
+ * will have additional system capabilities, including: (1) it will receive SurfaceControl for
+ * the organized TaskFragment, and (2) it needs to update the
+ * {@link android.view.SurfaceControl} following the window change accordingly.
+ *
+ * @param isSystemOrganizer If it is a system organizer
+ * @param outSavedState Returning the saved state (if any) that previously saved. This is
+ * useful when retrieve the state from the same TaskFragmentOrganizer
+ * that was killed by the system (e.g. to reclaim memory). Note that
+ * the save state is dropped and unable to retrieve once the system
+ * restarts or the organizer is unregistered.
+ * @hide
+ */
+ @CallSuper
+ @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
+ public void registerOrganizer(boolean isSystemOrganizer, @Nullable Bundle outSavedState) {
try {
- getController().registerOrganizer(mInterface, isSystemOrganizer);
+ getController().registerOrganizer(mInterface, isSystemOrganizer,
+ outSavedState != null ? outSavedState : new Bundle());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -207,6 +225,30 @@
}
/**
+ * Saves the state in the system, where the state can be restored if the process of
+ * the TaskFragmentOrganizer is restarted.
+ *
+ * @hide
+ *
+ * @param state the state to save.
+ */
+ public void setSavedState(@NonNull Bundle state) {
+ if (!Flags.aeBackStackRestore()) {
+ return;
+ }
+
+ if (state.getSize() > 200000) {
+ throw new IllegalArgumentException("Saved state too large, " + state.getSize());
+ }
+
+ try {
+ getController().setSavedState(mInterface, state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notifies the server that the organizer has finished handling the given transaction. The
* server should apply the given {@link WindowContainerTransaction} for the necessary changes.
*
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 7bbc3db..b6c0d7c 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -306,7 +306,7 @@
}
private boolean callOnKeyPreIme() {
- if (mViewRoot != null && !isOnBackInvokedCallbackEnabled(mViewRoot.mContext)) {
+ if (mViewRoot != null && !isOnBackInvokedCallbackEnabled()) {
return mViewRoot.injectBackKeyEvents(/*preImeOnly*/ true);
} else {
return false;
@@ -505,7 +505,7 @@
if (callback instanceof ImeBackAnimationController
|| callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) {
// call onKeyPreIme API if the current callback is an IME callback and the app has
- // not set enableOnBackInvokedCallback="false"
+ // not set enableOnBackInvokedCallback="true"
try {
boolean consumed = mOnKeyPreIme.getAsBoolean();
if (consumed) {
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index a6ae948..adbc598 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -61,16 +61,6 @@
flag {
namespace: "windowing_sdk"
- name: "fix_pip_restore_to_overlay"
- description: "Restore exit-pip activity back to ActivityEmbedding overlay"
- bug: "297887697"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "windowing_sdk"
name: "activity_embedding_animation_customization_flag"
description: "Whether the animation customization feature for AE is enabled"
bug: "293658614"
@@ -128,3 +118,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "ae_back_stack_restore"
+ description: "Allow the ActivityEmbedding back stack to be restored after process restarted"
+ bug: "289875940"
+ is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/content/om/TEST_MAPPING b/core/java/com/android/internal/content/om/TEST_MAPPING
index ab3abb1..c27c325 100644
--- a/core/java/com/android/internal/content/om/TEST_MAPPING
+++ b/core/java/com/android/internal/content/om/TEST_MAPPING
@@ -1,12 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "com.android.internal.content."
- }
- ]
+ "name": "FrameworksCoreTests_internal_content"
},
{
"name": "SelfTargetingOverlayDeviceTests"
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
index f72a5ca..f210741 100644
--- a/core/java/com/android/internal/graphics/ColorUtils.java
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -21,7 +21,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.graphics.Color;
-
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.internal.graphics.cam.Cam;
/**
@@ -29,6 +29,7 @@
*
* A set of color-related utility methods, building upon those available in {@code Color}.
*/
+@RavenwoodKeepWholeClass
public final class ColorUtils {
private static final double XYZ_WHITE_REFERENCE_X = 95.047;
@@ -696,4 +697,4 @@
double calculateContrast(int foreground, int background, int alpha);
}
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/graphics/cam/Cam.java b/core/java/com/android/internal/graphics/cam/Cam.java
index 1df85c3..49fa37b 100644
--- a/core/java/com/android/internal/graphics/cam/Cam.java
+++ b/core/java/com/android/internal/graphics/cam/Cam.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.internal.graphics.ColorUtils;
@@ -25,6 +26,7 @@
* A color appearance model, based on CAM16, extended to use L* as the lightness dimension, and
* coupled to a gamut mapping algorithm. Creates a color system, enables a digital design system.
*/
+@RavenwoodKeepWholeClass
public class Cam {
// The maximum difference between the requested L* and the L* returned.
private static final float DL_MAX = 0.2f;
diff --git a/core/java/com/android/internal/graphics/cam/CamUtils.java b/core/java/com/android/internal/graphics/cam/CamUtils.java
index f541729..76fabc6 100644
--- a/core/java/com/android/internal/graphics/cam/CamUtils.java
+++ b/core/java/com/android/internal/graphics/cam/CamUtils.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.graphics.Color;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.internal.graphics.ColorUtils;
@@ -45,6 +46,7 @@
* consistent, and reasonably good. It worked." - Fairchild, Color Models and Systems: Handbook of
* Color Psychology, 2015
*/
+@RavenwoodKeepWholeClass
public final class CamUtils {
private CamUtils() {
}
diff --git a/core/java/com/android/internal/graphics/cam/Frame.java b/core/java/com/android/internal/graphics/cam/Frame.java
index 0ac7cbc..c419fab 100644
--- a/core/java/com/android/internal/graphics/cam/Frame.java
+++ b/core/java/com/android/internal/graphics/cam/Frame.java
@@ -17,6 +17,7 @@
package com.android.internal.graphics.cam;
import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.util.MathUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -33,6 +34,7 @@
* number of calculations during the color => CAM conversion process that depend only on the viewing
* conditions. Caching those calculations in a Frame instance saves a significant amount of time.
*/
+@RavenwoodKeepWholeClass
public final class Frame {
// Standard viewing conditions assumed in RGB specification - Stokes, Anderson, Chandrasekar,
// Motta - A Standard Default Color Space for the Internet: sRGB, 1996.
diff --git a/core/java/com/android/internal/graphics/cam/HctSolver.java b/core/java/com/android/internal/graphics/cam/HctSolver.java
index d7a8691..6e558e7 100644
--- a/core/java/com/android/internal/graphics/cam/HctSolver.java
+++ b/core/java/com/android/internal/graphics/cam/HctSolver.java
@@ -16,6 +16,8 @@
package com.android.internal.graphics.cam;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+
/**
* An efficient algorithm for determining the closest sRGB color to a set of HCT coordinates,
* based on geometrical insights for finding intersections in linear RGB, CAM16, and L*a*b*.
@@ -24,6 +26,7 @@
* Copied from //java/com/google/ux/material/libmonet/hct on May 22 2022.
* ColorUtils/MathUtils functions that were required were added to CamUtils.
*/
+@RavenwoodKeepWholeClass
public class HctSolver {
private HctSolver() {}
diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING
index c09181f..e4550c0 100644
--- a/core/java/com/android/internal/infra/TEST_MAPPING
+++ b/core/java/com/android/internal/infra/TEST_MAPPING
@@ -20,12 +20,7 @@
]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "com.android.internal.infra."
- }
- ]
+ "name": "FrameworksCoreTests_internal_infra"
}
]
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 921363c..b009c99 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -20,6 +20,8 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
@@ -106,13 +108,10 @@
*
* @param vis visibility flags
* @param backDisposition disposition flags
- * @see android.inputmethodservice.InputMethodService#IME_ACTIVE
- * @see android.inputmethodservice.InputMethodService#IME_VISIBLE
- * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT
- * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING
*/
@AnyThread
- public void setImeWindowStatusAsync(int vis, int backDisposition) {
+ public void setImeWindowStatusAsync(@ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 69d1cb3..7bfb800 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -210,8 +210,16 @@
*/
public static final int CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE = 116;
+ /**
+ * Track interaction of exiting desktop mode on closing the last window.
+ *
+ * <p>Tracking starts when the last window is closed and finishes when the animation to exit
+ * desktop mode ends.
+ */
+ public static final int CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE = 117;
+
// When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
- @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE;
+ @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE;
/** @hide */
@IntDef({
@@ -319,7 +327,8 @@
CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_OPEN,
CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_CLOSE,
CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH,
- CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+ CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE,
+ CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
@@ -438,6 +447,7 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_CLOSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_KEYBOARD_QUICK_SWITCH_CLOSE;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE;
}
private Cuj() {
@@ -666,6 +676,8 @@
return "LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH";
case CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE:
return "DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE";
+ case CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE:
+ return "DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/jank/TEST_MAPPING b/core/java/com/android/internal/jank/TEST_MAPPING
index 4e00ff1..e7f3dc3 100644
--- a/core/java/com/android/internal/jank/TEST_MAPPING
+++ b/core/java/com/android/internal/jank/TEST_MAPPING
@@ -1,18 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "com.android.internal.jank"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ],
+ "name": "FrameworksCoreTests_internal_jank",
"file_patterns": [
"core/java/com/android/internal/jank/.*",
"core/tests/coretests/src/com/android/internal/jank/.*"
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index ae43acf..8346d71 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -6,11 +6,7 @@
"Kernel[^/]*\\.java",
"[^/]*Power[^/]*\\.java"
],
- "name": "FrameworksCoreTests",
- "options": [
- { "include-filter": "com.android.internal.os.BatteryStatsTests" },
- { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
- ]
+ "name": "FrameworksCoreTests_battery_stats"
},
{
"file_patterns": [
@@ -24,11 +20,7 @@
"file_patterns": [
"BinderDeathDispatcher\\.java"
],
- "name": "FrameworksCoreTests",
- "options": [
- { "include-filter": "com.android.internal.os.BinderDeathDispatcherTest" },
- { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
- ]
+ "name": "FrameworksCoreTests_internal_os_binder"
},
{
"file_patterns": [
@@ -50,25 +42,7 @@
"name": "PowerStatsTests"
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "com.android.internal.os.KernelCpuUidFreqTimeReaderTest"
- },
- {
- "include-filter": "com.android.internal.os.KernelCpuUidActiveTimeReaderTest"
- },
- {
- "include-filter": "com.android.internal.os.KernelCpuUidClusterTimeReaderTest"
- },
- {
- "include-filter": "com.android.internal.os.KernelSingleUidTimeReaderTest"
- },
- {
- "include-filter": "com.android.internal.os.KernelCpuUidBpfMapReaderTest"
- }
-
- ],
+ "name": "FrameworksCoreTests_internal_os_kernel",
"file_patterns": [
"KernelCpuUidTimeReader\\.java",
"KernelCpuUidBpfMapReader\\.java",
diff --git a/core/java/com/android/internal/power/TEST_MAPPING b/core/java/com/android/internal/power/TEST_MAPPING
index 1946f5c..3f184b2 100644
--- a/core/java/com/android/internal/power/TEST_MAPPING
+++ b/core/java/com/android/internal/power/TEST_MAPPING
@@ -1,11 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- { "include-filter": "com.android.internal.os.BatteryStatsTests" },
- { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
- ]
+ "name": "FrameworksCoreTests_battery_stats"
},
{
"name": "PowerStatsTests"
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index e0c90d8..cb20ceb 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -33,6 +33,7 @@
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.INTERNED_DATA;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_MESSAGE;
@@ -449,6 +450,8 @@
case (int) GROUP_ID:
os.write(GROUP_ID, pis.readInt(GROUP_ID));
break;
+ case (int) LOCATION:
+ os.write(LOCATION, pis.readInt(LOCATION));
default:
throw new RuntimeException(
"Unexpected field id " + pis.getFieldNumber());
diff --git a/core/java/com/android/internal/security/TEST_MAPPING b/core/java/com/android/internal/security/TEST_MAPPING
index 0af3b03..5bd9d2e 100644
--- a/core/java/com/android/internal/security/TEST_MAPPING
+++ b/core/java/com/android/internal/security/TEST_MAPPING
@@ -1,15 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "com.android.internal.security."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- }
- ]
+ "name": "FrameworksCoreTests_internal_security"
},
{
"name": "UpdatableSystemFontTest",
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 7240aff..3adc6b2 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -16,6 +16,8 @@
package com.android.internal.statusbar;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -30,7 +32,9 @@
public final int mDisabledFlags1; // switch[0]
public final int mAppearance; // switch[1]
public final AppearanceRegion[] mAppearanceRegions; // switch[2]
+ @ImeWindowVisibility
public final int mImeWindowVis; // switch[3]
+ @BackDispositionMode
public final int mImeBackDisposition; // switch[4]
public final boolean mShowImeSwitcher; // switch[5]
public final int mDisabledFlags2; // switch[6]
@@ -42,10 +46,11 @@
public final LetterboxDetails[] mLetterboxDetails;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
- int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
- int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2,
- boolean navbarColorManagedByIme, int behavior, int requestedVisibleTypes,
- String packageName, int transientBarTypes, LetterboxDetails[] letterboxDetails) {
+ int appearance, AppearanceRegion[] appearanceRegions,
+ @ImeWindowVisibility int imeWindowVis, @BackDispositionMode int imeBackDisposition,
+ boolean showImeSwitcher, int disabledFlags2, boolean navbarColorManagedByIme,
+ int behavior, int requestedVisibleTypes, String packageName, int transientBarTypes,
+ LetterboxDetails[] letterboxDetails) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mAppearance = appearance;
diff --git a/core/java/com/android/internal/util/TEST_MAPPING b/core/java/com/android/internal/util/TEST_MAPPING
index 00a8118..a0221f3b 100644
--- a/core/java/com/android/internal/util/TEST_MAPPING
+++ b/core/java/com/android/internal/util/TEST_MAPPING
@@ -5,30 +5,11 @@
"file_patterns": ["ScreenshotHelper"]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.util.XmlTest"
- },
- {
- "include-filter": "android.util.BinaryXmlTest"
- }
- ],
+ "name": "FrameworksCoreTests_xml",
"file_patterns": ["Xml"]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "com.android.internal.util.LatencyTrackerTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ],
+ "name": "FrameworksCoreTests_internal_util_latency_tracker",
"file_patterns": ["LatencyTracker.java"]
}
]
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java b/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
index 15ecedd..cd7dcfd 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java
@@ -29,7 +29,7 @@
import android.util.IntArray;
import android.util.LongArray;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedVibrationEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedComposedEffect.java
similarity index 86%
rename from core/java/com/android/internal/vibrator/persistence/SerializedVibrationEffect.java
rename to core/java/com/android/internal/vibrator/persistence/SerializedComposedEffect.java
index 23df304..6c562c9 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedVibrationEffect.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedComposedEffect.java
@@ -29,24 +29,24 @@
import java.util.Arrays;
/**
- * Serialized representation of a {@link VibrationEffect}.
+ * Serialized representation of a {@link VibrationEffect.Composed}.
*
* <p>The vibration is represented by a list of serialized segments that can be added to a
* {@link VibrationEffect.Composition} during the {@link #deserialize()} procedure.
*
* @hide
*/
-final class SerializedVibrationEffect implements XmlSerializedVibration<VibrationEffect> {
+final class SerializedComposedEffect implements XmlSerializedVibration<VibrationEffect.Composed> {
@NonNull
private final SerializedSegment[] mSegments;
- SerializedVibrationEffect(@NonNull SerializedSegment segment) {
+ SerializedComposedEffect(@NonNull SerializedSegment segment) {
requireNonNull(segment);
mSegments = new SerializedSegment[]{ segment };
}
- SerializedVibrationEffect(@NonNull SerializedSegment[] segments) {
+ SerializedComposedEffect(@NonNull SerializedSegment[] segments) {
requireNonNull(segments);
checkArgument(segments.length > 0, "Unsupported empty vibration");
mSegments = segments;
@@ -54,12 +54,12 @@
@NonNull
@Override
- public VibrationEffect deserialize() {
+ public VibrationEffect.Composed deserialize() {
VibrationEffect.Composition composition = VibrationEffect.startComposition();
for (SerializedSegment segment : mSegments) {
segment.deserializeIntoComposition(composition);
}
- return composition.compose();
+ return (VibrationEffect.Composed) composition.compose();
}
@Override
@@ -79,7 +79,7 @@
@Override
public String toString() {
- return "SerializedVibrationEffect{"
+ return "SerializedComposedEffect{"
+ "segments=" + Arrays.toString(mSegments)
+ '}';
}
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java b/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java
index db5c7ff..862f7cb 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java
@@ -27,7 +27,7 @@
import android.os.VibrationEffect;
import android.os.vibrator.PrimitiveSegment;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java
index 8924311..a6f48a4 100644
--- a/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedPredefinedEffect.java
@@ -25,7 +25,7 @@
import android.os.VibrationEffect;
import android.os.vibrator.PrebakedSegment;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.internal.vibrator.persistence.XmlConstants.PredefinedEffectName;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedVendorEffect.java b/core/java/com/android/internal/vibrator/persistence/SerializedVendorEffect.java
new file mode 100644
index 0000000..aa1b0a23
--- /dev/null
+++ b/core/java/com/android/internal/vibrator/persistence/SerializedVendorEffect.java
@@ -0,0 +1,127 @@
+/*
+ * 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.vibrator.persistence;
+
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VENDOR_EFFECT;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.os.PersistableBundle;
+import android.os.VibrationEffect;
+import android.text.TextUtils;
+import android.util.Base64;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Serialized representation of a {@link VibrationEffect.VendorEffect}.
+ *
+ * <p>The vibration is represented by an opaque {@link PersistableBundle} that can be used by
+ * {@link VibrationEffect#createVendorEffect(PersistableBundle)} during the {@link #deserialize()}
+ * procedure.
+ *
+ * @hide
+ */
+final class SerializedVendorEffect implements XmlSerializedVibration<VibrationEffect.VendorEffect> {
+
+ @NonNull
+ private final PersistableBundle mVendorData;
+
+ SerializedVendorEffect(@NonNull PersistableBundle vendorData) {
+ requireNonNull(vendorData);
+ mVendorData = vendorData;
+ }
+
+ @SuppressLint("MissingPermission")
+ @NonNull
+ @Override
+ public VibrationEffect.VendorEffect deserialize() {
+ return (VibrationEffect.VendorEffect) VibrationEffect.createVendorEffect(mVendorData);
+ }
+
+ @Override
+ public void write(@NonNull TypedXmlSerializer serializer)
+ throws IOException {
+ serializer.startTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VIBRATION_EFFECT);
+ writeContent(serializer);
+ serializer.endTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VIBRATION_EFFECT);
+ }
+
+ @Override
+ public void writeContent(@NonNull TypedXmlSerializer serializer) throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ mVendorData.writeToStream(outputStream);
+
+ serializer.startTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VENDOR_EFFECT);
+ serializer.text(Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP));
+ serializer.endTag(XmlConstants.NAMESPACE, XmlConstants.TAG_VENDOR_EFFECT);
+ }
+
+ @Override
+ public String toString() {
+ return "SerializedVendorEffect{"
+ + "vendorData=" + mVendorData
+ + '}';
+ }
+
+ /** Parser implementation for {@link SerializedVendorEffect}. */
+ static final class Parser {
+
+ @NonNull
+ static SerializedVendorEffect parseNext(@NonNull TypedXmlPullParser parser,
+ @XmlConstants.Flags int flags) throws XmlParserException, IOException {
+ XmlValidator.checkStartTag(parser, TAG_VENDOR_EFFECT);
+ XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+
+ PersistableBundle vendorData;
+ XmlReader.readNextText(parser, TAG_VENDOR_EFFECT);
+
+ try {
+ String text = parser.getText().trim();
+ XmlValidator.checkParserCondition(!text.isEmpty(),
+ "Expected tag %s to have base64 representation of vendor data, got empty",
+ TAG_VENDOR_EFFECT);
+
+ vendorData = PersistableBundle.readFromStream(
+ new ByteArrayInputStream(Base64.decode(text, Base64.DEFAULT)));
+ XmlValidator.checkParserCondition(!vendorData.isEmpty(),
+ "Expected tag %s to have non-empty vendor data, got empty bundle",
+ TAG_VENDOR_EFFECT);
+ } catch (IllegalArgumentException | NullPointerException e) {
+ throw new XmlParserException(
+ TextUtils.formatSimple(
+ "Expected base64 representation of vendor data in tag %s, got %s",
+ TAG_VENDOR_EFFECT, parser.getText()),
+ e);
+ } catch (IOException e) {
+ throw new XmlParserException("Error reading vendor data from decoded bytes", e);
+ }
+
+ // Consume tag
+ XmlReader.readEndTag(parser);
+
+ return new SerializedVendorEffect(vendorData);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
index 2b8b61d..a9fbcaf 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlParser.java
@@ -18,13 +18,15 @@
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREDEFINED_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITIVE_EFFECT;
+import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VENDOR_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_VIBRATION_EFFECT;
import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_EFFECT;
import android.annotation.NonNull;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.modules.utils.TypedXmlPullParser;
import java.io.IOException;
@@ -80,6 +82,16 @@
* }
* </pre>
*
+ * * Vendor vibration effects
+ *
+ * <pre>
+ * {@code
+ * <vibration-effect>
+ * <vendor-effect>base64-representation-of-persistable-bundle</vendor-effect>
+ * </vibration-effect>
+ * }
+ * </pre>
+ *
* @hide
*/
public class VibrationEffectXmlParser {
@@ -87,11 +99,9 @@
/**
* Parses the current XML tag with all nested tags into a single {@link XmlSerializedVibration}
* wrapping a {@link VibrationEffect}.
- *
- * @see XmlParser#parseTag(TypedXmlPullParser)
*/
@NonNull
- public static XmlSerializedVibration<VibrationEffect> parseTag(
+ public static XmlSerializedVibration<? extends VibrationEffect> parseTag(
@NonNull TypedXmlPullParser parser, @XmlConstants.Flags int flags)
throws XmlParserException, IOException {
XmlValidator.checkStartTag(parser, TAG_VIBRATION_EFFECT);
@@ -107,8 +117,9 @@
* <p>This can be reused for reading a vibration from an XML root tag or from within a combined
* vibration, but it should always be called from places that validates the top level tag.
*/
- static SerializedVibrationEffect parseVibrationContent(TypedXmlPullParser parser,
- @XmlConstants.Flags int flags) throws XmlParserException, IOException {
+ private static XmlSerializedVibration<? extends VibrationEffect> parseVibrationContent(
+ TypedXmlPullParser parser, @XmlConstants.Flags int flags)
+ throws XmlParserException, IOException {
String vibrationTagName = parser.getName();
int vibrationTagDepth = parser.getDepth();
@@ -116,11 +127,16 @@
XmlReader.readNextTagWithin(parser, vibrationTagDepth),
"Unsupported empty vibration tag");
- SerializedVibrationEffect serializedVibration;
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration;
switch (parser.getName()) {
+ case TAG_VENDOR_EFFECT:
+ if (Flags.vendorVibrationEffects()) {
+ serializedVibration = SerializedVendorEffect.Parser.parseNext(parser, flags);
+ break;
+ } // else fall through
case TAG_PREDEFINED_EFFECT:
- serializedVibration = new SerializedVibrationEffect(
+ serializedVibration = new SerializedComposedEffect(
SerializedPredefinedEffect.Parser.parseNext(parser, flags));
break;
case TAG_PRIMITIVE_EFFECT:
@@ -128,11 +144,11 @@
do { // First primitive tag already open
primitives.add(SerializedCompositionPrimitive.Parser.parseNext(parser));
} while (XmlReader.readNextTagWithin(parser, vibrationTagDepth));
- serializedVibration = new SerializedVibrationEffect(
+ serializedVibration = new SerializedComposedEffect(
primitives.toArray(new SerializedSegment[primitives.size()]));
break;
case TAG_WAVEFORM_EFFECT:
- serializedVibration = new SerializedVibrationEffect(
+ serializedVibration = new SerializedComposedEffect(
SerializedAmplitudeStepWaveform.Parser.parseNext(parser));
break;
default:
diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
index f561c14..d74a23d 100644
--- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
+++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java
@@ -17,13 +17,15 @@
package com.android.internal.vibrator.persistence;
import android.annotation.NonNull;
+import android.os.PersistableBundle;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
-import com.android.internal.vibrator.persistence.SerializedVibrationEffect.SerializedSegment;
+import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment;
import com.android.internal.vibrator.persistence.XmlConstants.PredefinedEffectName;
import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName;
@@ -41,6 +43,7 @@
* <li>{@link VibrationEffect#createWaveform(long[], int[], int)}
* <li>A composition created exclusively via
* {@link VibrationEffect.Composition#addPrimitive(int, float, int)}
+ * <li>{@link VibrationEffect#createVendorEffect(PersistableBundle)}
* </ul>
*
* @hide
@@ -49,13 +52,16 @@
/**
* Creates a serialized representation of the input {@code vibration}.
- *
- * @see XmlSerializer#serialize
*/
@NonNull
- public static XmlSerializedVibration<VibrationEffect> serialize(
+ public static XmlSerializedVibration<? extends VibrationEffect> serialize(
@NonNull VibrationEffect vibration, @XmlConstants.Flags int flags)
throws XmlSerializerException {
+ if (Flags.vendorVibrationEffects()
+ && (vibration instanceof VibrationEffect.VendorEffect vendorEffect)) {
+ return serializeVendorEffect(vendorEffect);
+ }
+
XmlValidator.checkSerializerCondition(vibration instanceof VibrationEffect.Composed,
"Unsupported VibrationEffect type %s", vibration);
@@ -73,7 +79,7 @@
return serializeWaveformEffect(composed);
}
- private static SerializedVibrationEffect serializePredefinedEffect(
+ private static SerializedComposedEffect serializePredefinedEffect(
VibrationEffect.Composed effect, @XmlConstants.Flags int flags)
throws XmlSerializerException {
List<VibrationEffectSegment> segments = effect.getSegments();
@@ -81,10 +87,15 @@
"Unsupported repeating predefined effect %s", effect);
XmlValidator.checkSerializerCondition(segments.size() == 1,
"Unsupported multiple segments in predefined effect %s", effect);
- return new SerializedVibrationEffect(serializePrebakedSegment(segments.get(0), flags));
+ return new SerializedComposedEffect(serializePrebakedSegment(segments.get(0), flags));
}
- private static SerializedVibrationEffect serializePrimitiveEffect(
+ private static SerializedVendorEffect serializeVendorEffect(
+ VibrationEffect.VendorEffect effect) {
+ return new SerializedVendorEffect(effect.getVendorData());
+ }
+
+ private static SerializedComposedEffect serializePrimitiveEffect(
VibrationEffect.Composed effect) throws XmlSerializerException {
List<VibrationEffectSegment> segments = effect.getSegments();
XmlValidator.checkSerializerCondition(effect.getRepeatIndex() == -1,
@@ -95,10 +106,10 @@
primitives[i] = serializePrimitiveSegment(segments.get(i));
}
- return new SerializedVibrationEffect(primitives);
+ return new SerializedComposedEffect(primitives);
}
- private static SerializedVibrationEffect serializeWaveformEffect(
+ private static SerializedComposedEffect serializeWaveformEffect(
VibrationEffect.Composed effect) throws XmlSerializerException {
SerializedAmplitudeStepWaveform.Builder serializedWaveformBuilder =
new SerializedAmplitudeStepWaveform.Builder();
@@ -120,7 +131,7 @@
segment.getDuration(), toAmplitudeInt(segment.getAmplitude()));
}
- return new SerializedVibrationEffect(serializedWaveformBuilder.build());
+ return new SerializedComposedEffect(serializedWaveformBuilder.build());
}
private static SerializedPredefinedEffect serializePrebakedSegment(
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
index 8b92153..2a55d99 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java
@@ -40,6 +40,7 @@
public static final String TAG_PREDEFINED_EFFECT = "predefined-effect";
public static final String TAG_PRIMITIVE_EFFECT = "primitive-effect";
+ public static final String TAG_VENDOR_EFFECT = "vendor-effect";
public static final String TAG_WAVEFORM_EFFECT = "waveform-effect";
public static final String TAG_WAVEFORM_ENTRY = "waveform-entry";
public static final String TAG_REPEATING = "repeating";
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlParser.java b/core/java/com/android/internal/vibrator/persistence/XmlParser.java
deleted file mode 100644
index 6712f1c..0000000
--- a/core/java/com/android/internal/vibrator/persistence/XmlParser.java
+++ /dev/null
@@ -1,51 +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.internal.vibrator.persistence;
-
-import android.annotation.NonNull;
-
-import com.android.modules.utils.TypedXmlPullParser;
-
-import java.io.IOException;
-
-/**
- * Parse XML tags into valid {@link XmlSerializedVibration} instances.
- *
- * @param <T> The vibration type that will be parsed.
- * @see XmlSerializedVibration
- * @hide
- */
-@FunctionalInterface
-public interface XmlParser<T> {
-
- /**
- * Parses the current XML tag with all nested tags into a single {@link XmlSerializedVibration}.
- *
- * <p>This method will consume nested XML tags until it finds the
- * {@link TypedXmlPullParser#END_TAG} for the current tag.
- *
- * <p>The vibration reconstructed by the returned {@link XmlSerializedVibration#deserialize()}
- * is guaranteed to be valid. This method will throw an exception otherwise.
- *
- * @param pullParser The {@link TypedXmlPullParser} with the input XML.
- * @return The parsed vibration wrapped in a {@link XmlSerializedVibration} representation.
- * @throws IOException On any I/O error while reading the input XML
- * @throws XmlParserException If the XML content does not represent a valid vibration.
- */
- XmlSerializedVibration<T> parseTag(@NonNull TypedXmlPullParser pullParser)
- throws XmlParserException, IOException;
-}
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlParserException.java b/core/java/com/android/internal/vibrator/persistence/XmlParserException.java
index 7507864..e2b30e7 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlParserException.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlParserException.java
@@ -23,7 +23,6 @@
/**
* Represents an error while parsing a vibration XML input.
*
- * @see XmlParser
* @hide
*/
public final class XmlParserException extends Exception {
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlReader.java b/core/java/com/android/internal/vibrator/persistence/XmlReader.java
index a5ace84..0ac6fef 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlReader.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlReader.java
@@ -130,6 +130,25 @@
}
/**
+ * Read the next element, ignoring comments and ignorable whitespace, and returns only if it's a
+ * {@link XmlPullParser#TEXT}. Any other tag will fail this check.
+ *
+ * <p>The parser will be pointing to the first next element after skipping comments,
+ * instructions and ignorable whitespace.
+ */
+ public static void readNextText(TypedXmlPullParser parser, String tagName)
+ throws XmlParserException, IOException {
+ try {
+ int type = parser.next(); // skips comments, instruction tokens and ignorable whitespace
+ XmlValidator.checkParserCondition(type == XmlPullParser.TEXT,
+ "Unexpected event %s of type %d, expected text event inside tag %s",
+ parser.getName(), type, tagName);
+ } catch (XmlPullParserException e) {
+ throw XmlParserException.createFromPullParserException("text event", e);
+ }
+ }
+
+ /**
* Check parser has a {@link XmlPullParser#END_TAG} as the next tag, with no nested tags.
*
* <p>The parser will be pointing to the end tag after this method.
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java b/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java
index 3233fa2..c20b7d2 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlSerializedVibration.java
@@ -26,8 +26,7 @@
* Serialized representation of a generic vibration.
*
* <p>This can be used to represent a {@link android.os.CombinedVibration} or a
- * {@link android.os.VibrationEffect}. Instances can be created from vibration objects via
- * {@link XmlSerializer}, or from XML content via {@link XmlParser}.
+ * {@link android.os.VibrationEffect}.
*
* <p>The separation of serialization and writing procedures enables configurable rules to define
* which vibrations can be successfully serialized before any data is written to the output stream.
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlSerializer.java b/core/java/com/android/internal/vibrator/persistence/XmlSerializer.java
deleted file mode 100644
index 102e6c1..0000000
--- a/core/java/com/android/internal/vibrator/persistence/XmlSerializer.java
+++ /dev/null
@@ -1,40 +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.internal.vibrator.persistence;
-
-import android.annotation.NonNull;
-
-/**
- * Creates a {@link XmlSerializedVibration} instance representing a vibration.
- *
- * @param <T> The vibration type that will be serialized.
- * @see XmlSerializedVibration
- * @hide
- */
-@FunctionalInterface
-public interface XmlSerializer<T> {
-
- /**
- * Creates a serialized representation of the input {@code vibration}.
- *
- * @param vibration The vibration to be serialized
- * @return The serialized representation of the input vibration
- * @throws XmlSerializerException If the input vibration cannot be serialized
- */
- @NonNull
- XmlSerializedVibration<T> serialize(@NonNull T vibration) throws XmlSerializerException;
-}
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java b/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java
index c57ff5d..2e7ad09 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlSerializerException.java
@@ -19,7 +19,6 @@
/**
* Represents an error while serializing a vibration input.
*
- * @see XmlSerializer
* @hide
*/
public final class XmlSerializerException extends Exception {
diff --git a/core/java/com/android/internal/vibrator/persistence/XmlValidator.java b/core/java/com/android/internal/vibrator/persistence/XmlValidator.java
index 84d4f3f..1b5a356 100644
--- a/core/java/com/android/internal/vibrator/persistence/XmlValidator.java
+++ b/core/java/com/android/internal/vibrator/persistence/XmlValidator.java
@@ -18,7 +18,7 @@
import static java.util.Objects.requireNonNull;
-import android.annotation.NonNull;
+import android.os.VibrationEffect;
import android.text.TextUtils;
import com.android.internal.util.ArrayUtils;
@@ -82,11 +82,11 @@
* Check given {@link XmlSerializedVibration} represents the expected {@code vibration} object
* when it's deserialized.
*/
- @NonNull
- public static <T> void checkSerializedVibration(
- XmlSerializedVibration<T> serializedVibration, T expectedVibration)
+ public static void checkSerializedVibration(
+ XmlSerializedVibration<? extends VibrationEffect> serializedVibration,
+ VibrationEffect expectedVibration)
throws XmlSerializerException {
- T deserializedVibration = requireNonNull(serializedVibration.deserialize());
+ VibrationEffect deserializedVibration = requireNonNull(serializedVibration.deserialize());
checkSerializerCondition(Objects.equals(expectedVibration, deserializedVibration),
"Unexpected serialized vibration %s: found deserialization %s, expected %s",
serializedVibration, deserializedVibration, expectedVibration);
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index b2bc19c..c0fe098 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -51,6 +51,10 @@
# Sensor
per-file android_hardware_SensorManager* = arthuri@google.com, bduddie@google.com, stange@google.com
+# Security
+per-file android_os_SELinux.cpp = file:/core/java/android/security/OWNERS
+per-file android_security_* = file:/core/java/android/security/OWNERS
+
per-file *Zygote* = file:/ZYGOTE_OWNERS
per-file core_jni_helpers.* = file:/ZYGOTE_OWNERS
per-file fd_utils.* = file:/ZYGOTE_OWNERS
@@ -67,7 +71,6 @@
per-file android_os_storage_* = file:/core/java/android/os/storage/OWNERS
per-file android_os_Trace* = file:/TRACE_OWNERS
per-file android_se_* = file:/omapi/java/android/se/OWNERS
-per-file android_security_* = file:/core/java/android/security/OWNERS
per-file android_view_* = file:/core/java/android/view/OWNERS
per-file com_android_internal_net_* = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/core/jni/TEST_MAPPING b/core/jni/TEST_MAPPING
index ea0b01e..fa73a4d 100644
--- a/core/jni/TEST_MAPPING
+++ b/core/jni/TEST_MAPPING
@@ -1,15 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.util.CharsetUtilsTest"
- },
- {
- "include-filter": "com.android.internal.util.FastDataTest"
- }
- ],
+ "name": "FrameworksCoreTests_util_data_charset",
"file_patterns": ["CharsetUtils|FastData"]
},
{
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 12804d4..e7f0560 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -163,7 +163,7 @@
optional bool vibrator_under_external_control = 5;
optional bool low_power_mode = 6;
optional bool vibrate_on = 24;
- optional bool keyboard_vibration_on = 25;
+ reserved 25; // prev keyboard_vibration_on
optional int32 default_vibration_amplitude = 26;
optional int32 alarm_intensity = 18;
optional int32 alarm_default_intensity = 19;
diff --git a/core/res/res/layout/time_picker_text_input_material.xml b/core/res/res/layout/time_picker_text_input_material.xml
index 4988842..86070b1 100644
--- a/core/res/res/layout/time_picker_text_input_material.xml
+++ b/core/res/res/layout/time_picker_text_input_material.xml
@@ -34,19 +34,29 @@
android:layoutDirection="ltr">
<EditText
android:id="@+id/input_hour"
- android:layout_width="50dp"
+ android:layout_width="50sp"
android:layout_height="wrap_content"
+ android:layout_alignEnd="@id/hour_label_holder"
android:inputType="number"
android:textAppearance="@style/TextAppearance.Material.TimePicker.InputField"
android:imeOptions="actionNext"/>
- <TextView
- android:id="@+id/label_hour"
+ <!-- Ensure the label_hour takes up at least 50sp of space -->
+ <FrameLayout
+ android:id="@+id/hour_label_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@id/input_hour"
- android:layout_alignStart="@id/input_hour"
- android:labelFor="@+id/input_hour"
- android:text="@string/time_picker_hour_label"/>
+ android:layout_below="@id/input_hour">
+ <TextView
+ android:id="@+id/label_hour"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:labelFor="@+id/input_hour"
+ android:text="@string/time_picker_hour_label"/>
+ <Space
+ android:layout_width="50sp"
+ android:layout_height="0dp"/>
+ </FrameLayout>
<TextView
android:id="@+id/input_separator"
@@ -58,21 +68,30 @@
<EditText
android:id="@+id/input_minute"
- android:layout_width="50dp"
+ android:layout_width="50sp"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/input_hour"
android:layout_toEndOf="@id/input_separator"
android:inputType="number"
android:textAppearance="@style/TextAppearance.Material.TimePicker.InputField" />
- <TextView
- android:id="@+id/label_minute"
+ <!-- Ensure the label_minute takes up at least 50sp of space -->
+ <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/input_minute"
android:layout_alignStart="@id/input_minute"
- android:labelFor="@+id/input_minute"
- android:text="@string/time_picker_minute_label"/>
-
+ >
+ <TextView
+ android:id="@+id/label_minute"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:labelFor="@+id/input_minute"
+ android:text="@string/time_picker_minute_label"/>
+ <Space
+ android:layout_width="50sp"
+ android:layout_height="0dp"/>
+ </FrameLayout>
<TextView
android:visibility="invisible"
android:id="@+id/label_error"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index df288f9..c0027f4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4982,7 +4982,7 @@
<!-- URI for camera shutter sound -->
<string translatable="false" name="config_cameraShutterSound">/product/media/audio/ui/camera_click.ogg</string>
- <!-- URI for default ringtone sound file to be used for silent ringer vibration -->
+ <!-- @deprecated This configuration is no longer used. -->
<string translatable="false" name="config_defaultRingtoneVibrationSound"></string>
<!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 5793bbe..2bbaf9c 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -249,6 +249,7 @@
],
srcs: [
"src/android/app/ActivityManagerTest.java",
+ "src/android/colormodel/CamTest.java",
"src/android/content/ContextTest.java",
"src/android/content/pm/PackageManagerTest.java",
"src/android/content/pm/UserInfoTest.java",
diff --git a/core/tests/coretests/src/android/colormodel/CamTest.java b/core/tests/coretests/src/android/colormodel/CamTest.java
index 05fc0e0..cf398db 100644
--- a/core/tests/coretests/src/android/colormodel/CamTest.java
+++ b/core/tests/coretests/src/android/colormodel/CamTest.java
@@ -18,9 +18,12 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.LargeTest;
import org.junit.Assert;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -35,6 +38,9 @@
static final int GREEN = 0xff00ff00;
static final int BLUE = 0xff0000ff;
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void camFromIntToInt() {
Cam cam = Cam.fromInt(RED);
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
index bbc2458..fd9fda3a 100644
--- a/core/tests/coretests/src/android/content/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -1,18 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.content.ContentCaptureOptionsTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
+ "name": "FrameworksCoreTests_content_capture_options"
}
]
}
diff --git a/core/tests/coretests/src/android/content/integrity/TEST_MAPPING b/core/tests/coretests/src/android/content/integrity/TEST_MAPPING
index 2920716..d22fe84 100644
--- a/core/tests/coretests/src/android/content/integrity/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/integrity/TEST_MAPPING
@@ -1,12 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.content.integrity."
- }
- ]
+ "name": "FrameworksCoreTests_android_content_integrity"
}
]
}
diff --git a/core/tests/coretests/src/android/content/pm/TEST_MAPPING b/core/tests/coretests/src/android/content/pm/TEST_MAPPING
index 978d80c..9ab438e 100644
--- a/core/tests/coretests/src/android/content/pm/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/pm/TEST_MAPPING
@@ -1,21 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.content.pm."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
+ "name": "FrameworksCoreTests_android_content_pm_PreSubmit"
}
],
"postsubmit": [
diff --git a/core/tests/coretests/src/android/content/res/TEST_MAPPING b/core/tests/coretests/src/android/content/res/TEST_MAPPING
index 4ea6e40..25927de5 100644
--- a/core/tests/coretests/src/android/content/res/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/res/TEST_MAPPING
@@ -1,24 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.content.res."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
+ "name": "FrameworksCoreTests_android_content_res"
}
],
"postsubmit": [
diff --git a/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java b/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
deleted file mode 100644
index 8a54e5b..0000000
--- a/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open 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.graphics;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.test.InstrumentationTestCase;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.text.flags.Flags;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * PaintTest tests {@link Paint}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class PaintFontVariationTest extends InstrumentationTestCase {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
- @Test
- public void testDerivedFromSameTypeface() {
- final Paint p = new Paint();
-
- p.setTypeface(Typeface.SANS_SERIF);
- assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
- Typeface first = p.getTypeface();
-
- p.setTypeface(Typeface.SANS_SERIF);
- assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
- Typeface second = p.getTypeface();
-
- assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
- @Test
- public void testDerivedFromChained() {
- final Paint p = new Paint();
-
- p.setTypeface(Typeface.SANS_SERIF);
- assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
- Typeface first = p.getTypeface();
-
- assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
- Typeface second = p.getTypeface();
-
- assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
- }
-}
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 878ba70..0dec756 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -16,22 +16,13 @@
package android.graphics;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertNotEquals;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.InstrumentationTestCase;
import android.text.TextUtils;
import androidx.test.filters.SmallTest;
-import com.android.text.flags.Flags;
-
-import org.junit.Rule;
-
import java.util.Arrays;
import java.util.HashSet;
@@ -39,9 +30,6 @@
* PaintTest tests {@link Paint}.
*/
public class PaintTest extends InstrumentationTestCase {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
static void assertEquals(String message, float[] expected, float[] actual) {
@@ -415,33 +403,4 @@
assertEquals(6, getClusterCount(p, rtlStr + ltrStr));
assertEquals(9, getClusterCount(p, ltrStr + rtlStr + ltrStr));
}
-
- @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
- public void testDerivedFromSameTypeface() {
- final Paint p = new Paint();
-
- p.setTypeface(Typeface.SANS_SERIF);
- assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
- Typeface first = p.getTypeface();
-
- p.setTypeface(Typeface.SANS_SERIF);
- assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
- Typeface second = p.getTypeface();
-
- assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
- }
-
- @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
- public void testDerivedFromChained() {
- final Paint p = new Paint();
-
- p.setTypeface(Typeface.SANS_SERIF);
- assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
- Typeface first = p.getTypeface();
-
- assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
- Typeface second = p.getTypeface();
-
- assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
- }
}
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
index bec72d9..21f248d 100644
--- a/core/tests/coretests/src/android/service/TEST_MAPPING
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -1,17 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {"include-filter": "android.service.controls"},
- {"include-filter": "android.service.controls.actions"},
- {"include-filter": "android.service.controls.templates"},
- {"include-filter": "android.service.euicc"},
- {"include-filter": "android.service.notification"},
- {"include-filter": "android.service.quicksettings"},
- {"include-filter": "android.service.settings.suggestions"},
- {"exclude-annotation": "org.junit.Ignore"}
- ]
+ "name": "FrameworksCoreTests_android_service"
}
]
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
index f8beac2..c2cf40d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -1,18 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.contentcapture"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
+ "name": "FrameworksCoreTests_android_view_contentcapture"
}
]
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
index 3cd4e17..3ef1ac1 100644
--- a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -1,18 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.view.contentprotection"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
+ "name": "FrameworksCoreTests_android_view_contentprotection"
}
]
}
diff --git a/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
index 9aed8be..4a46244 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
+++ b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
@@ -1,21 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "com.android.internal.content."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
+ "name": "FrameworksCoreTests_com_android_internal_content_Presubmit"
}
]
}
diff --git a/core/tests/resourceflaggingtests/Android.bp b/core/tests/resourceflaggingtests/Android.bp
index dd86094..efb8437 100644
--- a/core/tests/resourceflaggingtests/Android.bp
+++ b/core/tests/resourceflaggingtests/Android.bp
@@ -22,54 +22,6 @@
default_team: "trendy_team_android_resources",
}
-genrule {
- name: "resource-flagging-test-app-resources-compile",
- tools: ["aapt2"],
- srcs: [
- "flagged_resources_res/values/bools.xml",
- ],
- out: ["values_bools.arsc.flat"],
- cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
- "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
-}
-
-genrule {
- name: "resource-flagging-test-app-resources-compile2",
- tools: ["aapt2"],
- srcs: [
- "flagged_resources_res/values/bools2.xml",
- ],
- out: ["values_bools2.arsc.flat"],
- cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
- "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
-}
-
-genrule {
- name: "resource-flagging-test-app-apk",
- tools: ["aapt2"],
- // The first input file in the list must be the manifest
- srcs: [
- "TestAppAndroidManifest.xml",
- ":resource-flagging-test-app-resources-compile",
- ":resource-flagging-test-app-resources-compile2",
- ],
- out: ["resapp.apk"],
- cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
-}
-
-java_genrule {
- name: "resource-flagging-apk-as-resource",
- srcs: [
- ":resource-flagging-test-app-apk",
- ],
- out: ["apks_as_resources.res.zip"],
- tools: ["soong_zip"],
-
- cmd: "mkdir -p $(genDir)/res/raw && " +
- "cp $(in) $(genDir)/res/raw/$$(basename $(in)) && " +
- "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
-}
-
android_test {
name: "ResourceFlaggingTests",
srcs: [
@@ -82,6 +34,6 @@
"testng",
"compatibility-device-util-axt",
],
- resource_zips: [":resource-flagging-apk-as-resource"],
+ resource_zips: [":resource-flagging-test-app-apk-as-resource"],
test_suites: ["device-tests"],
}
diff --git a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
index ad8542e..c1e3578 100644
--- a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
+++ b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
@@ -69,11 +69,23 @@
}
private boolean getBoolean(String name) {
- int resId = mResources.getIdentifier(name, "bool", "com.android.intenal.flaggedresources");
+ int resId = mResources.getIdentifier(
+ name,
+ "bool",
+ "com.android.intenal.flaggedresources");
assertThat(resId).isNotEqualTo(0);
return mResources.getBoolean(resId);
}
+ private String getString(String name) {
+ int resId = mResources.getIdentifier(
+ name,
+ "string",
+ "com.android.intenal.flaggedresources");
+ assertThat(resId).isNotEqualTo(0);
+ return mResources.getString(resId);
+ }
+
private String extractApkAndGetPath(int id) throws Exception {
final Resources resources = mContext.getResources();
try (InputStream is = resources.openRawResource(id)) {
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 bf9a820..1cc38de 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -24,22 +24,32 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
+import android.os.PersistableBundle;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Xml;
import com.android.modules.utils.TypedXmlPullParser;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.xmlpull.v1.XmlPullParser;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
+import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -53,6 +63,9 @@
@RunWith(JUnit4.class)
public class VibrationEffectXmlSerializationTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void isSupportedMimeType_onlySupportsVibrationXmlMimeType() {
// Single MIME type supported
@@ -422,6 +435,97 @@
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void testVendorEffect_featureFlagEnabled_allSucceed() throws Exception {
+ PersistableBundle vendorData = new PersistableBundle();
+ vendorData.putInt("id", 1);
+ vendorData.putDouble("scale", 0.5);
+ vendorData.putBoolean("loop", false);
+ vendorData.putLongArray("amplitudes", new long[] { 0, 255, 128 });
+ vendorData.putString("label", "vibration");
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ vendorData.writeToStream(outputStream);
+ String vendorDataStr = Base64.getEncoder().encodeToString(outputStream.toByteArray());
+
+ VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+ String xml = "<vibration-effect><vendor-effect> " // test trailing whitespace is ignored
+ + vendorDataStr
+ + " \n </vendor-effect></vibration-effect>";
+
+ assertPublicApisParserSucceeds(xml, effect);
+ assertPublicApisSerializerSucceeds(effect, vendorDataStr);
+ assertPublicApisRoundTrip(effect);
+
+ assertHiddenApisParserSucceeds(xml, effect);
+ assertHiddenApisSerializerSucceeds(effect, vendorDataStr);
+ assertHiddenApisRoundTrip(effect);
+
+ // Check PersistableBundle from round-trip
+ PersistableBundle parsedVendorData =
+ ((VibrationEffect.VendorEffect) parseVibrationEffect(serialize(effect),
+ /* flags= */ 0)).getVendorData();
+ assertThat(parsedVendorData.size()).isEqualTo(vendorData.size());
+ assertThat(parsedVendorData.getInt("id")).isEqualTo(1);
+ assertThat(parsedVendorData.getDouble("scale")).isEqualTo(0.5);
+ assertThat(parsedVendorData.getBoolean("loop")).isFalse();
+ assertArrayEquals(parsedVendorData.getLongArray("amplitudes"), new long[] { 0, 255, 128 });
+ assertThat(parsedVendorData.getString("label")).isEqualTo("vibration");
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void testInvalidVendorEffect_featureFlagEnabled_allFail() throws IOException {
+ String emptyTag = "<vibration-effect><vendor-effect/></vibration-effect>";
+ assertPublicApisParserFails(emptyTag);
+ assertHiddenApisParserFails(emptyTag);
+
+ String emptyStringTag =
+ "<vibration-effect><vendor-effect> \n </vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(emptyStringTag);
+ assertHiddenApisParserFails(emptyStringTag);
+
+ String invalidString =
+ "<vibration-effect><vendor-effect>invalid</vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(invalidString);
+ assertHiddenApisParserFails(invalidString);
+
+ String validBase64String =
+ "<vibration-effect><vendor-effect>c29tZXNh</vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(validBase64String);
+ assertHiddenApisParserFails(validBase64String);
+
+ PersistableBundle emptyData = new PersistableBundle();
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ emptyData.writeToStream(outputStream);
+ String emptyBundleString = "<vibration-effect><vendor-effect>"
+ + Base64.getEncoder().encodeToString(outputStream.toByteArray())
+ + "</vendor-effect></vibration-effect>";
+ assertPublicApisParserFails(emptyBundleString);
+ assertHiddenApisParserFails(emptyBundleString);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void testVendorEffect_featureFlagDisabled_allFail() throws Exception {
+ PersistableBundle vendorData = new PersistableBundle();
+ vendorData.putInt("id", 1);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ vendorData.writeToStream(outputStream);
+ String vendorDataStr = Base64.getEncoder().encodeToString(outputStream.toByteArray());
+ String xml = "<vibration-effect><vendor-effect>"
+ + vendorDataStr
+ + "</vendor-effect></vibration-effect>";
+ VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData);
+
+ assertPublicApisParserFails(xml);
+ assertPublicApisSerializerFails(vendorEffect);
+
+ assertHiddenApisParserFails(xml);
+ assertHiddenApisSerializerFails(vendorEffect);
+ }
+
private void assertPublicApisParserFails(String xml) {
assertThrows("Expected parseVibrationEffect to fail for " + xml,
VibrationXmlParser.ParseFailedException.class,
@@ -493,6 +597,12 @@
() -> serialize(effect));
}
+ private void assertHiddenApisSerializerFails(VibrationEffect effect) {
+ assertThrows("Expected serialization to fail for " + effect,
+ VibrationXmlSerializer.SerializationFailedException.class,
+ () -> serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS));
+ }
+
private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
String... expectedSegments) throws Exception {
assertSerializationContainsSegments(serialize(effect), expectedSegments);
diff --git a/core/xsd/vibrator/vibration/schema/current.txt b/core/xsd/vibrator/vibration/schema/current.txt
index f0e13c4..280b405 100644
--- a/core/xsd/vibrator/vibration/schema/current.txt
+++ b/core/xsd/vibrator/vibration/schema/current.txt
@@ -41,9 +41,11 @@
ctor public VibrationEffect();
method public com.android.internal.vibrator.persistence.PredefinedEffect getPredefinedEffect_optional();
method public com.android.internal.vibrator.persistence.PrimitiveEffect getPrimitiveEffect_optional();
+ method public byte[] getVendorEffect_optional();
method public com.android.internal.vibrator.persistence.WaveformEffect getWaveformEffect_optional();
method public void setPredefinedEffect_optional(com.android.internal.vibrator.persistence.PredefinedEffect);
method public void setPrimitiveEffect_optional(com.android.internal.vibrator.persistence.PrimitiveEffect);
+ method public void setVendorEffect_optional(byte[]);
method public void setWaveformEffect_optional(com.android.internal.vibrator.persistence.WaveformEffect);
}
diff --git a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
index fcd250b..21a6fac 100644
--- a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
+++ b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd
@@ -46,6 +46,9 @@
<!-- Predefined vibration effect -->
<xs:element name="predefined-effect" type="PredefinedEffect"/>
+ <!-- Vendor vibration effect -->
+ <xs:element name="vendor-effect" type="VendorEffect"/>
+
<!-- Primitive composition effect -->
<xs:sequence>
<xs:element name="primitive-effect" type="PrimitiveEffect"/>
@@ -136,6 +139,10 @@
</xs:restriction>
</xs:simpleType>
+ <xs:simpleType name="VendorEffect">
+ <xs:restriction base="xs:base64Binary"/>
+ </xs:simpleType>
+
<xs:complexType name="PrimitiveEffect">
<xs:attribute name="name" type="PrimitiveEffectName" use="required"/>
<xs:attribute name="scale" type="PrimitiveScale"/>
diff --git a/core/xsd/vibrator/vibration/vibration.xsd b/core/xsd/vibrator/vibration/vibration.xsd
index b9de691..d35d777 100644
--- a/core/xsd/vibrator/vibration/vibration.xsd
+++ b/core/xsd/vibrator/vibration/vibration.xsd
@@ -44,6 +44,9 @@
<!-- Predefined vibration effect -->
<xs:element name="predefined-effect" type="PredefinedEffect"/>
+ <!-- Vendor vibration effect -->
+ <xs:element name="vendor-effect" type="VendorEffect"/>
+
<!-- Primitive composition effect -->
<xs:sequence>
<xs:element name="primitive-effect" type="PrimitiveEffect"/>
@@ -113,6 +116,10 @@
</xs:restriction>
</xs:simpleType>
+ <xs:simpleType name="VendorEffect">
+ <xs:restriction base="xs:base64Binary"/>
+ </xs:simpleType>
+
<xs:complexType name="PrimitiveEffect">
<xs:attribute name="name" type="PrimitiveEffectName" use="required"/>
<xs:attribute name="scale" type="PrimitiveScale"/>
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 050f9b5..8f85617 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -78,6 +78,12 @@
src: "package-shareduid-allowlist.xml",
}
+prebuilt_etc {
+ name: "oem-defined-uids.xml",
+ sub_dir: "sysconfig",
+ src: "oem-defined-uids.xml",
+}
+
// Privapp permission whitelist files
prebuilt_etc {
diff --git a/data/etc/oem-defined-uids.xml b/data/etc/oem-defined-uids.xml
new file mode 100644
index 0000000..87435b9
--- /dev/null
+++ b/data/etc/oem-defined-uids.xml
@@ -0,0 +1,38 @@
+<?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.
+ -->
+
+<!--
+This XML defines a list of UIDs for OEMs to register as shared UIDs. They will be registered at the
+start of the system, which allows OEMs to create services with these UIDs. The range of these UIDs
+must be in the OEM reserved range.
+
+OEM must provide a preloaded app that is installed at boot time to retain the newly registered UID
+by adding a android:sharedUserId tag in the manifest of the preloaded app, with the value of the tag
+set to the name of the UID defined in this config file. Otherwise, the uid will be cleared at the
+end of the boot and this config file will take no effect.
+
+- The "name" XML attribute refers to the name of the shared UID. It must start with "android.uid.".
+- The "uid" XML attribute refers to the value of the shared UID. It must be in range [2900, 2999].
+
+Example usage
+ <oem-defined-uid name="android.uid.vendordata" uid="2918"/>
+ Indicates that a shared UID named "android.uid.vendordata" will be added to the system with the
+ UID of 2918.
+-->
+
+<config>
+</config>
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 889a7785..fd78816 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -56,7 +56,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.text.flags.Flags;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -75,7 +74,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -145,23 +143,6 @@
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
private static final Object sDynamicCacheLock = new Object();
- private static final LruCache<Long, LruCache<String, Typeface>> sVariableCache =
- new LruCache<>(16);
- private static final Object sVariableCacheLock = new Object();
-
- /** @hide */
- @VisibleForTesting
- public static void clearTypefaceCachesForTestingPurpose() {
- synchronized (sWeightCacheLock) {
- sWeightTypefaceCache.clear();
- }
- synchronized (sDynamicCacheLock) {
- sDynamicTypefaceCache.evictAll();
- }
- synchronized (sVariableCacheLock) {
- sVariableCache.evictAll();
- }
- }
@GuardedBy("SYSTEM_FONT_MAP_LOCK")
static Typeface sDefaultTypeface;
@@ -214,8 +195,6 @@
@UnsupportedAppUsage
public final long native_instance;
- private final Typeface mDerivedFrom;
-
private final String mSystemFontFamilyName;
private final Runnable mCleaner;
@@ -295,18 +274,6 @@
}
/**
- * Returns the Typeface used for creating this Typeface.
- *
- * Maybe null if this is not derived from other Typeface.
- * TODO(b/357707916): Make this public API.
- * @hide
- */
- @VisibleForTesting
- public final @Nullable Typeface getDerivedFrom() {
- return mDerivedFrom;
- }
-
- /**
* Returns the system font family name if the typeface was created from a system font family,
* otherwise returns null.
*/
@@ -1054,51 +1021,9 @@
return typeface;
}
- private static String axesToVarKey(@NonNull List<FontVariationAxis> axes) {
- // The given list can be mutated because it is allocated in Paint#setFontVariationSettings.
- // Currently, Paint#setFontVariationSettings is the only code path reaches this method.
- axes.sort(Comparator.comparingInt(FontVariationAxis::getOpenTypeTagValue));
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < axes.size(); ++i) {
- final FontVariationAxis fva = axes.get(i);
- sb.append(fva.getTag());
- sb.append(fva.getStyleValue());
- }
- return sb.toString();
- }
-
- /**
- * TODO(b/357707916): Make this public API.
- * @hide
- */
+ /** @hide */
public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
@NonNull List<FontVariationAxis> axes) {
- if (Flags.typefaceCacheForVarSettings()) {
- final Typeface target = (family == null) ? Typeface.DEFAULT : family;
- final Typeface base = (target.mDerivedFrom == null) ? target : target.mDerivedFrom;
-
- final String key = axesToVarKey(axes);
-
- synchronized (sVariableCacheLock) {
- LruCache<String, Typeface> innerCache = sVariableCache.get(base.native_instance);
- if (innerCache == null) {
- // Cache up to 16 var instance per root Typeface
- innerCache = new LruCache<>(16);
- sVariableCache.put(base.native_instance, innerCache);
- } else {
- Typeface cached = innerCache.get(key);
- if (cached != null) {
- return cached;
- }
- }
- Typeface typeface = new Typeface(
- nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
- base.getSystemFontFamilyName(), base);
- innerCache.put(key, typeface);
- return typeface;
- }
- }
-
final Typeface base = family == null ? Typeface.DEFAULT : family;
Typeface typeface = new Typeface(
nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
@@ -1259,19 +1184,11 @@
// don't allow clients to call this directly
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Typeface(long ni) {
- this(ni, null, null);
+ this(ni, null);
}
-
// don't allow clients to call this directly
- // This is kept for robolectric.
private Typeface(long ni, @Nullable String systemFontFamilyName) {
- this(ni, systemFontFamilyName, null);
- }
-
- // don't allow clients to call this directly
- private Typeface(long ni, @Nullable String systemFontFamilyName,
- @Nullable Typeface derivedFrom) {
if (ni == 0) {
throw new RuntimeException("native typeface cannot be made");
}
@@ -1281,7 +1198,6 @@
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
mSystemFontFamilyName = systemFontFamilyName;
- mDerivedFrom = derivedFrom;
}
/**
diff --git a/graphics/java/android/graphics/drawable/TEST_MAPPING b/graphics/java/android/graphics/drawable/TEST_MAPPING
index 1018702..4f06452 100644
--- a/graphics/java/android/graphics/drawable/TEST_MAPPING
+++ b/graphics/java/android/graphics/drawable/TEST_MAPPING
@@ -12,13 +12,7 @@
},
{
- "name": "FrameworksCoreTests",
- "file_patterns": ["(/|^)Icon\\.java"],
- "options" : [
- {
- "include-filter": "android.graphics.drawable.IconTest"
- }
- ]
+ "name": "FrameworksCoreTests_drawable"
}
]
}
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 25e7107..26d180c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -695,12 +695,8 @@
break;
case TYPE_ACTIVITY_REPARENTED_TO_TASK:
final IBinder candidateAssociatedActToken, lastOverlayToken;
- if (Flags.fixPipRestoreToOverlay()) {
- candidateAssociatedActToken = change.getOtherActivityToken();
- lastOverlayToken = change.getTaskFragmentToken();
- } else {
- candidateAssociatedActToken = lastOverlayToken = null;
- }
+ candidateAssociatedActToken = change.getOtherActivityToken();
+ lastOverlayToken = change.getTaskFragmentToken();
onActivityReparentedToTask(
wct,
taskId,
@@ -1023,10 +1019,6 @@
@Nullable
OverlayContainerRestoreParams getOverlayContainerRestoreParams(
@Nullable IBinder associatedActivityToken, @Nullable IBinder overlayToken) {
- if (!Flags.fixPipRestoreToOverlay()) {
- return null;
- }
-
if (associatedActivityToken == null || overlayToken == null) {
return null;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index d0e2c99..ee3e6f3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -36,7 +36,6 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -257,7 +256,7 @@
mPendingAppearedIntent = pendingAppearedIntent;
// Save the information necessary for restoring the overlay when needed.
- if (Flags.fixPipRestoreToOverlay() && overlayTag != null && pendingAppearedIntent != null
+ if (overlayTag != null && pendingAppearedIntent != null
&& associatedActivity != null && !associatedActivity.isFinishing()) {
final IBinder associatedActivityToken = associatedActivity.getActivityToken();
final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken,
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 475475b..90eeb58 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -874,8 +874,6 @@
@Test
public void testOnActivityReparentedToTask_overlayRestoration() {
- mSetFlagRule.enableFlags(Flags.FLAG_FIX_PIP_RESTORE_TO_OVERLAY);
-
// Prepares and mock the data necessary for the test.
final IBinder activityToken = mActivity.getActivityToken();
final Intent intent = new Intent();
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 5135e9e..1c3d9c3 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -166,6 +166,16 @@
},
}
+java_library {
+ name: "WindowManager-Shell-lite-proto",
+
+ srcs: ["src/com/android/wm/shell/desktopmode/education/data/proto/**/*.proto"],
+
+ proto: {
+ type: "lite",
+ },
+}
+
filegroup {
name: "wm_shell-shared-aidls",
@@ -215,6 +225,7 @@
"androidx.core_core-animation",
"androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
+ "androidx.datastore_datastore",
"androidx.compose.material3_material3",
"androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
@@ -225,6 +236,7 @@
"//frameworks/libs/systemui:iconloader_base",
"com_android_wm_shell_flags_lib",
"WindowManager-Shell-proto",
+ "WindowManager-Shell-lite-proto",
"WindowManager-Shell-shared",
"perfetto_trace_java_protos",
"dagger2",
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index 2e19d52..c6044a4 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,5 +1,5 @@
xutan@google.com
# Give submodule owners in shell resource approval
-per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, vaniadesmonda@google.com, pbdr@google.com, tkachenkoi@google.com, mpodolian@google.com, liranb@google.com, pragyabajoria@google.com, uysalorhan@google.com, gsennton@google.com
+per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, vaniadesmonda@google.com, pbdr@google.com, tkachenkoi@google.com, mpodolian@google.com, liranb@google.com, pragyabajoria@google.com, uysalorhan@google.com, gsennton@google.com, mattsziklay@google.com, mdehaini@google.com
per-file res*/*/tv_*.xml = bronger@google.com
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 5e67333..84f7bb2 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -53,6 +53,7 @@
import org.mockito.kotlin.mock
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import com.android.wm.shell.common.bubbles.BubbleBarLocation
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
@@ -458,5 +459,7 @@
override fun isShowingAsBubbleBar(): Boolean = false
override fun hideCurrentInputMethod() {}
+
+ override fun updateBubbleBarLocation(location: BubbleBarLocation) {}
}
}
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
index bc59a23..debcba0 100644
--- a/libs/WindowManager/Shell/res/values/ids.xml
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -42,6 +42,8 @@
<item type="id" name="action_move_top_right"/>
<item type="id" name="action_move_bottom_left"/>
<item type="id" name="action_move_bottom_right"/>
+ <item type="id" name="action_move_bubble_bar_left"/>
+ <item type="id" name="action_move_bubble_bar_right"/>
<item type="id" name="dismiss_view"/>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 0a8166f..6a62d7a3 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -139,6 +139,14 @@
<string name="bubble_accessibility_action_move_bottom_left">Move bottom left</string>
<!-- Action in accessibility menu to move the stack of bubbles to the bottom right of the screen. [CHAR LIMIT=30]-->
<string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string>
+ <!-- Click action label for bubbles to expand menu. [CHAR LIMIT=30]-->
+ <string name="bubble_accessibility_action_expand_menu">expand menu</string>
+ <!-- Click action label for bubbles to collapse menu. [CHAR LIMIT=30]-->
+ <string name="bubble_accessibility_action_collapse_menu">collapse menu</string>
+ <!-- Action in accessibility menu to move the bubble bar to the left side of the screen. [CHAR_LIMIT=30] -->
+ <string name="bubble_accessibility_action_move_bar_left">Move left</string>
+ <!-- Action in accessibility menu to move the bubble bar to the right side of the screen. [CHAR_LIMIT=30] -->
+ <string name="bubble_accessibility_action_move_bar_right">Move right</string>
<!-- Accessibility announcement when the stack of bubbles expands. [CHAR LIMIT=NONE]-->
<string name="bubble_accessibility_announce_expand">expand <xliff:g id="bubble_title" example="Messages">%1$s</xliff:g></string>
<!-- Accessibility announcement when the stack of bubbles collapses. [CHAR LIMIT=NONE]-->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
index 8d30db6..5355138 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -18,6 +18,7 @@
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import android.annotation.CallSuper;
import android.graphics.Point;
@@ -146,6 +147,14 @@
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
// Update the surface position and alpha.
+ if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+ && mAnimation.getExtensionEdges() != 0x0
+ && !(mChange.hasFlags(FLAG_TRANSLUCENT)
+ && mChange.getActivityComponent() != null)) {
+ // Extend non-translucent activities
+ t.setEdgeExtensionEffect(mLeash, mAnimation.getExtensionEdges());
+ }
+
mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
@@ -165,7 +174,7 @@
if (!cropRect.intersect(mWholeAnimationBounds)) {
// Hide the surface when it is outside of the animation area.
t.setAlpha(mLeash, 0);
- } else if (mAnimation.hasExtension()) {
+ } else if (mAnimation.getExtensionEdges() != 0) {
// Allow the surface to be shown in its original bounds in case we want to use edge
// extensions.
cropRect.union(mContentBounds);
@@ -180,6 +189,10 @@
@CallSuper
void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
onAnimationUpdate(t, mAnimation.getDuration());
+ if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+ && mAnimation.getExtensionEdges() != 0x0) {
+ t.setEdgeExtensionEffect(mLeash, /* edge */ 0);
+ }
}
final long getDurationHint() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 5696a54..d2cef4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -144,8 +144,10 @@
// ending states.
prepareForJumpCut(info, startTransaction);
} else {
- addEdgeExtensionIfNeeded(startTransaction, finishTransaction,
- postStartTransactionCallbacks, adapters);
+ if (!com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+ addEdgeExtensionIfNeeded(startTransaction, finishTransaction,
+ postStartTransactionCallbacks, adapters);
+ }
addBackgroundColorIfNeeded(info, startTransaction, finishTransaction, adapters);
for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
duration = Math.max(duration, adapter.getDurationHint());
@@ -341,7 +343,7 @@
@NonNull List<ActivityEmbeddingAnimationAdapter> adapters) {
for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
final Animation animation = adapter.mAnimation;
- if (!animation.hasExtension()) {
+ if (animation.getExtensionEdges() == 0) {
continue;
}
if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 7275c64..1242287 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -95,6 +95,7 @@
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
/**
* Controls the window animation run when a user initiates a back gesture.
@@ -1209,7 +1210,7 @@
}
if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
- && !isGestureBackTransition(info)) {
+ && isNotGestureBackTransition(info)) {
return false;
}
@@ -1364,7 +1365,7 @@
// try to handle unexpected transition
mergePendingTransitions(info);
- if (!isGestureBackTransition(info) || shouldCancelAnimation(info)
+ if (isNotGestureBackTransition(info) || shouldCancelAnimation(info)
|| !mCloseTransitionRequested) {
if (mPrepareOpenTransition != null) {
applyFinishOpenTransition();
@@ -1395,8 +1396,8 @@
// Cancel close animation if something happen unexpected, let another handler to handle
private boolean shouldCancelAnimation(@NonNull TransitionInfo info) {
- final boolean noCloseAllowed =
- info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
+ final boolean noCloseAllowed = !mCloseTransitionRequested
+ && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
boolean unableToHandle = false;
boolean filterTargets = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -1455,7 +1456,11 @@
if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
return false;
}
-
+ // Must have open target, must not have close target.
+ if (hasAnimationInMode(info, TransitionUtil::isClosingMode)
+ || !hasAnimationInMode(info, TransitionUtil::isOpeningMode)) {
+ return false;
+ }
SurfaceControl openingLeash = null;
if (mApps != null) {
for (int i = mApps.length - 1; i >= 0; --i) {
@@ -1482,17 +1487,6 @@
return true;
}
- private boolean isGestureBackTransition(@NonNull TransitionInfo info) {
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change c = info.getChanges().get(i);
- if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
- && (TransitionUtil.isOpeningMode(c.getMode())
- || TransitionUtil.isClosingMode(c.getMode()))) {
- return true;
- }
- }
- return false;
- }
/**
* Check whether this transition is triggered from back gesture commitment.
* Reparent the transition targets to animation leashes, so the animation won't be broken.
@@ -1501,10 +1495,18 @@
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
- || !mCloseTransitionRequested) {
+ if (!mCloseTransitionRequested) {
return false;
}
+ // must have close target
+ if (!hasAnimationInMode(info, TransitionUtil::isClosingMode)) {
+ return false;
+ }
+ if (mApps == null) {
+ // animation is done
+ applyAndFinish(st, ft, finishCallback);
+ return true;
+ }
SurfaceControl openingLeash = null;
SurfaceControl closingLeash = null;
for (int i = mApps.length - 1; i >= 0; --i) {
@@ -1522,6 +1524,7 @@
final Point offset = c.getEndRelOffset();
st.setPosition(c.getLeash(), offset.x, offset.y);
st.reparent(c.getLeash(), openingLeash);
+ st.setAlpha(c.getLeash(), 1.0f);
} else if (TransitionUtil.isClosingMode(c.getMode())) {
st.reparent(c.getLeash(), closingLeash);
}
@@ -1592,6 +1595,21 @@
}
}
+ private static boolean isNotGestureBackTransition(@NonNull TransitionInfo info) {
+ return !hasAnimationInMode(info, TransitionUtil::isOpenOrCloseMode);
+ }
+
+ private static boolean hasAnimationInMode(@NonNull TransitionInfo info,
+ Predicate<Integer> mode) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED) && mode.test(c.getMode())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static ComponentName findComponentName(TransitionInfo.Change change) {
final ComponentName componentName = change.getActivityComponent();
if (componentName != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index f9a1d94..dc511be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -357,7 +357,9 @@
void showBadge() {
Bitmap appBadgeBitmap = mBubble.getAppBadge();
- if (appBadgeBitmap == null) {
+ final boolean isAppLaunchIntent = (mBubble instanceof Bubble)
+ && ((Bubble) mBubble).isAppLaunchIntent();
+ if (appBadgeBitmap == null || isAppLaunchIntent) {
mAppIcon.setVisibility(GONE);
return;
}
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 7dbbb04..5cd2cb7 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
@@ -50,6 +50,7 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
import com.android.wm.shell.common.bubbles.BubbleInfo;
@@ -246,7 +247,23 @@
mAppIntent = intent;
mDesiredHeight = Integer.MAX_VALUE;
mPackageName = intent.getPackage();
+ }
+ private Bubble(ShortcutInfo info, Executor mainExecutor) {
+ mGroupKey = null;
+ mLocusId = null;
+ mFlags = 0;
+ mUser = info.getUserHandle();
+ mIcon = info.getIcon();
+ mIsAppBubble = false;
+ mKey = getBubbleKeyForShortcut(info);
+ mShowBubbleUpdateDot = false;
+ mMainExecutor = mainExecutor;
+ mTaskId = INVALID_TASK_ID;
+ mAppIntent = null;
+ mDesiredHeight = Integer.MAX_VALUE;
+ mPackageName = info.getPackage();
+ mShortcutInfo = info;
}
/** Creates an app bubble. */
@@ -263,6 +280,13 @@
mainExecutor);
}
+ /** Creates a shortcut bubble. */
+ public static Bubble createShortcutBubble(
+ ShortcutInfo info,
+ Executor mainExecutor) {
+ return new Bubble(info, mainExecutor);
+ }
+
/**
* Returns the key for an app bubble from an app with package name, {@code packageName} on an
* Android user, {@code user}.
@@ -273,6 +297,14 @@
return KEY_APP_BUBBLE + ":" + user.getIdentifier() + ":" + packageName;
}
+ /**
+ * Returns the key for a shortcut bubble using {@code packageName}, {@code user}, and the
+ * {@code shortcutInfo} id.
+ */
+ public static String getBubbleKeyForShortcut(ShortcutInfo info) {
+ return info.getPackage() + ":" + info.getUserId() + ":" + info.getId();
+ }
+
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
final Bubbles.BubbleMetadataFlagListener listener,
@@ -888,6 +920,17 @@
return mIntent;
}
+ /**
+ * Whether this bubble represents the full app, i.e. the intent used is the launch
+ * intent for an app. In this case we don't show a badge on the icon.
+ */
+ public boolean isAppLaunchIntent() {
+ if (Flags.enableBubbleAnything() && mAppIntent != null) {
+ return mAppIntent.hasCategory("android.intent.category.LAUNCHER");
+ }
+ return false;
+ }
+
@Nullable
PendingIntent getDeleteIntent() {
return mDeleteIntent;
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 949a723..29520ef 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
@@ -1335,6 +1335,40 @@
}
/**
+ * Expands and selects a bubble created or found via the provided shortcut info.
+ *
+ * @param info the shortcut info for the bubble.
+ */
+ public void expandStackAndSelectBubble(ShortcutInfo info) {
+ if (!Flags.enableBubbleAnything()) return;
+ Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
+ if (b.isInflated()) {
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
+ } else {
+ b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+ inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
+ }
+ }
+
+ /**
+ * Expands and selects a bubble created or found for this app.
+ *
+ * @param intent the intent for the bubble.
+ */
+ public void expandStackAndSelectBubble(Intent intent) {
+ if (!Flags.enableBubbleAnything()) return;
+ Bubble b = mBubbleData.getOrCreateBubble(intent); // Removes from overflow
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
+ if (b.isInflated()) {
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
+ } else {
+ b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+ inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
+ }
+ }
+
+ /**
* Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble
* exists for this entry, and it is able to bubble, a new bubble will be created.
*
@@ -2323,6 +2357,7 @@
* @param entry the entry to bubble.
*/
static boolean canLaunchInTaskView(Context context, BubbleEntry entry) {
+ if (Flags.enableBubbleAnything()) return true;
PendingIntent intent = entry.getBubbleMetadata() != null
? entry.getBubbleMetadata().getIntent()
: null;
@@ -2439,6 +2474,16 @@
}
@Override
+ public void showShortcutBubble(ShortcutInfo info) {
+ mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(info));
+ }
+
+ @Override
+ public void showAppBubble(Intent intent) {
+ mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(intent));
+ }
+
+ @Override
public void showBubble(String key, int topOnScreen) {
mMainExecutor.execute(
() -> mController.expandStackAndSelectBubbleFromLauncher(key, topOnScreen));
@@ -2634,6 +2679,13 @@
}
@Override
+ public void expandStackAndSelectBubble(ShortcutInfo info) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.expandStackAndSelectBubble(info);
+ });
+ }
+
+ @Override
public void expandStackAndSelectBubble(Bubble bubble) {
mMainExecutor.execute(() -> {
BubbleController.this.expandStackAndSelectBubble(bubble);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index b6da761..3c6c6fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -23,8 +23,10 @@
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.LocusId;
import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -421,23 +423,19 @@
Bubble bubbleToReturn = getBubbleInStackWithKey(key);
if (bubbleToReturn == null) {
- bubbleToReturn = getOverflowBubbleWithKey(key);
- if (bubbleToReturn != null) {
- // Promoting from overflow
- mOverflowBubbles.remove(bubbleToReturn);
- if (mOverflowBubbles.isEmpty()) {
- mStateChange.showOverflowChanged = true;
+ // Check if it's in the overflow
+ bubbleToReturn = findAndRemoveBubbleFromOverflow(key);
+ if (bubbleToReturn == null) {
+ if (entry != null) {
+ // Not in the overflow, have an entry, so it's a new bubble
+ bubbleToReturn = new Bubble(entry,
+ mBubbleMetadataFlagListener,
+ mCancelledListener,
+ mMainExecutor);
+ } else {
+ // If there's no entry it must be a persisted bubble
+ bubbleToReturn = persistedBubble;
}
- } else if (mPendingBubbles.containsKey(key)) {
- // Update while it was pending
- bubbleToReturn = mPendingBubbles.get(key);
- } else if (entry != null) {
- // New bubble
- bubbleToReturn = new Bubble(entry, mBubbleMetadataFlagListener, mCancelledListener,
- mMainExecutor);
- } else {
- // Persisted bubble being promoted
- bubbleToReturn = persistedBubble;
}
}
@@ -448,6 +446,46 @@
return bubbleToReturn;
}
+ Bubble getOrCreateBubble(ShortcutInfo info) {
+ String bubbleKey = Bubble.getBubbleKeyForShortcut(info);
+ Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+ if (bubbleToReturn == null) {
+ bubbleToReturn = Bubble.createShortcutBubble(info, mMainExecutor);
+ }
+ return bubbleToReturn;
+ }
+
+ Bubble getOrCreateBubble(Intent intent) {
+ UserHandle user = UserHandle.of(mCurrentUserId);
+ String bubbleKey = Bubble.getAppBubbleKeyForApp(intent.getPackage(),
+ user);
+ Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+ if (bubbleToReturn == null) {
+ bubbleToReturn = Bubble.createAppBubble(intent, user, null, mMainExecutor);
+ }
+ return bubbleToReturn;
+ }
+
+ @Nullable
+ private Bubble findAndRemoveBubbleFromOverflow(String key) {
+ Bubble bubbleToReturn = getBubbleInStackWithKey(key);
+ if (bubbleToReturn != null) {
+ return bubbleToReturn;
+ }
+ bubbleToReturn = getOverflowBubbleWithKey(key);
+ if (bubbleToReturn != null) {
+ mOverflowBubbles.remove(bubbleToReturn);
+ // Promoting from overflow
+ mOverflowBubbles.remove(bubbleToReturn);
+ if (mOverflowBubbles.isEmpty()) {
+ mStateChange.showOverflowChanged = true;
+ }
+ } else if (mPendingBubbles.containsKey(key)) {
+ bubbleToReturn = mPendingBubbles.get(key);
+ }
+ return bubbleToReturn;
+ }
+
/**
* When this method is called it is expected that all info in the bubble has completed loading.
* @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index fdb4523..e16db22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -232,6 +232,9 @@
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
+ || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
+
if (mBubble.isAppBubble()) {
Context context =
mContext.createContextAsUser(
@@ -246,7 +249,8 @@
/* options= */ null);
mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
launchBounds);
- } else if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+ } else if (!mIsOverflow && isShortcutBubble) {
+ ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey());
options.setApplyActivityFlagsForBubbles(true);
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
@@ -1148,5 +1152,7 @@
pw.print(prefix); pw.println("BubbleExpandedView:");
pw.print(prefix); pw.print(" taskId: "); pw.println(mTaskId);
pw.print(prefix); pw.print(" stackView: "); pw.println(mStackView);
+ pw.print(prefix); pw.print(" contentVisibility: "); pw.println(mIsContentVisible);
+ pw.print(prefix); pw.print(" isAnimating: "); pw.println(mIsAnimating);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index 3d9bf03..4e80e90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles
+import com.android.wm.shell.common.bubbles.BubbleBarLocation
+
/** Manager interface for bubble expanded views. */
interface BubbleExpandedViewManager {
@@ -30,6 +32,7 @@
fun isStackExpanded(): Boolean
fun isShowingAsBubbleBar(): Boolean
fun hideCurrentInputMethod()
+ fun updateBubbleBarLocation(location: BubbleBarLocation)
companion object {
/**
@@ -78,6 +81,10 @@
override fun hideCurrentInputMethod() {
controller.hideCurrentInputMethod()
}
+
+ override fun updateBubbleBarLocation(location: BubbleBarLocation) {
+ controller.bubbleBarLocation = location
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index efa1031..1016133 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -339,6 +339,7 @@
pw.println(mExpandedViewContainer.getAnimationMatrix());
pw.print(" stack visibility : "); pw.println(getVisibility());
pw.print(" temporarilyInvisible: "); pw.println(mTemporarilyInvisible);
+ pw.print(" expandedViewTemporarilyHidden: "); pw.println(mExpandedViewTemporarilyHidden);
mStackAnimationController.dump(pw);
mExpandedAnimationController.dump(pw);
@@ -1601,6 +1602,11 @@
getResources().getColor(android.R.color.system_neutral1_1000)));
mManageMenuScrim.setBackgroundDrawable(new ColorDrawable(
getResources().getColor(android.R.color.system_neutral1_1000)));
+ if (mShowingManage) {
+ // the manage menu location depends on the manage button location which may need a
+ // layout pass, so post this to the looper
+ post(() -> showManageMenu(true));
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 5e2141a..5f8f0fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -36,6 +36,7 @@
import androidx.annotation.Nullable;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.taskview.TaskView;
/**
@@ -110,6 +111,8 @@
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
+ || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
if (mBubble.isAppBubble()) {
Context context =
mContext.createContextAsUser(
@@ -124,7 +127,7 @@
/* options= */ null);
mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
launchBounds);
- } else if (mBubble.hasMetadataShortcutId()) {
+ } else if (isShortcutBubble) {
options.setApplyActivityFlagsForBubbles(true);
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 589dfd2..9a27fb6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -23,6 +23,7 @@
import android.app.NotificationChannel;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.content.pm.UserInfo;
import android.graphics.drawable.Icon;
import android.hardware.HardwareBuffer;
@@ -118,6 +119,14 @@
/**
* Request the stack expand if needed, then select the specified Bubble as current.
+ * If no bubble exists for this entry, one is created.
+ *
+ * @param info the shortcut info to use to create the bubble.
+ */
+ void expandStackAndSelectBubble(ShortcutInfo info);
+
+ /**
+ * Request the stack expand if needed, then select the specified Bubble as current.
*
* @param bubble the bubble to be selected
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 0907ddd..5c78974 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.graphics.Rect;
+import android.content.pm.ShortcutInfo;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
@@ -48,4 +49,8 @@
oneway void updateBubbleBarTopOnScreen(in int topOnScreen) = 10;
oneway void stopBubbleDrag(in BubbleBarLocation location, in int topOnScreen) = 11;
+
+ oneway void showShortcutBubble(in ShortcutInfo info) = 12;
+
+ oneway void showAppBubble(in Intent intent) = 13;
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 24c568c..6d868d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -26,14 +26,18 @@
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.os.Bundle;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
@@ -42,6 +46,7 @@
import com.android.wm.shell.bubbles.BubbleTaskView;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.taskview.TaskView;
import java.util.function.Supplier;
@@ -81,6 +86,7 @@
private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
private static final int INVALID_TASK_ID = -1;
+ private Bubble mBubble;
private BubbleExpandedViewManager mManager;
private BubblePositioner mPositioner;
private boolean mIsOverflow;
@@ -188,12 +194,21 @@
// Handle view needs to draw on top of task view.
bringChildToFront(mHandleView);
+
+ mHandleView.setAccessibilityDelegate(new HandleViewAccessibilityDelegate());
}
mMenuViewController = new BubbleBarMenuViewController(mContext, this);
mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() {
@Override
public void onMenuVisibilityChanged(boolean visible) {
setObscured(visible);
+ if (visible) {
+ mHandleView.setFocusable(false);
+ mHandleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ } else {
+ mHandleView.setFocusable(true);
+ mHandleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
}
@Override
@@ -319,6 +334,7 @@
/** Updates the bubble shown in the expanded view. */
public void update(Bubble bubble) {
+ mBubble = bubble;
mBubbleTaskViewHelper.update(bubble);
mMenuViewController.updateMenu(bubble);
}
@@ -457,4 +473,51 @@
invalidateOutline();
}
}
+
+ private class HandleViewAccessibilityDelegate extends AccessibilityDelegate {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK, getResources().getString(
+ R.string.bubble_accessibility_action_expand_menu)));
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
+ if (mPositioner.isBubbleBarOnLeft()) {
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ R.id.action_move_bubble_bar_right, getResources().getString(
+ R.string.bubble_accessibility_action_move_bar_right)));
+ } else {
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ R.id.action_move_bubble_bar_left, getResources().getString(
+ R.string.bubble_accessibility_action_move_bar_left)));
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(@NonNull View host, int action,
+ @Nullable Bundle args) {
+ if (super.performAccessibilityAction(host, action, args)) {
+ return true;
+ }
+ if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) {
+ mManager.collapseStack();
+ return true;
+ }
+ if (action == AccessibilityNodeInfo.ACTION_DISMISS) {
+ mManager.dismissBubble(mBubble, Bubbles.DISMISS_USER_GESTURE);
+ return true;
+ }
+ if (action == R.id.action_move_bubble_bar_left) {
+ mManager.updateBubbleBarLocation(BubbleBarLocation.LEFT);
+ return true;
+ }
+ if (action == R.id.action_move_bubble_bar_right) {
+ mManager.updateBubbleBarLocation(BubbleBarLocation.RIGHT);
+ return true;
+ }
+ return false;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index 8389c81..0300869 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -23,7 +23,9 @@
import android.graphics.drawable.Icon;
import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -71,6 +73,16 @@
mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
mBubbleDismissIconView = findViewById(R.id.bubble_bar_manage_menu_dismiss_icon);
updateThemeColors();
+
+ mBubbleSectionView.setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK, getResources().getString(
+ R.string.bubble_accessibility_action_collapse_menu)));
+ }
+ });
}
private void updateThemeColors() {
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 b8b62a7..955361f 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
@@ -72,6 +72,7 @@
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.draganddrop.GlobalDragListener;
import com.android.wm.shell.freeform.FreeformComponents;
@@ -319,9 +320,19 @@
WindowDecorViewModel windowDecorViewModel,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellAnimationThread ShellExecutor animExecutor) {
- return new FreeformTaskTransitionHandler(shellInit, transitions, context,
- windowDecorViewModel, displayController, mainExecutor, animExecutor);
+ @ShellAnimationThread ShellExecutor animExecutor,
+ @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
+ InteractionJankMonitor interactionJankMonitor) {
+ return new FreeformTaskTransitionHandler(
+ shellInit,
+ transitions,
+ context,
+ windowDecorViewModel,
+ displayController,
+ mainExecutor,
+ animExecutor,
+ desktopModeTaskRepository,
+ interactionJankMonitor);
}
@WMSingleton
@@ -679,6 +690,13 @@
return new DesktopModeEventLogger();
}
+ @WMSingleton
+ @Provides
+ static AppHandleEducationDatastoreRepository provideAppHandleEducationDatastoreRepository(
+ Context context) {
+ return new AppHandleEducationDatastoreRepository(context);
+ }
+
//
// Drag and drop
//
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 400882a..05c9d02 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
@@ -73,25 +73,9 @@
sessionId,
taskUpdate.instanceId
)
- FrameworkStatsLog.write(
- DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
- /* task_event */
+ logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED,
- /* instance_id */
- taskUpdate.instanceId,
- /* uid */
- taskUpdate.uid,
- /* task_height */
- taskUpdate.taskHeight,
- /* task_width */
- taskUpdate.taskWidth,
- /* task_x */
- taskUpdate.taskX,
- /* task_y */
- taskUpdate.taskY,
- /* session_id */
- sessionId
- )
+ sessionId, taskUpdate)
}
/**
@@ -105,25 +89,9 @@
sessionId,
taskUpdate.instanceId
)
- FrameworkStatsLog.write(
- DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
- /* task_event */
+ logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED,
- /* instance_id */
- taskUpdate.instanceId,
- /* uid */
- taskUpdate.uid,
- /* task_height */
- taskUpdate.taskHeight,
- /* task_width */
- taskUpdate.taskWidth,
- /* task_x */
- taskUpdate.taskX,
- /* task_y */
- taskUpdate.taskY,
- /* session_id */
- sessionId
- )
+ sessionId, taskUpdate)
}
/**
@@ -137,10 +105,16 @@
sessionId,
taskUpdate.instanceId
)
+ logTaskUpdate(
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
+ sessionId, taskUpdate)
+ }
+
+ private fun logTaskUpdate(taskEvent: Int, sessionId: Int, taskUpdate: TaskUpdate) {
FrameworkStatsLog.write(
DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
/* task_event */
- FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
+ taskEvent,
/* instance_id */
taskUpdate.instanceId,
/* uid */
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 73aa7ce..a6ed3b8 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
@@ -22,6 +22,7 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.os.IBinder
+import android.os.Trace
import android.util.SparseArray
import android.view.SurfaceControl
import android.view.WindowManager
@@ -51,6 +52,8 @@
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
+const val VISIBLE_TASKS_COUNTER_NAME = "DESKTOP_MODE_VISIBLE_TASKS"
+
/**
* A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log
* appropriate desktop mode session log events. This observes transitions related to desktop mode
@@ -292,8 +295,14 @@
val previousTaskInfo = preTransitionVisibleFreeformTasks[taskId]
when {
// new tasks added
- previousTaskInfo == null ->
+ previousTaskInfo == null -> {
desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate)
+ Trace.setCounter(
+ Trace.TRACE_TAG_WINDOW_MANAGER,
+ VISIBLE_TASKS_COUNTER_NAME,
+ postTransitionVisibleFreeformTasks.size().toLong()
+ )
+ }
// old tasks that were resized or repositioned
// TODO(b/347935387): Log changes only once they are stable.
buildTaskUpdateForTask(previousTaskInfo) != currentTaskUpdate ->
@@ -305,6 +314,11 @@
preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
desktopModeEventLogger.logTaskRemoved(sessionId, buildTaskUpdateForTask(taskInfo))
+ Trace.setCounter(
+ Trace.TRACE_TAG_WINDOW_MANAGER,
+ VISIBLE_TASKS_COUNTER_NAME,
+ postTransitionVisibleFreeformTasks.size().toLong()
+ )
}
}
}
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 8375294..9d04169 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
@@ -194,6 +194,11 @@
fun getActiveNonMinimizedOrderedTasks(displayId: Int): List<Int> =
getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
+ /** Returns the count of active non-minimized tasks for [displayId]. */
+ fun getActiveNonMinimizedTaskCount(displayId: Int): Int {
+ return getActiveTasks(displayId).count { !isMinimizedTask(it) }
+ }
+
/** Returns a list of freeform tasks, ordered from top-bottom (top at index 0). */
fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
ArrayList(desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder ?: emptyList())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 38675129..597637d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -70,11 +70,11 @@
// TODO(b/333018485): replace this observer when implementing the minimize-animation
private inner class MinimizeTransitionObserver : TransitionObserver {
- private val mPendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
- private val mActiveTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+ private val pendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+ private val activeTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
fun addPendingTransitionToken(transition: IBinder, taskDetails: TaskDetails) {
- mPendingTransitionTokensAndTasks[transition] = taskDetails
+ pendingTransitionTokensAndTasks[transition] = taskDetails
}
override fun onTransitionReady(
@@ -83,9 +83,7 @@
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction
) {
- val taskToMinimize = mPendingTransitionTokensAndTasks.remove(transition) ?: return
- taskToMinimize.transitionInfo = info
- mActiveTransitionTokensAndTasks[transition] = taskToMinimize
+ val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return
if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
@@ -97,6 +95,8 @@
return
}
+ taskToMinimize.transitionInfo = info
+ activeTransitionTokensAndTasks[transition] = taskToMinimize
this@DesktopTasksLimiter.markTaskMinimized(
taskToMinimize.displayId, taskToMinimize.taskId)
}
@@ -121,7 +121,7 @@
}
override fun onTransitionStarting(transition: IBinder) {
- val mActiveTaskDetails = mActiveTransitionTokensAndTasks[transition]
+ val mActiveTaskDetails = activeTransitionTokensAndTasks[transition]
if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
// Begin minimize window CUJ instrumentation.
interactionJankMonitor.begin(
@@ -132,11 +132,11 @@
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- if (mActiveTransitionTokensAndTasks.remove(merged) != null) {
+ if (activeTransitionTokensAndTasks.remove(merged) != null) {
interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
}
- mPendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
- mPendingTransitionTokensAndTasks[playing] = taskToTransfer
+ pendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
+ pendingTransitionTokensAndTasks[playing] = taskToTransfer
}
}
@@ -144,14 +144,14 @@
ProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"DesktopTasksLimiter: transition %s finished", transition)
- if (mActiveTransitionTokensAndTasks.remove(transition) != null) {
+ if (activeTransitionTokensAndTasks.remove(transition) != null) {
if (aborted) {
interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
} else {
interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
}
}
- mPendingTransitionTokensAndTasks.remove(transition)
+ pendingTransitionTokensAndTasks.remove(transition)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
new file mode 100644
index 0000000..bf4a2ab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.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.wm.shell.desktopmode.education.data
+
+import android.content.Context
+import android.util.Log
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.core.Serializer
+import androidx.datastore.dataStore
+import androidx.datastore.dataStoreFile
+import com.android.framework.protobuf.InvalidProtocolBufferException
+import com.android.internal.annotations.VisibleForTesting
+import java.io.InputStream
+import java.io.OutputStream
+import kotlinx.coroutines.flow.first
+
+/**
+ * Manages interactions with the App Handle education datastore.
+ *
+ * This class provides a layer of abstraction between the UI/business logic and the underlying
+ * DataStore.
+ */
+class AppHandleEducationDatastoreRepository
+@VisibleForTesting
+constructor(private val dataStore: DataStore<WindowingEducationProto>) {
+ constructor(
+ context: Context
+ ) : this(
+ DataStoreFactory.create(
+ serializer = WindowingEducationProtoSerializer,
+ produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) }))
+
+ /**
+ * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the
+ * DataStore is empty or there's an error reading, it returns the default value of Proto.
+ */
+ suspend fun windowingEducationProto(): WindowingEducationProto =
+ try {
+ dataStore.data.first()
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to read from datastore")
+ WindowingEducationProto.getDefaultInstance()
+ }
+
+ companion object {
+ private const val TAG = "AppHandleEducationDatastoreRepository"
+ private const val APP_HANDLE_EDUCATION_DATASTORE_FILEPATH = "app_handle_education.pb"
+
+ object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> {
+
+ override val defaultValue: WindowingEducationProto =
+ WindowingEducationProto.getDefaultInstance()
+
+ override suspend fun readFrom(input: InputStream): WindowingEducationProto =
+ try {
+ WindowingEducationProto.parseFrom(input)
+ } catch (exception: InvalidProtocolBufferException) {
+ throw CorruptionException("Cannot read proto.", exception)
+ }
+
+ override suspend fun writeTo(windowingProto: WindowingEducationProto, output: OutputStream) =
+ windowingProto.writeTo(output)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
new file mode 100644
index 0000000..d29ec53
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+option java_package = "com.android.wm.shell.desktopmode.education.data";
+option java_multiple_files = true;
+
+// Desktop Windowing education data
+message WindowingEducationProto {
+ // Timestamp in milliseconds of when the education was last viewed.
+ optional int64 education_viewed_timestamp_millis = 1;
+ // Timestamp in milliseconds of when the feature was last used.
+ optional int64 feature_used_timestamp_millis = 2;
+ oneof education_data {
+ // Fields specific to app handle education
+ AppHandleEducation app_handle_education = 3;
+ }
+
+ message AppHandleEducation {
+ // Map that stores app launch count for corresponding package
+ map<string, int32> app_usage_stats = 1;
+ // Timestamp of when app_usage_stats was last cached
+ optional int64 app_usage_stats_last_update_timestamp_millis = 2;
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 8402775..4284d06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,8 +38,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -56,7 +59,9 @@
private final Context mContext;
private final Transitions mTransitions;
private final WindowDecorViewModel mWindowDecorViewModel;
+ private final DesktopModeTaskRepository mDesktopModeTaskRepository;
private final DisplayController mDisplayController;
+ private final InteractionJankMonitor mInteractionJankMonitor;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
@@ -71,11 +76,15 @@
WindowDecorViewModel windowDecorViewModel,
DisplayController displayController,
ShellExecutor mainExecutor,
- ShellExecutor animExecutor) {
+ ShellExecutor animExecutor,
+ DesktopModeTaskRepository desktopModeTaskRepository,
+ InteractionJankMonitor interactionJankMonitor) {
mTransitions = transitions;
mContext = context;
mWindowDecorViewModel = windowDecorViewModel;
+ mDesktopModeTaskRepository = desktopModeTaskRepository;
mDisplayController = displayController;
+ mInteractionJankMonitor = interactionJankMonitor;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -238,13 +247,22 @@
startBounds.top + (animation.getAnimatedFraction() * screenHeight));
t.apply();
});
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animations.remove(animator);
- onAnimFinish.run();
- }
- });
+ if (mDesktopModeTaskRepository.getActiveNonMinimizedTaskCount(
+ change.getTaskInfo().displayId) == 1) {
+ // Starting the jank trace if closing the last window in desktop mode.
+ mInteractionJankMonitor.begin(
+ sc, mContext, CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE);
+ }
+ animator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animations.remove(animator);
+ onAnimFinish.run();
+ mInteractionJankMonitor.end(
+ CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE);
+ }
+ });
animations.add(animator);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index 93351c3..83b5bf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -9,3 +9,5 @@
pragyabajoria@google.com
uysalorhan@google.com
gsennton@google.com
+mattsziklay@google.com
+mdehaini@google.com
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 428cc91..86777df 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
@@ -496,7 +496,9 @@
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"startSwipePipToHome: %s, state=%s", componentName, mPipTransitionState);
mPipTransitionState.setInSwipePipToHomeTransition(true);
- if (!ENABLE_SHELL_TRANSITIONS) {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ mPipTransitionController.onStartSwipePipToHome();
+ } else {
sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
}
setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index ba97c832..37e2fd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -191,6 +191,11 @@
}
@Override
+ protected boolean isInSwipePipToHomeTransition() {
+ return mPipTransitionState.getInSwipePipToHomeTransition();
+ }
+
+ @Override
public void startExitTransition(int type, WindowContainerTransaction out,
@Nullable Rect destinationBounds) {
if (destinationBounds != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index fc9e2be..9b81581 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -189,6 +189,32 @@
mPipTransitionCallbacks.put(callback, executor);
}
+ protected void onStartSwipePipToHome() {
+ if (Flags.enablePipUiStateCallbackOnEntering()) {
+ try {
+ ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
+ new PictureInPictureUiState.Builder()
+ .setTransitioningToPip(true)
+ .build());
+ } catch (RemoteException | IllegalStateException e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "Failed to set alert PiP state change.");
+ }
+ }
+ }
+
+ /**
+ * Used in {@link #sendOnPipTransitionStarted(int)} to decide whether we should send the
+ * PictureInPictureUiState change callback on transition start.
+ * For instance, in auto-enter-pip case, {@link #onStartSwipePipToHome()} should have signaled
+ * the app, and we can return {@code true} here to avoid double callback.
+ *
+ * @return {@code true} if there is a ongoing swipe pip to home transition.
+ */
+ protected boolean isInSwipePipToHomeTransition() {
+ return false;
+ }
+
protected void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
final Rect pipBounds = mPipBoundsState.getBounds();
@@ -199,7 +225,8 @@
entry.getValue().execute(
() -> entry.getKey().onPipTransitionStarted(direction, pipBounds));
}
- if (isInPipDirection(direction) && Flags.enablePipUiStateCallbackOnEntering()) {
+ if (isInPipDirection(direction) && Flags.enablePipUiStateCallbackOnEntering()
+ && !isInSwipePipToHomeTransition()) {
try {
ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
new PictureInPictureUiState.Builder()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 7774384..dc21f82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -31,6 +31,8 @@
import android.os.Bundle;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.window.DisplayAreaInfo;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
@@ -40,6 +42,7 @@
import com.android.internal.util.Preconditions;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
@@ -71,7 +74,8 @@
*/
public class PipController implements ConfigurationChangeListener,
PipTransitionState.PipTransitionStateChangedListener,
- DisplayController.OnDisplaysChangedListener, RemoteCallable<PipController> {
+ DisplayController.OnDisplaysChangedListener,
+ DisplayChangeController.OnDisplayChangingListener, RemoteCallable<PipController> {
private static final String TAG = PipController.class.getSimpleName();
private static final String SWIPE_TO_PIP_APP_BOUNDS = "pip_app_bounds";
private static final String SWIPE_TO_PIP_OVERLAY = "swipe_to_pip_overlay";
@@ -197,11 +201,12 @@
mPipDisplayLayoutState.setDisplayLayout(layout);
mDisplayController.addDisplayWindowListener(this);
+ mDisplayController.addDisplayChangingController(this);
mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
new DisplayInsetsController.OnInsetsChangedListener() {
@Override
public void insetsChanged(InsetsState insetsState) {
- onDisplayChanged(mDisplayController
+ setDisplayLayout(mDisplayController
.getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
}
});
@@ -264,11 +269,12 @@
@Override
public void onThemeChanged() {
- onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()));
+ setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
}
//
- // DisplayController.OnDisplaysChangedListener implementations
+ // DisplayController.OnDisplaysChangedListener and
+ // DisplayChangeController.OnDisplayChangingListener implementations
//
@Override
@@ -276,7 +282,7 @@
if (displayId != mPipDisplayLayoutState.getDisplayId()) {
return;
}
- onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
}
@Override
@@ -284,10 +290,35 @@
if (displayId != mPipDisplayLayoutState.getDisplayId()) {
return;
}
- onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
}
- private void onDisplayChanged(DisplayLayout layout) {
+ /**
+ * A callback for any observed transition that contains a display change in its
+ * {@link android.window.TransitionRequestInfo} with a non-zero rotation delta.
+ */
+ @Override
+ public void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t) {
+ if (!mPipTransitionState.isInPip()) {
+ return;
+ }
+
+ // Calculate the snap fraction pre-rotation.
+ float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds());
+
+ // Update the caches to reflect the new display layout and movement bounds.
+ mPipDisplayLayoutState.rotateTo(toRotation);
+ mPipTouchHandler.updateMovementBounds();
+
+ // The policy is to keep PiP width, height and snap fraction invariant.
+ Rect toBounds = mPipBoundsState.getBounds();
+ mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
+ mPipBoundsState.setBounds(toBounds);
+ t.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+ }
+
+ private void setDisplayLayout(DisplayLayout layout) {
mPipDisplayLayoutState.setDisplayLayout(layout);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index d7c225b..d75fa00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -1081,7 +1081,7 @@
* Updates the current movement bounds based on whether the menu is currently visible and
* resized.
*/
- private void updateMovementBounds() {
+ void updateMovementBounds() {
Rect insetBounds = new Rect();
mPipBoundsAlgorithm.getInsetBounds(insetBounds);
mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 7790c51..846fa62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -137,6 +137,11 @@
}
}
+ @Override
+ protected boolean isInSwipePipToHomeTransition() {
+ return mPipTransitionState.isInSwipePipToHomeTransition();
+ }
+
//
// Transition collection stage lifecycle hooks
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 48d17ec6..c1f60383 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -439,9 +439,9 @@
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition: hasPendingResize=%b",
mPendingResize != null);
if (mPendingResize != null) {
+ mPendingResize.cancel(null);
mainDecor.cancelRunningAnimations();
sideDecor.cancelRunningAnimations();
- mPendingResize.cancel(null);
mAnimations.clear();
onFinish(null /* wct */);
}
@@ -504,7 +504,9 @@
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for passThrough transition");
}
- // TODO: handle transition consumed for active remote handler
+ if (mActiveRemoteHandler != null) {
+ mActiveRemoteHandler.onTransitionConsumed(transition, aborted, finishT);
+ }
}
void onFinish(WindowContainerTransaction wct) {
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 7784784..de6887a2 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
@@ -502,15 +502,19 @@
backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
backgroundColorForTransition);
- if (!isTask && a.hasExtension()) {
- if (!TransitionUtil.isOpeningType(mode)) {
- // Can screenshot now (before startTransaction is applied)
- edgeExtendWindow(change, a, startTransaction, finishTransaction);
+ if (!isTask && a.getExtensionEdges() != 0x0) {
+ if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+ finishTransaction.setEdgeExtensionEffect(change.getLeash(), /* edge */ 0);
} else {
- // Need to screenshot after startTransaction is applied otherwise activity
- // may not be visible or ready yet.
- postStartTransactionCallbacks
- .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
+ if (!TransitionUtil.isOpeningType(mode)) {
+ // Can screenshot now (before startTransaction is applied)
+ edgeExtendWindow(change, a, startTransaction, finishTransaction);
+ } else {
+ // Need to screenshot after startTransaction is applied otherwise
+ // activity may not be visible or ready yet.
+ postStartTransactionCallbacks
+ .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
+ }
}
}
@@ -558,7 +562,7 @@
buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, animRelOffset, cornerRadius,
- clipRect);
+ clipRect, change.getActivityComponent() != null);
final TransitionInfo.AnimationOptions options;
if (Flags.moveAnimationOptionsToChange()) {
@@ -823,7 +827,7 @@
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
- @Nullable Rect clipRect) {
+ @Nullable Rect clipRect, boolean isActivity) {
final SurfaceControl.Transaction transaction = pool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
@@ -835,13 +839,13 @@
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
- position, cornerRadius, clipRect);
+ position, cornerRadius, clipRect, isActivity);
};
va.addUpdateListener(updateListener);
final Runnable finisher = () -> {
applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
- position, cornerRadius, clipRect);
+ position, cornerRadius, clipRect, isActivity);
pool.release(transaction);
mainExecutor.execute(() -> {
@@ -931,7 +935,8 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+ change.getActivityComponent() != null);
}
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@@ -955,7 +960,8 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+ change.getActivityComponent() != null);
}
private static int getWallpaperTransitType(TransitionInfo info) {
@@ -1005,9 +1011,14 @@
private static void applyTransformation(long time, SurfaceControl.Transaction t,
SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
- Point position, float cornerRadius, @Nullable Rect immutableClipRect) {
+ Point position, float cornerRadius, @Nullable Rect immutableClipRect,
+ boolean isActivity) {
tmpTransformation.clear();
anim.getTransformation(time, tmpTransformation);
+ if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+ && anim.getExtensionEdges() != 0x0 && isActivity) {
+ t.setEdgeExtensionEffect(leash, anim.getExtensionEdges());
+ }
if (position != null) {
tmpTransformation.getMatrix().postTranslate(position.x, position.y);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index c5dc668..b7b42c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -193,6 +193,8 @@
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishTransaction) {
try {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "OneShot onTransitionConsumed for %s", mRemote);
mRemote.getRemoteTransition().onTransitionConsumed(transition, aborted);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error calling onTransitionConsumed()", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index e196254..1958825 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -325,21 +325,21 @@
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, false /* isActivity */);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, false /* isActivity */);
}
private void buildScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, false /* isActivity */);
}
private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS
index 4417209..3f828f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS
@@ -1 +1,3 @@
jorgegil@google.com
+mattsziklay@google.com
+mdehaini@google.com
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index d1be12f..65e50f8 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -18,3 +18,5 @@
pragyabajoria@google.com
uysalorhan@google.com
gsennton@google.com
+mattsziklay@google.com
+mdehaini@google.com
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS b/libs/WindowManager/Shell/tests/e2e/desktopmode/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/OWNERS
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/Android.bp b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/Android.bp
new file mode 100644
index 0000000..50581f7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsDesktopMode",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellScenariosDesktopMode",
+ "WMShellTestUtils",
+ ],
+ data: ["trace_config/*"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidManifest.xml
similarity index 95%
copy from libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidManifest.xml
index d54b694..1bbbefa 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidManifest.xml
@@ -16,7 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker.service">
+ package="com.android.wm.shell.flicker">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -71,7 +71,7 @@
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker.service"
- android:label="WindowManager Flicker Service Tests">
+ android:targetPackage="com.android.wm.shell.flicker"
+ android:label="WindowManager Shell Flicker Tests">
</instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidTestTemplate.xml
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidTestTemplate.xml
index a66dfb4..40dbbac 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidTestTemplate.xml
@@ -59,13 +59,6 @@
<option name="test-file-name" value="{MODULE}.apk"/>
<option name="test-file-name" value="FlickerTestApp.apk"/>
</target_preparer>
- <!-- Enable mocking GPS location by the test app -->
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
- <option name="teardown-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
- </target_preparer>
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
@@ -97,7 +90,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/res/xml/network_security_config.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
similarity index 80%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
index 5563bb9..b697d80 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,9 +23,9 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
+import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
similarity index 80%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
index 3d16d2219..a11e876 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,9 +23,9 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
+import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
similarity index 98%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 430f80b..8584b59 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.flicker.AssertionInvocationGroup
import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragLandscape.kt
index 9dfafe9..f7b2556 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,8 +23,8 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
+import com.android.wm.shell.scenarios.EnterDesktopWithDrag
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragPortrait.kt
index 1c7d623..f4bf0f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,8 +23,8 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
+import com.android.wm.shell.scenarios.EnterDesktopWithDrag
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
index 6319cf7..45e5fdc 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,8 +23,8 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizePortrait.kt
similarity index 85%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizePortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizePortrait.kt
index 431f6e3..62a2571 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizePortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizePortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.flicker.FlickerConfig
import android.tools.flicker.annotation.ExpectedScenarios
@@ -22,8 +22,8 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizeLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizeLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizeLandscape.kt
index 8d1a530..ea8b10b 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizeLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizeLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,8 +23,8 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizePortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizePortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizePortrait.kt
index 2d81c8c..d7bba6e 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizePortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizePortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,8 +23,8 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/trace_config/trace_config.textproto
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/trace_config/trace_config.textproto
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/Android.bp b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/Android.bp
new file mode 100644
index 0000000..4389f09
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+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"],
+}
+
+java_library {
+ name: "WMShellScenariosDesktopMode",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellTestUtils",
+ "wm-shell-flicker-utils",
+ "androidx.test.ext.junit",
+ "flickertestapplib",
+ "flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
+ "platform-test-annotations",
+ "wm-flicker-common-app-helpers",
+ "launcher-helper-lib",
+ "launcher-aosp-tapl",
+ ],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
similarity index 89%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
index e77a457..e9056f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -28,16 +29,18 @@
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-@Ignore("Base Test Class")
-abstract class CloseAllAppsWithAppHeaderExit
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class CloseAllAppsWithAppHeaderExit
@JvmOverloads
constructor(val rotation: Rotation = Rotation.ROTATION_0) {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowMultiWindow.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
index bbf0ce5..ca1dc1a 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.MailAppHelper
@@ -25,12 +26,13 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-/** Base scenario test for window drag CUJ with multiple windows. */
-@Ignore("Base Test Class")
-abstract class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
{
private val imeAppHelper = ImeAppHelper(instrumentation)
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowScenarioTestBase.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowScenarioTestBase.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowScenarioTestBase.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowScenarioTestBase.kt
index a613ca1..7219287 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowScenarioTestBase.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowScenarioTestBase.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -24,7 +24,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowSingleWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt
similarity index 83%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowSingleWindow.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt
index 0655620..91cfd17 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowSingleWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
-
-/** Base scenario test for window drag CUJ with single window. */
-@Ignore("Base Test Class")
-abstract class DragAppWindowSingleWindow : DragAppWindowScenarioTestBase()
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class DragAppWindowSingleWindow : DragAppWindowScenarioTestBase()
{
private val simpleAppHelper = SimpleAppHelper(instrumentation)
private val testApp = DesktopModeAppHelper(simpleAppHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithAppHandleMenu.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenu.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithAppHandleMenu.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenu.kt
index 47a215a..1073050 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithAppHandleMenu.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenu.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
@@ -27,12 +28,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
-
-/** Base test class for enter desktop with app handle menu CUJ. */
-@Ignore("Base Test Class")
-abstract class EnterDesktopWithAppHandleMenu {
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class EnterDesktopWithAppHandleMenu {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
index fe139d2..0f0d2df 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -26,17 +27,18 @@
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-
-@Ignore("Base Test Class")
-abstract class EnterDesktopWithDrag
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class EnterDesktopWithDrag
@JvmOverloads
constructor(val rotation: Rotation = Rotation.ROTATION_0) {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ExitDesktopWithDragToTopDragZone.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
index 0b6c9af..533be88 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -26,17 +27,18 @@
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-
-@Ignore("Base Test Class")
-abstract class ExitDesktopWithDragToTopDragZone
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ExitDesktopWithDragToTopDragZone
@JvmOverloads
constructor(val rotation: Rotation = Rotation.ROTATION_0) {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MaximizeAppWindow.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
index 20e2167c..e3660fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MaximizeAppWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -26,17 +27,17 @@
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-
-/** Base scenario test for maximize app window CUJ in desktop mode. */
-@Ignore("Base Test Class")
-abstract class MaximizeAppWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class MaximizeAppWindow
{
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
similarity index 91%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
index c847710..b86765e 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
@@ -31,16 +32,17 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
-
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
/**
* Base scenario test for minimizing the least recently used window when a new window is opened
* above the window limit. For tangor devices, which this test currently runs on, the window limit
* is 4.
*/
-@Ignore("Base Test Class")
-abstract class MinimizeWindowOnAppOpen()
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class MinimizeWindowOnAppOpen()
{
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ResizeAppWithCornerResize.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
index 136cf37..63e7387 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ResizeAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -26,17 +27,18 @@
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
-
-@Ignore("Base Test Class")
-abstract class ResizeAppWithCornerResize
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ResizeAppWithCornerResize
@JvmOverloads
constructor(val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 50,
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
index b4cadf4..53e36e23 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.desktopmode.scenarios
+package com.android.wm.shell.scenarios
+import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -26,21 +27,23 @@
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
/**
* Base test for opening recent apps overview from desktop mode.
*
* Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation.
*/
-@Ignore("Base Test Class")
-abstract class SwitchToOverviewFromDesktop
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class SwitchToOverviewFromDesktop
@JvmOverloads
constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) {
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/e2e/splitscreen/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidManifest.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidManifest.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/OWNERS
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
similarity index 73%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
index dad5db9..a9dba4a 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
@@ -17,11 +17,13 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.Presubmit
+import android.tools.NavBar
import android.tools.Rotation
+import android.tools.ScenarioBuilder
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
-import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.SERVICE_TRACE_CONFIG
import android.tools.traces.component.ComponentNameMatcher
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark
@@ -35,7 +37,7 @@
/**
* Test quick switch between two split pairs.
*
- * To run this test: `atest WMShellFlickerTestsSplitScreenGroup2:MultipleShowImeRequestsInSplitScreen`
+ * To run this test: `atest WMShellFlickerTestsSplitScreenGroupOther:MultipleShowImeRequestsInSplitScreen`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -58,10 +60,22 @@
}
companion object {
+ private fun createFlickerTest(
+ navBarMode: NavBar
+ ) = LegacyFlickerTest(ScenarioBuilder()
+ .withStartRotation(Rotation.ROTATION_0)
+ .withEndRotation(Rotation.ROTATION_0)
+ .withNavBarMode(navBarMode), resultReaderProvider = { scenario ->
+ android.tools.flicker.datastore.CachedResultReader(
+ scenario, SERVICE_TRACE_CONFIG
+ )
+ })
+
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
+ fun getParams() = listOf(
+ createFlickerTest(NavBar.MODE_GESTURAL),
+ createFlickerTest(NavBar.MODE_3BUTTON)
)
}
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
similarity index 98%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index d349988..3018b56 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.splitscreen
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.NavBar
import android.tools.flicker.junit.FlickerParametersRunnerFactory
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/trace_config/trace_config.textproto
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/trace_config/trace_config.textproto
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/Android.bp
new file mode 100644
index 0000000..dd0018a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "WMShellFlickerServiceTests",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellScenariosSplitScreen",
+ "WMShellTestUtils",
+ ],
+ data: [
+ ":FlickerTestApp",
+ "trace_config/*",
+ ],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidManifest.xml
similarity index 97%
rename from libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidManifest.xml
index d54b694..662e7f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidManifest.xml
@@ -16,7 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker.service">
+ package="com.android.wm.shell">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -71,7 +71,7 @@
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker.service"
+ android:targetPackage="com.android.wm.shell"
android:label="WindowManager Flicker Service Tests">
</instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
similarity index 74%
copy from libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
index a66dfb4..6c903a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+<configuration description="WMShell Platinum Tests {MODULE}">
<option name="test-tag" value="FlickerTests"/>
<!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
<option name="isolated-storage" value="false"/>
@@ -59,37 +59,15 @@
<option name="test-file-name" value="{MODULE}.apk"/>
<option name="test-file-name" value="FlickerTestApp.apk"/>
</target_preparer>
- <!-- Enable mocking GPS location by the test app -->
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
- <option name="teardown-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
- </target_preparer>
-
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="push-file"
- key="trace_config.textproto"
- value="/data/misc/perfetto-traces/trace_config.textproto"
- />
- <!--Install the content provider automatically when we push some file in sdcard folder.-->
- <!--Needed to avoid the installation during the test suite.-->
- <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="{PACKAGE}"/>
<option name="shell-timeout" value="6600s"/>
<option name="test-timeout" value="6000s"/>
<option name="hidden-api-checks" value="false"/>
- <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
- <!-- PerfettoListener related arguments -->
- <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
- <option name="instrumentation-arg"
- key="perfetto_config_file"
- value="trace_config.textproto"
- />
+ <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
+ <!-- DefaultUITraceListener args -->
<option name="instrumentation-arg" key="per_run" value="true"/>
<option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
@@ -97,7 +75,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ value="/data/user/0/com.android.wm.shell/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavLandscape.kt
index 1684a26..3cb9cf2 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import com.android.wm.shell.scenarios.CopyContentInSplit
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavPortrait.kt
index 3b5fad6..b27a8ae 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import com.android.wm.shell.scenarios.CopyContentInSplit
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
index 2b8a903..9388114 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
index b284fe1..30ef492 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index a400ee4..059f967 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 7f5ee4c..0c6d546 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavLandscape.kt
index 1b075c4..14fb72b 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import com.android.wm.shell.scenarios.DragDividerToResize
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavPortrait.kt
index 6ca3737..9be61a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import com.android.wm.shell.scenarios.DragDividerToResize
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index f7d231f0..c12d199 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index ab819fa..11cd38a 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index a6b732c..66d4bfa 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 07e5f4b..f3a11eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index 2725694..327ecc3 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 58cc4d7..dd5a395 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 85897a1..8e7cf30 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index 891b6df..0324dac 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index 7983652..2fa141e 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 1bdea66..0176913 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index bab0c0a..1db28dc 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 17a59ab..c69167b 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index 2c36d64..602283a 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index 6e91d04..7cc14e0 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index a921b46..daf6547 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index 05f8912..b0f5e65 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index 1ae1f53..88fa783 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index e14ca55..aa36f44 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index ce0c4c4..292f413 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index 5a8d2d5..865958f 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.Rotation
import android.tools.flicker.FlickerConfig
@@ -23,7 +23,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index d442615..6c36e84 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.flicker.FlickerConfig
import android.tools.flicker.annotation.ExpectedScenarios
@@ -22,7 +22,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index ddc8a06..61ccd36 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.flicker
+package com.android.wm.shell.flicker
import android.tools.flicker.FlickerConfig
import android.tools.flicker.annotation.ExpectedScenarios
@@ -22,7 +22,7 @@
import android.tools.flicker.config.FlickerConfig
import android.tools.flicker.config.FlickerServiceConfig
import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/service/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/Android.bp
similarity index 67%
rename from libs/WindowManager/Shell/tests/flicker/service/Android.bp
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/Android.bp
index a5bc261..90210b1 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/Android.bp
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/Android.bp
@@ -26,9 +26,7 @@
filegroup {
name: "WMShellFlickerServicePlatinumTests-src",
srcs: [
- "src/**/platinum/*.kt",
- "src/**/scenarios/*.kt",
- "src/**/common/*.kt",
+ "src/**/*.kt",
],
}
@@ -43,33 +41,42 @@
],
static_libs: [
"wm-shell-flicker-utils",
+ "WMShellScenariosSplitScreen",
],
}
android_test {
- name: "WMShellFlickerServiceTests",
- defaults: ["WMShellFlickerTestsDefault"],
- manifest: "AndroidManifest.xml",
- package_name: "com.android.wm.shell.flicker.service",
- instrumentation_target_package: "com.android.wm.shell.flicker.service",
- test_config_template: "AndroidTestTemplate.xml",
- srcs: ["src/**/*.kt"],
- static_libs: ["WMShellFlickerTestsBase"],
- data: ["trace_config/*"],
-}
-
-android_test {
name: "WMShellFlickerServicePlatinumTests",
- defaults: ["WMShellFlickerTestsDefault"],
+ platform_apis: true,
+ certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
manifest: "AndroidManifest.xml",
- package_name: "com.android.wm.shell.flicker.service",
- instrumentation_target_package: "com.android.wm.shell.flicker.service",
test_config_template: "AndroidTestTemplate.xml",
test_suites: [
"device-tests",
"device-platinum-tests",
],
srcs: [":WMShellFlickerServicePlatinumTests-src"],
- static_libs: ["WMShellFlickerTestsBase"],
- data: ["trace_config/*"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellScenariosSplitScreen",
+ "WMShellTestUtils",
+ "ui-trace-collector",
+ "collector-device-lib",
+ "wm-shell-flicker-utils",
+ "androidx.test.ext.junit",
+ "flickertestapplib",
+ "flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
+ "platform-test-annotations",
+ "wm-flicker-common-app-helpers",
+ "launcher-helper-lib",
+ "launcher-aosp-tapl",
+ ],
+ data: [
+ ":FlickerTestApp",
+ "trace_config/*",
+ ],
}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidManifest.xml
similarity index 97%
copy from libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidManifest.xml
index d54b694..662e7f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidManifest.xml
@@ -16,7 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker.service">
+ package="com.android.wm.shell">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -71,7 +71,7 @@
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker.service"
+ android:targetPackage="com.android.wm.shell"
android:label="WindowManager Flicker Service Tests">
</instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
similarity index 74%
copy from libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
index a66dfb4..6c903a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+<configuration description="WMShell Platinum Tests {MODULE}">
<option name="test-tag" value="FlickerTests"/>
<!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
<option name="isolated-storage" value="false"/>
@@ -59,37 +59,15 @@
<option name="test-file-name" value="{MODULE}.apk"/>
<option name="test-file-name" value="FlickerTestApp.apk"/>
</target_preparer>
- <!-- Enable mocking GPS location by the test app -->
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
- <option name="teardown-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
- </target_preparer>
-
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="push-file"
- key="trace_config.textproto"
- value="/data/misc/perfetto-traces/trace_config.textproto"
- />
- <!--Install the content provider automatically when we push some file in sdcard folder.-->
- <!--Needed to avoid the installation during the test suite.-->
- <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="{PACKAGE}"/>
<option name="shell-timeout" value="6600s"/>
<option name="test-timeout" value="6000s"/>
<option name="hidden-api-checks" value="false"/>
- <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
- <!-- PerfettoListener related arguments -->
- <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
- <option name="instrumentation-arg"
- key="perfetto_config_file"
- value="trace_config.textproto"
- />
+ <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
+ <!-- DefaultUITraceListener args -->
<option name="instrumentation-arg" key="per_run" value="true"/>
<option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
@@ -97,7 +75,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ value="/data/user/0/com.android.wm.shell/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavLandscape.kt
index 64293b2..4c2ca67 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import com.android.wm.shell.scenarios.CopyContentInSplit
import org.junit.Test
open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavPortrait.kt
index 517ba2d..0cca310 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import com.android.wm.shell.scenarios.CopyContentInSplit
import org.junit.Test
open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavLandscape.kt
index 1bafe3b..7aa62cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavPortrait.kt
index fd0100f..de11fc6 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 850b3d8..daa6aac 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 0b752bf..ff57d00 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavLandscape.kt
index 3c52aa7..0ac19c8 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import com.android.wm.shell.scenarios.DragDividerToResize
import org.junit.Test
open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavPortrait.kt
index c2e21b8..5713602e 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import com.android.wm.shell.scenarios.DragDividerToResize
import org.junit.Test
open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index bf85ab4..d7333f1a 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 0ac4ca2..e29a140 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index 80bd088..9ccccb1 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 0dffb4a..87a4d08 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index b721f2f..559652c 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 22cbc77..bcb8e0c 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index ac0f9e2..39e0fed 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index f7a229d..6431629 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index 6dbbcb0..2093424 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index bd69ea9..f89259d 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 404b96f..e5aff0c 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index a79687d..defade9 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index b52eb4c..e28deca 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index d79620c..99fb06c 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index d27bfa1..7045e66 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index 3c7d4d4..b2da052 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index 26a2034..04d7f62 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 5154b35..bc36fb7 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index 86451c5..ceda4da 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavLandscape :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index baf72b4..365c5cc 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavPortrait :
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 9caab9b..a866297 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index bf484e5..6d59001 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.platinum
+package com.android.wm.shell
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/Android.bp
new file mode 100644
index 0000000..60c7de7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+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"],
+}
+
+java_library {
+ name: "WMShellScenariosSplitScreen",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellTestUtils",
+ "wm-shell-flicker-utils",
+ "androidx.test.ext.junit",
+ "flickertestapplib",
+ "flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
+ "platform-test-annotations",
+ "wm-flicker-common-app-helpers",
+ "wm-flicker-common-assertions",
+ "launcher-helper-lib",
+ "launcher-aosp-tapl",
+ ],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
index 6171074..ba46542 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
index c1a8ee7..d774a31 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
index 600855a..5aa1619 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
index b5a6d83..668f367 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromAllApps.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromAllApps.kt
index a189325..06c7b9b 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -24,7 +24,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromNotification.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromNotification.kt
index bcd0f12..96b22bf 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -25,7 +25,7 @@
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.helpers.MultiWindowUtils
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromShortcut.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromShortcut.kt
index 3f07be0..9e05b63 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -24,7 +24,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromTaskbar.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromTaskbar.kt
index 5328013..9090055 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenFromOverview.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenFromOverview.kt
index be4035d..d5cc92e 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenFromOverview.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
similarity index 96%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
index 2406bde..26203d4 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.graphics.Point
@@ -25,7 +25,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
index de26982..2ccffa85 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
index 873b019..8673c46 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
index 15934d0..c7cbc3e 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
index 79e69ae..4ded148 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/UnlockKeyguardToSplitScreen.kt
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/UnlockKeyguardToSplitScreen.kt
index 0f932d4..7b062fcc 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
+package com.android.wm.shell.scenarios
import android.app.Instrumentation
import android.tools.NavBar
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.common.Utils
+import com.android.wm.shell.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/Android.bp b/libs/WindowManager/Shell/tests/e2e/utils/Android.bp
new file mode 100644
index 0000000..51d9c40
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+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_team: "trendy_team_windowing_tools",
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "WMShellTestUtils",
+ srcs: ["src/**/*.kt"],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
similarity index 97%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
rename to libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
index 4c6c6cc..dee67f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.common
+package com.android.wm.shell
import android.app.Instrumentation
import android.platform.test.rule.NavigationModeRule
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 2ee53f4..d7ea9f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -100,14 +100,14 @@
@Postsubmit
@Test
fun navBarLayerIsVisibleAtEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerIsVisibleAtEnd()
}
@Postsubmit
@Test
fun navBarLayerPositionAtEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerPositionAtEnd()
}
@@ -154,7 +154,7 @@
@Postsubmit
@Test
fun taskBarLayerIsVisibleAtEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index a5e0550..3ffc9d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -21,6 +21,7 @@
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.traces.component.ComponentNameMatcher
+import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.ClosePipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -60,7 +61,7 @@
val pipCenterY = pipRegion.centerY()
val displayCenterX = device.displayWidth / 2
val barComponent =
- if (flicker.scenario.isTablet) {
+ if (flicker.scenario.isTablet || Flags.enableTaskbarOnPhones()) {
ComponentNameMatcher.TASK_BAR
} else {
ComponentNameMatcher.NAV_BAR
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 3a0eeb6..68fa7c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -103,7 +103,7 @@
@Postsubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
// Netflix starts in immersive fullscreen mode, so taskbar bar is not visible at start
flicker.assertLayersStart { this.isInvisible(ComponentNameMatcher.TASK_BAR) }
flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
index 5c539a5..72be3d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
@@ -88,7 +88,7 @@
@Postsubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
// YouTube starts in immersive fullscreen mode, so taskbar bar is not visible at start
flicker.assertLayersStart { this.isInvisible(ComponentNameMatcher.TASK_BAR) }
flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/CloseAllAppsWithAppHeaderExitTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/CloseAllAppsWithAppHeaderExitTest.kt
deleted file mode 100644
index 9ba3a45..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/CloseAllAppsWithAppHeaderExitTest.kt
+++ /dev/null
@@ -1,34 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [CloseAllAppsWithAppHeaderExit]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class CloseAllAppsWithAppHeaderExitTest : CloseAllAppsWithAppHeaderExit() {
-
- @Test
- override fun closeAllAppsInDesktop() {
- super.closeAllAppsInDesktop()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowMultiWindowTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowMultiWindowTest.kt
deleted file mode 100644
index ed1d488..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowMultiWindowTest.kt
+++ /dev/null
@@ -1,34 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.DragAppWindowMultiWindow
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [DragAppWindowMultiWindow]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class DragAppWindowMultiWindowTest : DragAppWindowMultiWindow()
-{
- @Test
- override fun dragAppWindow() {
- super.dragAppWindow()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowSingleWindowTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowSingleWindowTest.kt
deleted file mode 100644
index d8b9348..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowSingleWindowTest.kt
+++ /dev/null
@@ -1,34 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.DragAppWindowSingleWindow
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [DragAppWindowSingleWindow]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class DragAppWindowSingleWindowTest : DragAppWindowSingleWindow()
-{
- @Test
- override fun dragAppWindow() {
- super.dragAppWindow()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithAppHandleMenuTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithAppHandleMenuTest.kt
deleted file mode 100644
index 546ce2d..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithAppHandleMenuTest.kt
+++ /dev/null
@@ -1,33 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithAppHandleMenu
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [EnterDesktopWithAppHandleMenu]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class EnterDesktopWithAppHandleMenuTest : EnterDesktopWithAppHandleMenu() {
- @Test
- override fun enterDesktopWithAppHandleMenu() {
- super.enterDesktopWithAppHandleMenu()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithDragTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithDragTest.kt
deleted file mode 100644
index b5fdb16..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithDragTest.kt
+++ /dev/null
@@ -1,35 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [EnterDesktopWithDrag]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class EnterDesktopWithDragTest : EnterDesktopWithDrag(Rotation.ROTATION_0) {
-
- @Test
- override fun enterDesktopWithDrag() {
- super.enterDesktopWithDrag()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ExitDesktopWithDragToTopDragZoneTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ExitDesktopWithDragToTopDragZoneTest.kt
deleted file mode 100644
index 8f802d2..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ExitDesktopWithDragToTopDragZoneTest.kt
+++ /dev/null
@@ -1,35 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ExitDesktopWithDragToTopDragZone
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [ExitDesktopWithDragToTopDragZone]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ExitDesktopWithDragToTopDragZoneTest :
- ExitDesktopWithDragToTopDragZone(Rotation.ROTATION_0) {
- @Test
- override fun exitDesktopWithDragToTopDragZone() {
- super.exitDesktopWithDragToTopDragZone()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MaximizeAppWindowTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MaximizeAppWindowTest.kt
deleted file mode 100644
index f899082..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MaximizeAppWindowTest.kt
+++ /dev/null
@@ -1,34 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.MaximizeAppWindow
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [MaximizeAppWindow]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class MaximizeAppWindowTest : MaximizeAppWindow()
-{
- @Test
- override fun maximizeAppWindow() {
- super.maximizeAppWindow()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MinimizeWindowOnAppOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MinimizeWindowOnAppOpenTest.kt
deleted file mode 100644
index 63c428a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MinimizeWindowOnAppOpenTest.kt
+++ /dev/null
@@ -1,36 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.MinimizeWindowOnAppOpen
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [MinimizeWindowOnAppOpen]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class MinimizeWindowOnAppOpenTest : MinimizeWindowOnAppOpen()
-{
- @Test
- override fun openAppToMinimizeWindow() {
- // Launch a new app while 4 apps are already open on desktop. This should result in the
- // first app we opened to be minimized.
- super.openAppToMinimizeWindow()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ResizeAppWithCornerResizeTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ResizeAppWithCornerResizeTest.kt
deleted file mode 100644
index 4797aaf..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ResizeAppWithCornerResizeTest.kt
+++ /dev/null
@@ -1,34 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [ResizeAppWithCornerResize]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppWithCornerResizeTest : ResizeAppWithCornerResize(Rotation.ROTATION_0) {
- @Test
- override fun resizeAppWithCornerResize() {
- super.resizeAppWithCornerResize()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/SwitchToOverviewFromDesktopTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/SwitchToOverviewFromDesktopTest.kt
deleted file mode 100644
index 9a71361..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/SwitchToOverviewFromDesktopTest.kt
+++ /dev/null
@@ -1,33 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.SwitchToOverviewFromDesktop
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [SwitchToOverviewFromDesktop]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SwitchToOverviewFromDesktopTest : SwitchToOverviewFromDesktop() {
- @Test
- override fun switchToOverview() {
- super.switchToOverview()
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
deleted file mode 100644
index 3ab6a1e..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Split Screen
-# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
index 4465a16..acaf021 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
@@ -28,12 +28,16 @@
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
+import com.android.wm.shell.Flags
import org.junit.Assume
import org.junit.Test
interface ICommonAssertions {
val flicker: LegacyFlickerTest
+ val usesTaskbar: Boolean
+ get() = flicker.scenario.isTablet || Flags.enableTaskbarOnPhones()
+
/** Checks that all parts of the screen are covered during the transition */
@Presubmit @Test fun entireScreenCovered() = flicker.entireScreenCovered()
@@ -43,7 +47,7 @@
@Presubmit
@Test
fun navBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerIsVisibleAtStartAndEnd()
}
@@ -54,7 +58,7 @@
@Presubmit
@Test
fun navBarLayerPositionAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerPositionAtStartAndEnd()
}
@@ -66,7 +70,7 @@
@Presubmit
@Test
fun navBarWindowIsAlwaysVisible() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarWindowIsAlwaysVisible()
}
@@ -76,7 +80,7 @@
@Presubmit
@Test
fun taskBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.taskBarLayerIsVisibleAtStartAndEnd()
}
@@ -88,7 +92,7 @@
@Presubmit
@Test
fun taskBarWindowIsAlwaysVisible() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.taskBarWindowIsAlwaysVisible()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index a040865..4d761e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -44,6 +44,8 @@
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
+ "androidx.datastore_datastore",
+ "kotlinx_coroutines_test",
"androidx.dynamicanimation_dynamicanimation",
"dagger2",
"frameworks-base-testutils",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
new file mode 100644
index 0000000..4d40738
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.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.wm.shell.desktopmode.education
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.dataStoreFile
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
+class AppHandleEducationDatastoreRepositoryTest {
+ private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
+ private lateinit var testDatastore: DataStore<WindowingEducationProto>
+ private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+ private lateinit var datastoreScope: CoroutineScope
+
+ @Before
+ fun setUp() {
+ Dispatchers.setMain(StandardTestDispatcher())
+ datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+ testDatastore =
+ DataStoreFactory.create(
+ serializer =
+ AppHandleEducationDatastoreRepository.Companion.WindowingEducationProtoSerializer,
+ scope = datastoreScope) {
+ testContext.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE)
+ }
+ datastoreRepository = AppHandleEducationDatastoreRepository(testDatastore)
+ }
+
+ @After
+ fun tearDown() {
+ File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
+ .deleteRecursively()
+
+ datastoreScope.cancel()
+ }
+
+ @Test
+ fun getWindowingEducationProto_returnsCorrectProto() =
+ runTest(StandardTestDispatcher()) {
+ val windowingEducationProto =
+ createWindowingEducationProto(
+ educationViewedTimestampMillis = 123L,
+ featureUsedTimestampMillis = 124L,
+ appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+ appUsageStatsLastUpdateTimestampMillis = 125L)
+ testDatastore.updateData { windowingEducationProto }
+
+ val resultProto = datastoreRepository.windowingEducationProto()
+
+ assertThat(resultProto).isEqualTo(windowingEducationProto)
+ }
+
+ private fun createWindowingEducationProto(
+ educationViewedTimestampMillis: Long? = null,
+ featureUsedTimestampMillis: Long? = null,
+ appUsageStats: Map<String, Int>? = null,
+ appUsageStatsLastUpdateTimestampMillis: Long? = null
+ ): WindowingEducationProto =
+ WindowingEducationProto.newBuilder()
+ .apply {
+ if (educationViewedTimestampMillis != null)
+ setEducationViewedTimestampMillis(educationViewedTimestampMillis)
+ if (featureUsedTimestampMillis != null)
+ setFeatureUsedTimestampMillis(featureUsedTimestampMillis)
+ setAppHandleEducation(
+ createAppHandleEducationProto(
+ appUsageStats, appUsageStatsLastUpdateTimestampMillis))
+ }
+ .build()
+
+ private fun createAppHandleEducationProto(
+ appUsageStats: Map<String, Int>? = null,
+ appUsageStatsLastUpdateTimestampMillis: Long? = null
+ ): WindowingEducationProto.AppHandleEducation =
+ WindowingEducationProto.AppHandleEducation.newBuilder()
+ .apply {
+ if (appUsageStats != null) putAllAppUsageStats(appUsageStats)
+ if (appUsageStatsLastUpdateTimestampMillis != null)
+ setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestampMillis)
+ }
+ .build()
+
+ companion object {
+ private const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
+ private const val APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE = "app_handle_education_test.pb"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 22b408c..1990fe7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -219,7 +219,7 @@
@Test
@UiThreadTest
- public void testRemoteTransitionConsumed() {
+ public void testRemoteTransitionConsumedForStartAnimation() {
// Omit side child change
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
.addChange(TRANSIT_OPEN, mMainChild)
@@ -238,7 +238,30 @@
assertTrue(accepted);
assertTrue(testRemote.isConsumed());
+ }
+ @Test
+ @UiThreadTest
+ public void testRemoteTransitionConsumed() {
+ // Omit side child change
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(TRANSIT_OPEN, mMainChild)
+ .build();
+ TestRemoteTransition testRemote = new TestRemoteTransition();
+
+ IBinder transition = mSplitScreenTransitions.startEnterTransition(
+ TRANSIT_OPEN, new WindowContainerTransaction(),
+ new RemoteTransition(testRemote, "Test"), mStageCoordinator,
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ mMainStage.onTaskAppeared(mMainChild, createMockSurface());
+ mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ mStageCoordinator.onTransitionConsumed(transition, false /*aborted*/,
+ mock(SurfaceControl.Transaction.class));
+
+ assertTrue(testRemote.isConsumed());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 6d68797..fa905e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -57,6 +57,7 @@
import android.view.View
import android.view.WindowInsets.Type.statusBars
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -356,6 +357,36 @@
}
@Test
+ fun testCloseButtonInFreeform() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val windowDecor = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+ val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
+ verify(windowDecor).setCaptionListeners(
+ onClickListenerCaptor.capture(), any(), any(), any())
+
+ val onClickListener = onClickListenerCaptor.firstValue
+ val view = mock(View::class.java)
+ whenever(view.id).thenReturn(R.id.close_window)
+
+ val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java)
+ desktopModeWindowDecorViewModel
+ .setFreeformTaskTransitionStarter(freeformTaskTransitionStarter)
+
+ onClickListener.onClick(view)
+
+ val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
+ verify(freeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture())
+ val wct = transactionCaptor.firstValue
+
+ assertEquals(1, wct.getHierarchyOps().size)
+ assertEquals(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK,
+ wct.getHierarchyOps().get(0).getType())
+ assertEquals(task.token.asBinder(), wct.getHierarchyOps().get(0).getContainer())
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() {
val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
diff --git a/libs/androidfw/BigBuffer.cpp b/libs/androidfw/BigBuffer.cpp
index bedfc49..43b56c3 100644
--- a/libs/androidfw/BigBuffer.cpp
+++ b/libs/androidfw/BigBuffer.cpp
@@ -17,8 +17,8 @@
#include <androidfw/BigBuffer.h>
#include <algorithm>
+#include <iterator>
#include <memory>
-#include <vector>
#include "android-base/logging.h"
@@ -78,10 +78,27 @@
std::string BigBuffer::to_string() const {
std::string result;
+ result.reserve(size_);
for (const Block& block : blocks_) {
result.append(block.buffer.get(), block.buffer.get() + block.size);
}
return result;
}
+void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
+ std::move(buffer.blocks_.begin(), buffer.blocks_.end(), std::back_inserter(blocks_));
+ size_ += buffer.size_;
+ buffer.blocks_.clear();
+ buffer.size_ = 0;
+}
+
+void BigBuffer::BackUp(size_t count) {
+ Block& block = blocks_.back();
+ block.size -= count;
+ size_ -= count;
+ // BigBuffer is supposed to always give zeroed memory, but backing up usually means
+ // something has been already written into the block. Erase it.
+ std::fill_n(block.buffer.get() + block.size, count, 0);
+}
+
} // namespace android
diff --git a/libs/androidfw/include/androidfw/BigBuffer.h b/libs/androidfw/include/androidfw/BigBuffer.h
index b99a4ed..c4cd7c5 100644
--- a/libs/androidfw/include/androidfw/BigBuffer.h
+++ b/libs/androidfw/include/androidfw/BigBuffer.h
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-#ifndef _ANDROID_BIG_BUFFER_H
-#define _ANDROID_BIG_BUFFER_H
+#pragma once
-#include <cstring>
#include <memory>
#include <string>
#include <type_traits>
+#include <utility>
#include <vector>
#include "android-base/logging.h"
@@ -150,24 +149,11 @@
template <typename T>
inline T* BigBuffer::NextBlock(size_t count) {
- static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
+ static_assert(std::is_standard_layout_v<T>, "T must be standard_layout type");
CHECK(count != 0);
return reinterpret_cast<T*>(NextBlockImpl(sizeof(T) * count));
}
-inline void BigBuffer::BackUp(size_t count) {
- Block& block = blocks_.back();
- block.size -= count;
- size_ -= count;
-}
-
-inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
- std::move(buffer.blocks_.begin(), buffer.blocks_.end(), std::back_inserter(blocks_));
- size_ += buffer.size_;
- buffer.blocks_.clear();
- buffer.size_ = 0;
-}
-
inline void BigBuffer::Pad(size_t bytes) {
NextBlock<char>(bytes);
}
@@ -188,5 +174,3 @@
}
} // namespace android
-
-#endif // _ANDROID_BIG_BUFFER_H
diff --git a/libs/androidfw/tests/BigBuffer_test.cpp b/libs/androidfw/tests/BigBuffer_test.cpp
index 382d21e..7e38f17 100644
--- a/libs/androidfw/tests/BigBuffer_test.cpp
+++ b/libs/androidfw/tests/BigBuffer_test.cpp
@@ -98,4 +98,20 @@
ASSERT_EQ(8u, buffer.size());
}
+TEST(BigBufferTest, BackUpZeroed) {
+ BigBuffer buffer(16);
+
+ auto block = buffer.NextBlock<char>(2);
+ ASSERT_TRUE(block != nullptr);
+ ASSERT_EQ(2u, buffer.size());
+ block[0] = 0x01;
+ block[1] = 0x02;
+ buffer.BackUp(1);
+ ASSERT_EQ(1u, buffer.size());
+ auto new_block = buffer.NextBlock<char>(1);
+ ASSERT_TRUE(new_block != nullptr);
+ ASSERT_EQ(2u, buffer.size());
+ ASSERT_EQ(0, *new_block);
+}
+
} // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d71f3b6..23cd3ce 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -143,6 +143,7 @@
"aconfig_text_flags_c_lib",
"server_configurable_flags",
"libaconfig_storage_read_api_cc",
+ "libgraphicsenv",
],
static_libs: [
"libEGL_blobCache",
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 1217b47..b6476c9 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -42,6 +42,11 @@
constexpr bool initialize_gl_always() {
return false;
}
+
+constexpr bool skip_eglmanager_telemetry() {
+ return false;
+}
+
constexpr bool resample_gainmap_regions() {
return false;
}
@@ -103,6 +108,7 @@
bool Properties::clipSurfaceViews = false;
bool Properties::hdr10bitPlus = false;
+bool Properties::skipTelemetry = false;
bool Properties::resampleGainmapRegions = false;
int Properties::timeoutMultiplier = 1;
@@ -183,6 +189,8 @@
hwui_flags::resample_gainmap_regions());
timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+ skipTelemetry = base::GetBoolProperty(PROPERTY_SKIP_EGLMANAGER_TELEMETRY,
+ hwui_flags::skip_eglmanager_telemetry());
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 73e80ce..db47152 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -234,6 +234,8 @@
*/
#define PROPERTY_INITIALIZE_GL_ALWAYS "debug.hwui.initialize_gl_always"
+#define PROPERTY_SKIP_EGLMANAGER_TELEMETRY "debug.hwui.skip_eglmanager_telemetry"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -342,6 +344,7 @@
static bool clipSurfaceViews;
static bool hdr10bitPlus;
+ static bool skipTelemetry;
static bool resampleGainmapRegions;
static int timeoutMultiplier;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 13c0b00..a1f5168 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -99,6 +99,13 @@
}
flag {
+ name: "skip_eglmanager_telemetry"
+ namespace: "core_graphics"
+ description: "Skip telemetry in EglManager's calls to eglCreateContext to avoid polluting telemetry"
+ bug: "347911216"
+}
+
+flag {
name: "resample_gainmap_regions"
namespace: "core_graphics"
description: "Resample gainmaps when decoding regions, to improve visual quality"
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 708b011..6010445 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -19,6 +19,7 @@
#include <EGL/eglext.h>
#include <GLES/gl.h>
#include <cutils/properties.h>
+#include <graphicsenv/GpuStatsInfo.h>
#include <log/log.h>
#include <sync/sync.h>
#include <utils/Trace.h>
@@ -366,6 +367,10 @@
contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
contextAttributes.push_back(Properties::contextPriority);
}
+ if (Properties::skipTelemetry) {
+ contextAttributes.push_back(EGL_TELEMETRY_HINT_ANDROID);
+ contextAttributes.push_back(android::GpuStatsInfo::SKIP_TELEMETRY);
+ }
contextAttributes.push_back(EGL_NONE);
mEglContext = eglCreateContext(
mEglDisplay, EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig,
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index f28c2c1..2c71ee0 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -288,6 +288,7 @@
private static int sTunerVersion = TunerVersionChecker.TUNER_VERSION_UNKNOWN;
private DemuxInfo mDesiredDemuxInfo = new DemuxInfo(Filter.TYPE_UNDEFINED);
+ private boolean mClosed = false;
private Frontend mFrontend;
private EventHandler mHandler;
@Nullable
@@ -813,6 +814,9 @@
*/
@Override
public void close() {
+ if (mClosed) {
+ return;
+ }
acquireTRMSLock("close()");
try {
releaseAll();
@@ -820,6 +824,7 @@
TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner");
} finally {
releaseTRMSLock();
+ mClosed = true;
}
}
@@ -888,19 +893,14 @@
try {
if (mFrontendCiCamHandle != null) {
if (DEBUG) {
- Log.d(TAG, "unlinking CiCam : " + mFrontendCiCamHandle + " for " + mClientId);
+ Log.d(TAG, "releasing CiCam : " + mFrontendCiCamHandle + " for " + mClientId);
}
- int result = nativeUnlinkCiCam(mFrontendCiCamId);
- if (result == RESULT_SUCCESS) {
- mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
- replicateCiCamSettings(null);
- } else {
- Log.e(TAG, "nativeUnlinkCiCam(" + mFrontendCiCamHandle + ") for mClientId:"
- + mClientId + "failed with result:" + result);
- }
+ nativeUnlinkCiCam(mFrontendCiCamId);
+ mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
+ replicateCiCamSettings(null);
} else {
if (DEBUG) {
- Log.d(TAG, "NOT unlinking CiCam : " + mClientId);
+ Log.d(TAG, "NOT releasing CiCam : " + mClientId);
}
}
} finally {
@@ -1665,11 +1665,9 @@
if (mFrontendCiCamHandle != null && mFrontendCiCamId != null
&& mFrontendCiCamId == ciCamId) {
int result = nativeUnlinkCiCam(ciCamId);
- if (result == RESULT_SUCCESS) {
- mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
- mFrontendCiCamId = null;
- mFrontendCiCamHandle = null;
- }
+ mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
+ mFrontendCiCamId = null;
+ mFrontendCiCamHandle = null;
return result;
}
}
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index cf7aea4..b0d1f71 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -232,6 +232,8 @@
method public final void notifyUnhandled();
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
+ method @FlaggedApi("android.nfc.nfc_event_listener") public void onObserveModeStateChanged(boolean);
+ method @FlaggedApi("android.nfc.nfc_event_listener") public void onPreferredServiceChanged(boolean);
method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>);
method public final void sendResponseApdu(byte[]);
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index c3c74a6..cd8e19c 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -242,6 +242,16 @@
/**
* @hide
*/
+ public static final int MSG_OBSERVE_MODE_CHANGE = 5;
+
+ /**
+ * @hide
+ */
+ public static final int MSG_PREFERRED_SERVICE_CHANGED = 6;
+
+ /**
+ * @hide
+ */
public static final String KEY_DATA = "data";
/**
@@ -333,7 +343,17 @@
processPollingFrames(pollingFrames);
}
break;
- default:
+ case MSG_OBSERVE_MODE_CHANGE:
+ if (android.nfc.Flags.nfcEventListener()) {
+ onObserveModeStateChanged(msg.arg1 == 1);
+ }
+ break;
+ case MSG_PREFERRED_SERVICE_CHANGED:
+ if (android.nfc.Flags.nfcEventListener()) {
+ onPreferredServiceChanged(msg.arg1 == 1);
+ }
+ break;
+ default:
super.handleMessage(msg);
}
}
@@ -441,4 +461,26 @@
* @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED}
*/
public abstract void onDeactivated(int reason);
+
+
+ /**
+ * This method is called when this service is the preferred Nfc service and
+ * Observe mode has been enabled or disabled.
+ *
+ * @param isEnabled true if observe mode has been enabled, false if it has been disabled
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+ public void onObserveModeStateChanged(boolean isEnabled) {
+
+ }
+
+ /**
+ * This method is called when this service gains or loses preferred Nfc service status.
+ *
+ * @param isPreferred true is this service has become the preferred Nfc service,
+ * false if it is no longer the preferred service
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+ public void onPreferredServiceChanged(boolean isPreferred) {
+ }
}
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index f16fa80..5819b98 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -2,6 +2,14 @@
container: "system"
flag {
+ name: "nfc_event_listener"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable NFC Event listener APIs"
+ bug: "356447790"
+}
+
+flag {
name: "enable_nfc_mainline"
is_exported: true
namespace: "nfc"
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index b3fb1e7..ff83610 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -28,6 +28,17 @@
},
{
"name": "CtsIntentSignatureTestCases"
+ },
+ {
+ "name": "CtsPackageInstallerCUJTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
}
]
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 9ad3e3c..170cb45 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -91,8 +91,7 @@
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null);
- // TODO(b/318521110) Enable PIA v2 for archive dialog.
- if (usePiaV2() && !isTv() && !isArchiveDialog(getIntent())) {
+ if (usePiaV2() && !isTv()) {
Log.i(TAG, "Using Pia V2");
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
@@ -225,11 +224,6 @@
showConfirmationDialog();
}
- private boolean isArchiveDialog(Intent intent) {
- return (intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0)
- & PackageManager.DELETE_ARCHIVE) != 0;
- }
-
/**
* Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent}
* to archive an app if requested.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 186b69b..3b0faf0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -736,7 +736,8 @@
val appInfo = packageManager.getApplicationInfo(
pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES
)
- if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
+ // If the package is archived, treat it as an update case.
+ if (!appInfo.isArchived && appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
return false
}
} catch (e: PackageManager.NameNotFoundException) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
index 0091a3e8..96525f6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -32,6 +32,8 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
+import android.content.pm.PackageManager.PackageInfoFlags
import android.content.pm.VersionedPackage
import android.graphics.drawable.Icon
import android.os.Build
@@ -51,6 +53,9 @@
import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
import com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame
+import com.android.packageinstaller.v2.model.UninstallAborted.Companion.ABORT_REASON_UNINSTALL_DONE
+import android.content.pm.Flags as PmFlags
+import android.multiuser.Flags as MultiuserFlags
class UninstallRepository(private val context: Context) {
@@ -71,6 +76,7 @@
private var uninstallFromAllUsers = false
private var isClonedApp = false
private var uninstallId = 0
+ private var deleteFlags = 0
fun performPreUninstallChecks(intent: Intent, callerInfo: CallerInfo): UninstallStage {
this.intent = intent
@@ -155,7 +161,9 @@
try {
targetAppInfo = packageManager.getApplicationInfo(
targetPackageName!!,
- PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER.toLong())
+ ApplicationInfoFlags.of(
+ PackageManager.MATCH_ANY_USER.toLong() or PackageManager.MATCH_ARCHIVED_PACKAGES
+ )
)
} catch (e: PackageManager.NameNotFoundException) {
Log.e(LOG_TAG, "Unable to get packageName")
@@ -180,9 +188,27 @@
}
}
+ parseDeleteFlags(intent)
+
return UninstallReady()
}
+ /**
+ * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent}
+ * to archive an app if requested.
+ *
+ * Do not parse other flags because developers might pass here any flags which might cause
+ * unintended behaviour.
+ * For more context {@link com.android.server.pm.PackageArchiver#requestArchive}.
+ */
+ private fun parseDeleteFlags(intent: Intent) {
+ val flags = intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0)
+ val archive = flags and PackageManager.DELETE_ARCHIVE
+ val keepData = flags and PackageManager.DELETE_KEEP_DATA
+
+ deleteFlags = archive or keepData
+ }
+
fun generateUninstallDetails(): UninstallStage {
val messageBuilder = StringBuilder()
@@ -201,6 +227,8 @@
}
val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+ val isArchive =
+ PmFlags.archiving() && ((deleteFlags and PackageManager.DELETE_ARCHIVE) != 0)
val myUserHandle = Process.myUserHandle()
val isSingleUser = isSingleUser()
@@ -215,34 +243,54 @@
)
)
} else if (uninstallFromAllUsers && !isSingleUser) {
- messageBuilder.append(context.getString(R.string.uninstall_application_text_all_users))
+ val messageString = if (isArchive) {
+ context.getString(R.string.archive_application_text_all_users)
+ } else {
+ context.getString(R.string.uninstall_application_text_all_users)
+ }
+ messageBuilder.append(messageString)
} else if (uninstalledUser != myUserHandle) {
// Uninstalling user is issuing uninstall for another user
val customUserManager = context.createContextAsUser(uninstalledUser!!, 0)
.getSystemService(UserManager::class.java)
val userName = customUserManager!!.userName
- var messageString = context.getString(
- R.string.uninstall_application_text_user,
- userName
- )
+
+ var messageString = if (isArchive) {
+ context.getString(R.string.archive_application_text_user, userName)
+ } else {
+ context.getString(R.string.uninstall_application_text_user, userName)
+ }
+
if (userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)) {
if (customUserManager.isManagedProfile) {
- messageString = context.getString(
+ messageString = if (isArchive) {
+ context.getString(
+ R.string.archive_application_text_current_user_work_profile, userName
+ )
+ } else {
+ context.getString(
R.string.uninstall_application_text_current_user_work_profile, userName
- )
+ )
+ }
} else if (customUserManager.isCloneProfile){
isClonedApp = true
messageString = context.getString(
R.string.uninstall_application_text_current_user_clone_profile
)
} else if (Flags.allowPrivateProfile()
- && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && MultiuserFlags.enablePrivateSpaceFeatures()
&& customUserManager.isPrivateProfile
) {
// TODO(b/324244123): Get these Strings from a User Property API.
- messageString = context.getString(
+ messageString = if (isArchive) {
+ context.getString(
+ R.string.archive_application_text_current_user_private_profile, userName
+ )
+ } else {
+ context.getString(
R.string.uninstall_application_text_current_user_private_profile
- )
+ )
+ }
}
}
messageBuilder.append(messageString)
@@ -262,6 +310,8 @@
targetAppLabel
)
)
+ } else if (isArchive) {
+ messageBuilder.append(context.getString(R.string.archive_application_text))
} else {
messageBuilder.append(context.getString(R.string.uninstall_application_text))
}
@@ -270,15 +320,21 @@
val title = if (isClonedApp) {
context.getString(R.string.cloned_app_label, targetAppLabel)
+ } else if (isArchive) {
+ context.getString(R.string.archiving_app_label, targetAppLabel)
} else {
targetAppLabel.toString()
}
var suggestToKeepAppData = false
try {
- val pkgInfo = packageManager.getPackageInfo(targetPackageName!!, 0)
+ val pkgInfo = packageManager.getPackageInfo(
+ targetPackageName!!, PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES)
+ )
suggestToKeepAppData =
- pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData()
+ pkgInfo.applicationInfo != null
+ && pkgInfo.applicationInfo!!.hasFragileUserData()
+ && !isArchive
} catch (e: PackageManager.NameNotFoundException) {
Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e)
}
@@ -291,7 +347,7 @@
)
}
- return UninstallUserActionRequired(title, message, appDataSize)
+ return UninstallUserActionRequired(title, message, appDataSize, isArchive)
}
/**
@@ -444,10 +500,11 @@
callback!!.onUninstallComplete(targetPackageName!!, legacyStatus, message)
// Since the caller already received the results, just finish the app at this point
- uninstallResult.value = null
+ uninstallResult.value = UninstallAborted(ABORT_REASON_UNINSTALL_DONE)
return
}
val returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
+
if (returnResult || callingActivity != null) {
val intent = Intent()
intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus)
@@ -717,6 +774,7 @@
): Boolean {
var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0
flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0
+ flags = flags or deleteFlags
return try {
context.createContextAsUser(targetUser, 0)
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
index f086209..316e8b7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
@@ -38,7 +38,8 @@
data class UninstallUserActionRequired(
val title: String? = null,
val message: String? = null,
- val appDataSize: Long = 0
+ val appDataSize: Long = 0,
+ val isArchive: Boolean = false
) : UninstallStage(STAGE_USER_ACTION_REQUIRED)
data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) :
@@ -96,6 +97,11 @@
dialogTextResource = R.string.user_is_not_allowed_dlg_text
}
+ ABORT_REASON_UNINSTALL_DONE -> {
+ dialogTitleResource = 0
+ dialogTextResource = 0
+ }
+
else -> {
dialogTitleResource = 0
dialogTextResource = R.string.generic_error_dlg_text
@@ -107,6 +113,7 @@
const val ABORT_REASON_GENERIC_ERROR = 0
const val ABORT_REASON_APP_UNAVAILABLE = 1
const val ABORT_REASON_USER_NOT_ALLOWED = 2
+ const val ABORT_REASON_UNINSTALL_DONE = 3
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
index e2ab316..c61a2ac 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
@@ -16,7 +16,9 @@
package com.android.packageinstaller.v2.ui
-import android.app.Activity
+import android.app.Activity.RESULT_CANCELED
+import android.app.Activity.RESULT_FIRST_USER
+import android.app.Activity.RESULT_OK
import android.app.AppOpsManager
import android.content.ActivityNotFoundException
import android.content.Intent
@@ -135,7 +137,7 @@
}
InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted)
- else -> setResult(Activity.RESULT_CANCELED, null, true)
+ else -> setResult(RESULT_CANCELED, null, true)
}
}
@@ -169,7 +171,7 @@
val success = installStage as InstallSuccess
if (success.shouldReturnResult) {
val successIntent = success.resultIntent
- setResult(Activity.RESULT_OK, successIntent, true)
+ setResult(RESULT_OK, successIntent, true)
} else {
val successDialog = InstallSuccessFragment(success)
showDialogInner(successDialog)
@@ -180,7 +182,7 @@
val failed = installStage as InstallFailed
if (failed.shouldReturnResult) {
val failureIntent = failed.resultIntent
- setResult(Activity.RESULT_FIRST_USER, failureIntent, true)
+ setResult(RESULT_FIRST_USER, failureIntent, true)
} else {
val failureDialog = InstallFailedFragment(failed)
showDialogInner(failureDialog)
@@ -219,7 +221,7 @@
shouldFinish = blockedByPolicyDialog == null
showDialogInner(blockedByPolicyDialog)
}
- setResult(Activity.RESULT_CANCELED, null, shouldFinish)
+ setResult(RESULT_CANCELED, null, shouldFinish)
}
/**
@@ -257,6 +259,10 @@
fun setResult(resultCode: Int, data: Intent?, shouldFinish: Boolean) {
super.setResult(resultCode, data)
+ if (resultCode != RESULT_OK) {
+ // Let callers know that the install was cancelled
+ installViewModel!!.cleanupInstall()
+ }
if (shouldFinish) {
finish()
}
@@ -282,7 +288,7 @@
if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
installViewModel!!.cleanupInstall()
}
- setResult(Activity.RESULT_CANCELED, null, true)
+ setResult(RESULT_CANCELED, null, true)
}
override fun onNegativeResponse(resultCode: Int, data: Intent?) {
@@ -318,7 +324,7 @@
if (localLogv) {
Log.d(LOG_TAG, "Opening $intent")
}
- setResult(Activity.RESULT_OK, intent, true)
+ setResult(RESULT_OK, intent, true)
if (intent != null && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
startActivity(intent)
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
index 87af1ae..524b4e6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
@@ -60,7 +60,7 @@
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
.setTitle(mDialogData.getTitle())
- .setPositiveButton(R.string.ok,
+ .setPositiveButton(mDialogData.isArchive() ? R.string.archive : R.string.ok,
(dialogInt, which) -> mUninstallActionListener.onPositiveResponse(
mKeepData != null && mKeepData.isChecked()))
.setNegativeButton(R.string.cancel,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 7124ed2..c6eb9fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -178,7 +178,7 @@
}
log("updateRelationshipOfGroupDevices: mCachedDevices list =" + mCachedDevices.toString());
- // Get the preferred main device by getPreferredMainDeviceWithoutConectionState
+ // Get the preferred main device by getPreferredMainDeviceWithoutConnectionState
List<CachedBluetoothDevice> groupDevicesList = getGroupDevicesFromAllOfDevicesList(groupId);
CachedBluetoothDevice preferredMainDevice =
getPreferredMainDevice(groupId, groupDevicesList);
@@ -373,6 +373,7 @@
preferredMainDevice.addMemberDevice(deviceItem);
mCachedDevices.remove(deviceItem);
mBtManager.getEventManager().dispatchDeviceRemoved(deviceItem);
+ preferredMainDevice.refresh();
hasChanged = true;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 27fcdbe..26905b1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -80,6 +80,7 @@
"com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE";
public static final String EXTRA_LE_AUDIO_SHARING_STATE = "BLUETOOTH_LE_AUDIO_SHARING_STATE";
public static final String EXTRA_BLUETOOTH_DEVICE = "BLUETOOTH_DEVICE";
+ public static final String EXTRA_START_LE_AUDIO_SHARING = "START_LE_AUDIO_SHARING";
public static final int BROADCAST_STATE_UNKNOWN = 0;
public static final int BROADCAST_STATE_ON = 1;
public static final int BROADCAST_STATE_OFF = 2;
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
index 492828d..64e503b32 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
@@ -174,7 +174,6 @@
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));
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index 69c7410..6198d80 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -181,14 +181,14 @@
* admin status.
*/
public Dialog createDialog(Activity activity,
- ActivityStarter activityStarter, boolean isMultipleAdminEnabled,
+ ActivityStarter activityStarter, boolean canCreateAdminUser,
NewUserData successCallback, Runnable cancelCallback) {
mActivity = activity;
mCustomDialogHelper = new CustomDialogHelper(activity);
mSuccessCallback = successCallback;
mCancelCallback = cancelCallback;
mActivityStarter = activityStarter;
- addCustomViews(isMultipleAdminEnabled);
+ addCustomViews(canCreateAdminUser);
mUserCreationDialog = mCustomDialogHelper.getDialog();
updateLayout();
mUserCreationDialog.setOnDismissListener(view -> finish());
@@ -197,19 +197,19 @@
return mUserCreationDialog;
}
- private void addCustomViews(boolean isMultipleAdminEnabled) {
+ private void addCustomViews(boolean canCreateAdminUser) {
addGrantAdminView();
addUserInfoEditView();
mCustomDialogHelper.setPositiveButton(R.string.next, view -> {
mCurrentState++;
- if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
+ if (mCurrentState == GRANT_ADMIN_DIALOG && !canCreateAdminUser) {
mCurrentState++;
}
updateLayout();
});
mCustomDialogHelper.setNegativeButton(R.string.back, view -> {
mCurrentState--;
- if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
+ if (mCurrentState == GRANT_ADMIN_DIALOG && !canCreateAdminUser) {
mCurrentState--;
}
updateLayout();
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 c88c4c9..0e71116 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
@@ -25,6 +25,7 @@
import android.provider.Settings
import androidx.concurrent.futures.DirectExecutor
import com.android.internal.util.ConcurrentUtils
+import com.android.settingslib.volume.shared.AudioLogger
import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
@@ -99,7 +100,7 @@
private val contentResolver: ContentResolver,
private val backgroundCoroutineContext: CoroutineContext,
private val coroutineScope: CoroutineScope,
- private val logger: Logger,
+ private val logger: AudioLogger,
) : AudioRepository {
private val streamSettingNames: Map<AudioStream, String> =
@@ -117,10 +118,10 @@
override val mode: StateFlow<Int> =
callbackFlow {
- val listener = AudioManager.OnModeChangedListener { newMode -> trySend(newMode) }
- audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener)
- awaitClose { audioManager.removeOnModeChangedListener(listener) }
- }
+ val listener = AudioManager.OnModeChangedListener { newMode -> trySend(newMode) }
+ audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener)
+ awaitClose { audioManager.removeOnModeChangedListener(listener) }
+ }
.onStart { emit(audioManager.mode) }
.flowOn(backgroundCoroutineContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
@@ -140,14 +141,14 @@
override val communicationDevice: StateFlow<AudioDeviceInfo?>
get() =
callbackFlow {
- val listener = OnCommunicationDeviceChangedListener { trySend(Unit) }
- audioManager.addOnCommunicationDeviceChangedListener(
- ConcurrentUtils.DIRECT_EXECUTOR,
- listener,
- )
+ val listener = OnCommunicationDeviceChangedListener { trySend(Unit) }
+ audioManager.addOnCommunicationDeviceChangedListener(
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ listener,
+ )
- awaitClose { audioManager.removeOnCommunicationDeviceChangedListener(listener) }
- }
+ awaitClose { audioManager.removeOnCommunicationDeviceChangedListener(listener) }
+ }
.filterNotNull()
.map { audioManager.communicationDevice }
.onStart { emit(audioManager.communicationDevice) }
@@ -160,15 +161,15 @@
override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
return merge(
- audioManagerEventsReceiver.events.filter {
- if (it is StreamAudioManagerEvent) {
- it.audioStream == audioStream
- } else {
- true
- }
- },
- volumeSettingChanges(audioStream),
- )
+ audioManagerEventsReceiver.events.filter {
+ if (it is StreamAudioManagerEvent) {
+ it.audioStream == audioStream
+ } else {
+ true
+ }
+ },
+ volumeSettingChanges(audioStream),
+ )
.conflate()
.map { getCurrentAudioStream(audioStream) }
.onStart { emit(getCurrentAudioStream(audioStream)) }
@@ -251,11 +252,4 @@
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/data/repository/AudioSharingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
index 7a66335..ebba7f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioSharingRepository.kt
@@ -34,6 +34,7 @@
import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved
import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX
import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN
+import com.android.settingslib.volume.shared.AudioSharingLogger
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,6 +51,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.runningFold
import kotlinx.coroutines.flow.stateIn
@@ -90,6 +92,7 @@
private val btManager: LocalBluetoothManager,
private val coroutineScope: CoroutineScope,
private val backgroundCoroutineContext: CoroutineContext,
+ private val logger: AudioSharingLogger
) : AudioSharingRepository {
private val isAudioSharingProfilesReady: StateFlow<Boolean> =
btManager.profileManager.onServiceStateChanged
@@ -104,6 +107,7 @@
btManager.profileManager.leAudioBroadcastProfile.onBroadcastStartedOrStopped
.map { isBroadcasting() }
.onStart { emit(isBroadcasting()) }
+ .onEach { logger.onAudioSharingStateChanged(it) }
.flowOn(backgroundCoroutineContext)
} else {
flowOf(false)
@@ -156,6 +160,7 @@
.map { getSecondaryGroupId() },
primaryGroupId.map { getSecondaryGroupId() })
.onStart { emit(getSecondaryGroupId()) }
+ .onEach { logger.onSecondaryGroupIdChanged(it) }
.flowOn(backgroundCoroutineContext)
.stateIn(
coroutineScope,
@@ -202,6 +207,7 @@
acc
}
}
+ .onEach { logger.onVolumeMapChanged(it) }
.flowOn(backgroundCoroutineContext)
} else {
emptyFlow()
@@ -220,6 +226,7 @@
BluetoothUtils.getSecondaryDeviceForBroadcast(contentResolver, btManager)
if (cachedDevice != null) {
it.setDeviceVolume(cachedDevice.device, volume, /* isGroupOp= */ true)
+ logger.onSetDeviceVolumeRequested(volume)
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioLogger.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioLogger.kt
new file mode 100644
index 0000000..84f7fcb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioLogger.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.shared
+
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.AudioStreamModel
+
+/** A log interface for audio streams volume events. */
+interface AudioLogger {
+ fun onSetVolumeRequested(audioStream: AudioStream, volume: Int)
+
+ fun onVolumeUpdateReceived(audioStream: AudioStream, model: AudioStreamModel)
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt
similarity index 64%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt
index 460de8c..18a4c6d 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.settingslib.volume.shared
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+/** A log interface for audio sharing volume events. */
+interface AudioSharingLogger {
- /** carrier id */
- int mCarrierId;
+ fun onAudioSharingStateChanged(state: Boolean)
- /** apn */
- String mNiddApn;
-}
+ fun onSecondaryGroupIdChanged(groupId: Int)
+
+ fun onVolumeMapChanged(map: Map<Int, Int>)
+
+ fun onSetDeviceVolumeRequested(volume: Int)
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
index 078f0c8..8c5a085 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
@@ -48,6 +48,7 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -101,7 +102,7 @@
@Captor
private lateinit var assistantCallbackCaptor:
- ArgumentCaptor<BluetoothLeBroadcastAssistant.Callback>
+ ArgumentCaptor<BluetoothLeBroadcastAssistant.Callback>
@Captor private lateinit var btCallbackCaptor: ArgumentCaptor<BluetoothCallback>
@@ -110,6 +111,7 @@
@Captor
private lateinit var volumeCallbackCaptor: ArgumentCaptor<BluetoothVolumeControl.Callback>
+ private val logger = FakeAudioSharingRepositoryLogger()
private val testScope = TestScope()
private val context: Context = ApplicationProvider.getApplicationContext()
@Spy private val contentResolver: ContentResolver = context.contentResolver
@@ -135,16 +137,23 @@
Settings.Secure.putInt(
contentResolver,
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
- TEST_GROUP_ID_INVALID)
+ TEST_GROUP_ID_INVALID
+ )
underTest =
AudioSharingRepositoryImpl(
contentResolver,
btManager,
testScope.backgroundScope,
testScope.testScheduler,
+ logger
)
}
+ @After
+ fun tearDown() {
+ logger.reset()
+ }
+
@Test
fun audioSharingStateChange_profileReady_emitValues() {
testScope.runTest {
@@ -160,6 +169,13 @@
runCurrent()
Truth.assertThat(states).containsExactly(false, true, false, true)
+ Truth.assertThat(logger.logs)
+ .containsAtLeastElementsIn(
+ listOf(
+ "onAudioSharingStateChanged state=true",
+ "onAudioSharingStateChanged state=false",
+ )
+ ).inOrder()
}
}
@@ -187,7 +203,8 @@
Truth.assertThat(groupIds)
.containsExactly(
TEST_GROUP_ID_INVALID,
- TEST_GROUP_ID2)
+ TEST_GROUP_ID2
+ )
}
}
@@ -219,13 +236,16 @@
triggerSourceAdded()
runCurrent()
triggerProfileConnectionChange(
- BluetoothAdapter.STATE_CONNECTING, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+ BluetoothAdapter.STATE_CONNECTING, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+ )
runCurrent()
triggerProfileConnectionChange(
- BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO)
+ BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO
+ )
runCurrent()
triggerProfileConnectionChange(
- BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+ BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+ )
runCurrent()
Truth.assertThat(groupIds)
@@ -235,7 +255,16 @@
TEST_GROUP_ID1,
TEST_GROUP_ID_INVALID,
TEST_GROUP_ID2,
- TEST_GROUP_ID_INVALID)
+ TEST_GROUP_ID_INVALID
+ )
+ Truth.assertThat(logger.logs)
+ .containsAtLeastElementsIn(
+ listOf(
+ "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID_INVALID",
+ "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID2",
+ "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID1",
+ )
+ ).inOrder()
}
}
@@ -257,11 +286,22 @@
verify(volumeControl).unregisterCallback(any())
runCurrent()
+ val expectedMap1 = mapOf(TEST_GROUP_ID1 to TEST_VOLUME1)
+ val expectedMap2 = mapOf(TEST_GROUP_ID1 to TEST_VOLUME2)
Truth.assertThat(volumeMaps)
.containsExactly(
emptyMap<Int, Int>(),
- mapOf(TEST_GROUP_ID1 to TEST_VOLUME1),
- mapOf(TEST_GROUP_ID1 to TEST_VOLUME2))
+ expectedMap1,
+ expectedMap2
+ )
+ Truth.assertThat(logger.logs)
+ .containsAtLeastElementsIn(
+ listOf(
+ "onVolumeMapChanged map={}",
+ "onVolumeMapChanged map=$expectedMap1",
+ "onVolumeMapChanged map=$expectedMap2",
+ )
+ ).inOrder()
}
}
@@ -281,12 +321,19 @@
Settings.Secure.putInt(
contentResolver,
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
- TEST_GROUP_ID2)
+ TEST_GROUP_ID2
+ )
`when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
underTest.setSecondaryVolume(TEST_VOLUME1)
runCurrent()
verify(volumeControl).setDeviceVolume(device1, TEST_VOLUME1, true)
+ Truth.assertThat(logger.logs)
+ .isEqualTo(
+ listOf(
+ "onSetVolumeRequested volume=$TEST_VOLUME1",
+ )
+ )
}
}
@@ -313,7 +360,8 @@
Settings.Secure.putInt(
contentResolver,
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
- TEST_GROUP_ID1)
+ TEST_GROUP_ID1
+ )
`when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
assistantCallbackCaptor.value.sourceAdded(device1, receiveState)
}
@@ -324,7 +372,8 @@
Settings.Secure.putInt(
contentResolver,
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
- TEST_GROUP_ID1)
+ TEST_GROUP_ID1
+ )
assistantCallbackCaptor.value.sourceRemoved(device2)
}
@@ -334,7 +383,8 @@
Settings.Secure.putInt(
contentResolver,
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
- TEST_GROUP_ID1)
+ TEST_GROUP_ID1
+ )
btCallbackCaptor.value.onProfileConnectionStateChanged(cachedDevice2, state, profile)
}
@@ -343,12 +393,14 @@
.registerContentObserver(
eq(Settings.Secure.getUriFor(BluetoothUtils.getPrimaryGroupIdUriForBroadcast())),
eq(false),
- contentObserverCaptor.capture())
+ contentObserverCaptor.capture()
+ )
`when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
Settings.Secure.putInt(
contentResolver,
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
- TEST_GROUP_ID2)
+ TEST_GROUP_ID2
+ )
contentObserverCaptor.value.primaryChanged()
}
@@ -380,8 +432,9 @@
onBroadcastStopped(TEST_REASON, TEST_BROADCAST_ID)
}
val sourceAdded:
- BluetoothLeBroadcastAssistant.Callback.(
- sink: BluetoothDevice, state: BluetoothLeBroadcastReceiveState) -> Unit =
+ BluetoothLeBroadcastAssistant.Callback.(
+ sink: BluetoothDevice, state: BluetoothLeBroadcastReceiveState
+ ) -> Unit =
{ sink, state ->
onReceiveStateChanged(sink, TEST_SOURCE_ID, state)
}
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
index 389bf53..bd573fb 100644
--- 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
@@ -16,10 +16,11 @@
package com.android.settingslib.volume.data.repository
+import com.android.settingslib.volume.shared.AudioLogger
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
-class FakeAudioRepositoryLogger : AudioRepositoryImpl.Logger {
+class FakeAudioRepositoryLogger : AudioLogger {
private val mutableLogs: MutableList<String> = mutableListOf()
val logs: List<String>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt
new file mode 100644
index 0000000..cc4cc8d
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.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.settingslib.volume.data.repository
+
+import com.android.settingslib.volume.shared.AudioSharingLogger
+import java.util.concurrent.CopyOnWriteArrayList
+
+class FakeAudioSharingRepositoryLogger : AudioSharingLogger {
+ private val mutableLogs = CopyOnWriteArrayList<String>()
+ val logs: List<String>
+ get() = mutableLogs.toList()
+
+ fun reset() {
+ mutableLogs.clear()
+ }
+
+ override fun onAudioSharingStateChanged(state: Boolean) {
+ mutableLogs.add("onAudioSharingStateChanged state=$state")
+ }
+
+ override fun onSecondaryGroupIdChanged(groupId: Int) {
+ mutableLogs.add("onSecondaryGroupIdChanged groupId=$groupId")
+ }
+
+ override fun onVolumeMapChanged(map: GroupIdToVolumes) {
+ mutableLogs.add("onVolumeMapChanged map=$map")
+ }
+
+ override fun onSetDeviceVolumeRequested(volume: Int) {
+ mutableLogs.add("onSetVolumeRequested volume=$volume")
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 7e7c76e..8cc9974 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -35,6 +35,7 @@
import android.os.ParcelUuid;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -545,6 +546,7 @@
* Test to verify OnDeviceUnpaired() for csip device unpair.
*/
@Test
+ @Ignore("b/359066481")
public void onDeviceUnpaired_unpairCsipSubDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
index f94f21f..698eb81 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
@@ -19,7 +19,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
@@ -352,4 +354,34 @@
assertThat(mCachedDevice1.getMemberDevice()).contains(mCachedDevice3);
assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice);
}
+
+ @Test
+ public void onProfileConnectionStateChangedIfProcessed_addMemberDevice_refreshUI() {
+ mCachedDevice3.setGroupId(GROUP1);
+
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+ BluetoothProfile.STATE_CONNECTED);
+
+ verify(mCachedDevice1).refresh();
+ }
+
+ @Test
+ public void onProfileConnectionStateChangedIfProcessed_switchMainDevice_refreshUI() {
+ when(mDevice3.isConnected()).thenReturn(true);
+ when(mDevice2.isConnected()).thenReturn(false);
+ when(mDevice1.isConnected()).thenReturn(false);
+ mCachedDevice3.setGroupId(GROUP1);
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+ BluetoothProfile.STATE_CONNECTED);
+
+ when(mDevice3.isConnected()).thenReturn(false);
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+ BluetoothProfile.STATE_DISCONNECTED);
+ when(mDevice1.isConnected()).thenReturn(true);
+ mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
+ BluetoothProfile.STATE_CONNECTED);
+
+ verify(mCachedDevice3).switchMemberDeviceContent(mCachedDevice1);
+ verify(mCachedDevice3, atLeastOnce()).refresh();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
index 194a0e2..4e54d8f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
@@ -40,6 +40,7 @@
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,6 +59,7 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowInteractionJankMonitor.class, SettingsJankMonitorTest.ShadowBuilder.class})
+@Ignore("b/359066481")
public class SettingsJankMonitorTest {
private static final String TEST_KEY = "key";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 0d318c3..325bb2c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -36,6 +36,7 @@
import com.android.settingslib.testutils.shadow.ShadowSmsApplication;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -86,6 +87,7 @@
mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService);
}
+ @Ignore("b/359066481")
@Test
public void testIsAllowlisted() throws Exception {
doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
@@ -160,6 +162,7 @@
assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE, UID)).isTrue();
}
+ @Ignore("b/359066481")
@Test
public void testIsSystemAllowlisted() throws Exception {
doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
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
index 00c7ae3..539519b 100644
--- 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
@@ -123,7 +123,6 @@
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,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 4f3b200..b549e3f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -26,6 +26,7 @@
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@@ -86,6 +87,7 @@
assertThat(PowerUtil.roundTimeToNearestThreshold(-200, -75)).isEqualTo(225);
}
+ @Ignore("b/359066481")
@Test
public void getTargetTimeShortString_lessThan15Minutes_returnsTimeShortStringWithoutRounded() {
mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
@@ -100,6 +102,7 @@
assertThat(actualTimeString).endsWith("14 PM");
}
+ @Ignore("b/359066481")
@Test
public void getTargetTimeShortString_moreThan15Minutes_returnsTimeShortStringWithRounded() {
mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
index f0f53d6..7f4bdae 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
@@ -44,6 +44,7 @@
import com.android.settingslib.widget.preference.banner.R;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -53,6 +54,7 @@
import org.robolectric.shadows.ShadowTouchDelegate;
import org.robolectric.util.ReflectionHelpers;
+@Ignore("b/359066481")
@RunWith(RobolectricTestRunner.class)
public class BannerMessagePreferenceTest {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 30c4ee5..9ab853f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -690,7 +690,7 @@
Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
null, null);
try {
- return extractRelevantValues(cursor, GlobalSettings.SETTINGS_TO_BACKUP);
+ return extractRelevantValues(cursor, getGlobalSettingsToBackup());
} finally {
cursor.close();
}
@@ -1011,7 +1011,7 @@
Settings.System.LEGACY_RESTORE_SETTINGS);
validators = SystemSettingsValidators.VALIDATORS;
} else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
- whitelist = ArrayUtils.concat(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
+ whitelist = ArrayUtils.concat(String.class, getGlobalSettingsToBackup(),
Settings.Global.LEGACY_RESTORE_SETTINGS);
validators = GlobalSettingsValidators.VALIDATORS;
} else {
@@ -1021,6 +1021,17 @@
return new SettingsBackupWhitelist(whitelist, validators);
}
+ private String[] getGlobalSettingsToBackup() {
+ // On watches, we don't want to backup or restore 'bluetooth_on' setting, as setting it to
+ // false during restore would cause watch OOBE to fail due to bluetooth connection loss.
+ if (isWatch()) {
+ return ArrayUtils.removeElement(
+ String.class, GlobalSettings.SETTINGS_TO_BACKUP, Settings.Global.BLUETOOTH_ON);
+ }
+
+ return GlobalSettings.SETTINGS_TO_BACKUP;
+ }
+
private boolean isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key) {
String contentKey = Uri.withAppendedPath(areaUri, key).toString();
return dynamicBlockList.contains(contentKey);
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index d4ca4a3..3a391505 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -26,6 +26,7 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
@@ -221,6 +222,21 @@
}
@Test
+ public void testOnRestore_bluetoothOnRestoredOnNonWearablesOnly() {
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ restoreGlobalSettings(generateBackupData(Map.of(Settings.Global.BLUETOOTH_ON, "0")));
+
+ var isWatch = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ if (isWatch) {
+ assertFalse(settingsHelper.mWrittenValues.containsKey(Settings.Global.BLUETOOTH_ON));
+ } else {
+ assertEquals("0", settingsHelper.mWrittenValues.get(Settings.Global.BLUETOOTH_ON));
+ }
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_CONFIGURABLE_FONT_SCALE_DEFAULT)
public void testFindClosestAllowedFontScale() {
final String[] availableFontScales = new String[]{"0.5", "0.9", "1.0", "1.1", "1.5"};
@@ -266,6 +282,20 @@
return buffer.array();
}
+ private void restoreGlobalSettings(byte[] backupData) {
+ mAgentUnderTest.restoreSettings(
+ backupData,
+ /* pos= */ 0,
+ backupData.length,
+ Settings.Global.CONTENT_URI,
+ null,
+ null,
+ null,
+ R.array.restore_blocked_global_settings,
+ /* dynamicBlockList= */ Collections.emptySet(),
+ /* settingsToPreserve= */ Collections.emptySet());
+ }
+
private byte[] generateUncorruptedHeader() throws IOException {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
mAgentUnderTest.writeHeader(os);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9f3c2bf..1d9f469 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -482,6 +482,15 @@
android:exported="true"
android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter>
+ <action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity"
+ android:exported="true"
+ android:theme="@style/Theme.AppCompat.NoActionBar">
+ <intent-filter>
<action android:name="com.android.systemui.action.TOUCHPAD_KEYBOARD_TUTORIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 2e036e6..6bea30f 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -348,7 +348,17 @@
/** Toggles a11y menu layout visibility. */
public void toggleVisibility() {
- mLayout.setVisibility((mLayout.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE);
+ if (mLayout.getVisibility() == View.VISIBLE) {
+ mLayout.setVisibility(View.GONE);
+ } else {
+ if (Flags.hideRestrictedActions()) {
+ // Reconfigure the shortcut list in case the set of restricted actions has changed.
+ mA11yMenuViewPager.configureViewPagerAndFooter(
+ mLayout, createShortcutList(), getPageIndex());
+ updateViewLayout();
+ }
+ mLayout.setVisibility(View.VISIBLE);
+ }
}
/** Shows hint text on a minimal Snackbar-like text view. */
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index d16617f..4ab771b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -542,8 +542,6 @@
final Context context = sInstrumentation.getTargetContext();
final UserManager userManager = context.getSystemService(UserManager.class);
userManager.setUserRestriction(restriction, isRestricted);
- // Re-enable the service for the restriction to take effect.
- enableA11yMenuService(context);
}
private static void unlockSignal() throws IOException {
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
index e81d5d5..95e4b59 100644
--- a/packages/SystemUI/aconfig/biometrics_framework.aconfig
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -3,9 +3,3 @@
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
-flag {
- name: "constraint_bp"
- namespace: "biometrics_framework"
- description: "Refactors Biometric Prompt to use a ConstraintLayout"
- bug: "288175072"
-}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e6fae7b..cdbac33 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -156,17 +156,6 @@
}
flag {
- name: "pss_app_selector_abrupt_exit_fix"
- namespace: "systemui"
- description: "Fixes the app selector abruptly disappearing without an animation, when the"
- "selected task is the foreground task."
- bug: "314385883"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
@@ -388,6 +377,16 @@
}
flag {
+ name: "status_bar_swipe_over_chip"
+ namespace: "systemui"
+ description: "Allow users to swipe over the status bar chip to open the shade"
+ bug: "185897191"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "compose_bouncer"
namespace: "systemui"
description: "Use the new compose bouncer in SystemUI"
@@ -504,6 +503,16 @@
}
flag {
+ name: "status_bar_switch_to_spn_from_data_spn"
+ namespace: "systemui"
+ description: "Fix usage of the SPN broadcast extras"
+ bug: "350812372"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "haptic_volume_slider"
namespace: "systemui"
description: "Adds haptic feedback to the volume slider."
@@ -1248,6 +1257,16 @@
}
flag {
+ name: "relock_with_power_button_immediately"
+ namespace: "systemui"
+ description: "UDFPS unlock followed by immediate power button push should relock"
+ bug: "343327511"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "lockscreen_preview_renderer_create_on_main_thread"
namespace: "systemui"
description: "Force preview renderer to be created on the main thread"
@@ -1274,6 +1293,13 @@
}
flag {
+ name: "new_picker_ui"
+ namespace: "systemui"
+ description: "Enables the BC25 design of the customization picker UI."
+ bug: "339081035"
+}
+
+flag {
namespace: "systemui"
name: "settings_ext_register_content_observer_on_bg_thread"
description: "Register content observer in callback flow APIs on background thread in SettingsProxyExt."
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt
new file mode 100644
index 0000000..8f5cdbf
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.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.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+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 org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.getParentOfType
+
+/**
+ * Checks if registerContentObserver/registerContentObserverAsUser/unregisterContentObserver is
+ * called on a ContentResolver (or subclasses), and directs the caller to using
+ * com.android.systemui.util.settings.SettingsProxy or its sub-classes.
+ */
+@Suppress("UnstableApiUsage")
+class RegisterContentObserverViaContentResolverDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames(): List<String> {
+ return CONTENT_RESOLVER_METHOD_LIST
+ }
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ val classQualifiedName = node.getParentOfType(UClass::class.java)?.qualifiedName
+ if (classQualifiedName in CLASSNAME_ALLOWLIST) {
+ // Don't warn for class we want the developers to use.
+ return
+ }
+
+ val evaluator = context.evaluator
+ if (evaluator.isMemberInSubClassOf(method, "android.content.ContentResolver")) {
+ context.report(
+ issue = CONTENT_RESOLVER_ERROR,
+ location = context.getNameLocation(node),
+ message =
+ "`ContentResolver.${method.name}()` should be replaced with " +
+ "an appropriate interface API call, for eg. " +
+ "`<SettingsProxy>/<UserSettingsProxy>.${method.name}()`"
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val CONTENT_RESOLVER_ERROR: Issue =
+ Issue.create(
+ id = "RegisterContentObserverViaContentResolver",
+ briefDescription =
+ "Content observer registration done via `ContentResolver`" +
+ "instead of `SettingsProxy or child interfaces.`",
+ // lint trims indents and converts \ to line continuations
+ explanation =
+ """
+ Use registerContentObserver/unregisterContentObserver methods in \
+ `SettingsProxy`, `UserSettingsProxy` or `GlobalSettings` class instead of \
+ using `ContentResolver.registerContentObserver` or \
+ `ContentResolver.unregisterContentObserver`.""",
+ category = Category.PERFORMANCE,
+ priority = 10,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ RegisterContentObserverViaContentResolverDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private val CLASSNAME_ALLOWLIST =
+ listOf(
+ "com.android.systemui.util.settings.SettingsProxy",
+ "com.android.systemui.util.settings.UserSettingsProxy",
+ "com.android.systemui.util.settings.GlobalSettings",
+ "com.android.systemui.util.settings.SecureSettings",
+ "com.android.systemui.util.settings.SystemSettings"
+ )
+
+ private val CONTENT_RESOLVER_METHOD_LIST =
+ listOf(
+ "registerContentObserver",
+ "registerContentObserverAsUser",
+ "unregisterContentObserver"
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 5206b05..a1f4f55 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -46,7 +46,8 @@
DemotingTestWithoutBugDetector.ISSUE,
TestFunctionNameViolationDetector.ISSUE,
MissingApacheLicenseDetector.ISSUE,
- RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING
+ RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING,
+ RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR
)
override val api: Int
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt
new file mode 100644
index 0000000..1d33bce
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt
@@ -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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class RegisterContentObserverViaContentResolverDetectorTest : SystemUILintDetectorTest() {
+
+ override fun getDetector(): Detector = RegisterContentObserverViaContentResolverDetector()
+
+ override fun getIssues(): List<Issue> =
+ listOf(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+
+ @Test
+ fun testRegisterContentObserver_throwError() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:7: Error: ContentResolver.registerContentObserver() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.registerContentObserver() [RegisterContentObserverViaContentResolver]
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ )
+ }
+
+ @Test
+ fun testRegisterContentObserverForUser_throwError() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserverAsUser(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:7: Error: ContentResolver.registerContentObserverAsUser() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.registerContentObserverAsUser() [RegisterContentObserverViaContentResolver]
+ registerContentObserverAsUser(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+ .trimIndent()
+ )
+ }
+
+ @Test
+ fun testSuppressRegisterContentObserver() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ @SuppressWarnings("RegisterContentObserverViaContentResolver")
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testRegisterContentObserverInSettingsProxy_allowed() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package com.android.systemui.util.settings;
+ import android.content.Context;
+
+ public class SettingsProxy {
+ public void register(Context context) {
+ context.getContentResolver().
+ registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false, mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testNoopIfNoCall() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class SettingsProxy {
+ public void register(Context context) {
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testUnRegisterContentObserver_throwError() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass {
+ public void register(Context context) {
+ context.getContentResolver().
+ unregisterContentObserver(mSettingObserver);
+ }
+ }
+ """
+ )
+ .indented(),
+ *androidStubs
+ )
+ .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:7: Error: ContentResolver.unregisterContentObserver() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver() [RegisterContentObserverViaContentResolver]
+ unregisterContentObserver(mSettingObserver);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ )
+ }
+}
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 8c38253..6fca178 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
@@ -28,6 +28,7 @@
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.view.layout.sections.CommunalAppWidgetSection
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
@@ -46,6 +47,7 @@
private val bottomAreaSection: BottomAreaSection,
private val ambientStatusBarSection: AmbientStatusBarSection,
private val communalPopupSection: CommunalPopupSection,
+ private val widgetSection: CommunalAppWidgetSection,
) {
@Composable
@@ -63,6 +65,7 @@
viewModel = viewModel,
interactionHandler = interactionHandler,
dialogFactory = dialogFactory,
+ widgetSection = widgetSection,
modifier = Modifier.element(Communal.Elements.Grid)
)
}
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 b65b471..b93b049 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
@@ -19,10 +19,7 @@
import android.content.Context
import android.content.res.Configuration
import android.graphics.drawable.Icon
-import android.os.Bundle
import android.util.SizeF
-import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
-import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
import android.widget.FrameLayout
import android.widget.RemoteViews
import androidx.annotation.VisibleForTesting
@@ -105,7 +102,6 @@
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
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
@@ -169,6 +165,7 @@
import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
import com.android.systemui.communal.ui.compose.extensions.observeTaps
+import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -180,11 +177,12 @@
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import kotlinx.coroutines.launch
-@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CommunalHub(
modifier: Modifier = Modifier,
viewModel: BaseCommunalViewModel,
+ widgetSection: CommunalAppWidgetSection,
interactionHandler: RemoteViews.InteractionHandler? = null,
dialogFactory: SystemUIDialogFactory? = null,
widgetConfigurator: WidgetConfigurator? = null,
@@ -383,6 +381,7 @@
selectedKey = selectedKey,
widgetConfigurator = widgetConfigurator,
interactionHandler = interactionHandler,
+ widgetSection = widgetSection,
)
}
}
@@ -632,6 +631,7 @@
updateDragPositionForRemove: (offset: Offset) -> Boolean,
widgetConfigurator: WidgetConfigurator?,
interactionHandler: RemoteViews.InteractionHandler?,
+ widgetSection: CommunalAppWidgetSection,
) {
var gridModifier =
Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
@@ -719,18 +719,20 @@
index = index,
contentListState = contentListState,
interactionHandler = interactionHandler,
+ widgetSection = widgetSection,
)
}
} else {
CommunalContent(
- modifier = cardModifier.animateItem(),
model = list[index],
viewModel = viewModel,
size = size,
selected = false,
+ modifier = cardModifier.animateItem(),
index = index,
contentListState = contentListState,
interactionHandler = interactionHandler,
+ widgetSection = widgetSection,
)
}
}
@@ -972,6 +974,7 @@
index: Int,
contentListState: ContentListState,
interactionHandler: RemoteViews.InteractionHandler?,
+ widgetSection: CommunalAppWidgetSection,
) {
when (model) {
is CommunalContentModel.WidgetContent.Widget ->
@@ -983,7 +986,8 @@
widgetConfigurator,
modifier,
index,
- contentListState
+ contentListState,
+ widgetSection,
)
is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(modifier)
is CommunalContentModel.WidgetContent.DisabledWidget ->
@@ -1116,9 +1120,9 @@
modifier: Modifier = Modifier,
index: Int,
contentListState: ContentListState,
+ widgetSection: CommunalAppWidgetSection,
) {
val context = LocalContext.current
- val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
val accessibilityLabel =
remember(model, context) {
model.providerInfo.loadLabel(context.packageManager).toString().trim()
@@ -1205,36 +1209,14 @@
}
}
) {
- AndroidView(
- modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
- factory = { context ->
- model.appWidgetHost
- .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
- .apply {
- updateAppWidgetSize(
- /* newOptions = */ Bundle(),
- /* minWidth = */ size.width.toInt(),
- /* minHeight = */ size.height.toInt(),
- /* maxWidth = */ size.width.toInt(),
- /* maxHeight = */ size.height.toInt(),
- /* ignorePadding = */ true
- )
- accessibilityDelegate = viewModel.widgetAccessibilityDelegate
- }
- },
- update = {
- it.apply {
- importantForAccessibility =
- if (isFocusable) {
- IMPORTANT_FOR_ACCESSIBILITY_AUTO
- } else {
- IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- }
- }
- },
- // For reusing composition in lazy lists.
- onReset = {},
- )
+ with(widgetSection) {
+ Widget(
+ viewModel = viewModel,
+ model = model,
+ size = size,
+ modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
+ )
+ }
if (
viewModel is CommunalEditModeViewModel &&
model.reconfigurable &&
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 5886d7d..6750e41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -23,6 +23,7 @@
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.SysUISingleton
@@ -42,6 +43,7 @@
private val viewModel: CommunalViewModel,
private val dialogFactory: SystemUIDialogFactory,
private val interactionHandler: WidgetInteractionHandler,
+ private val widgetSection: CommunalAppWidgetSection,
) : ComposableScene {
override val key = Scenes.Communal
@@ -55,6 +57,12 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- CommunalHub(modifier, viewModel, interactionHandler, dialogFactory)
+ CommunalHub(
+ modifier = modifier,
+ viewModel = viewModel,
+ interactionHandler = interactionHandler,
+ widgetSection = widgetSection,
+ dialogFactory = dialogFactory,
+ )
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
new file mode 100644
index 0000000..04bcc36
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.composable
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+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.Color
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.modifiers.background
+import com.android.compose.modifiers.height
+import com.android.compose.modifiers.width
+import com.android.systemui.deviceentry.shared.model.BiometricMessage
+import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
+import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerUdfpsViewBinder
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+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.res.R
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@Composable
+fun AlternateBouncer(
+ alternateBouncerDependencies: AlternateBouncerDependencies,
+ modifier: Modifier = Modifier,
+) {
+
+ val isVisible by
+ alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(
+ initialValue = false
+ )
+
+ val udfpsIconLocation by
+ alternateBouncerDependencies.udfpsIconViewModel.iconLocation.collectAsStateWithLifecycle(
+ initialValue = null
+ )
+
+ // TODO (b/353955910): back handling doesn't work
+ BackHandler { alternateBouncerDependencies.viewModel.onBackRequested() }
+
+ AnimatedVisibility(
+ visible = isVisible,
+ enter = fadeIn(),
+ exit = fadeOut(),
+ modifier = modifier,
+ ) {
+ Box(
+ contentAlignment = Alignment.TopCenter,
+ modifier =
+ Modifier.background(color = Colors.AlternateBouncerBackgroundColor, alpha = { 1f })
+ .pointerInput(Unit) {
+ detectTapGestures(
+ onTap = { alternateBouncerDependencies.viewModel.onTapped() }
+ )
+ },
+ ) {
+ StatusMessage(
+ viewModel = alternateBouncerDependencies.messageAreaViewModel,
+ )
+ }
+
+ udfpsIconLocation?.let { udfpsLocation ->
+ Box {
+ DeviceEntryIcon(
+ viewModel = alternateBouncerDependencies.udfpsIconViewModel,
+ modifier =
+ Modifier.width { udfpsLocation.width }
+ .height { udfpsLocation.height }
+ .fillMaxHeight()
+ .offset {
+ IntOffset(
+ x = udfpsLocation.left,
+ y = udfpsLocation.top,
+ )
+ },
+ )
+ }
+
+ UdfpsA11yOverlay(
+ viewModel = alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel.get(),
+ modifier = Modifier.fillMaxHeight(),
+ )
+ }
+ }
+}
+
+@ExperimentalCoroutinesApi
+@Composable
+private fun StatusMessage(
+ viewModel: AlternateBouncerMessageAreaViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val message: BiometricMessage? by
+ viewModel.message.collectAsStateWithLifecycle(initialValue = null)
+
+ Crossfade(
+ targetState = message,
+ label = "Alternate Bouncer message",
+ animationSpec = tween(),
+ modifier = modifier,
+ ) { biometricMessage ->
+ biometricMessage?.let {
+ Text(
+ textAlign = TextAlign.Center,
+ text = it.message ?: "",
+ color = Colors.AlternateBouncerTextColor,
+ fontSize = 18.sp,
+ lineHeight = 24.sp,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.padding(top = 92.dp),
+ )
+ }
+ }
+}
+
+@ExperimentalCoroutinesApi
+@Composable
+private fun DeviceEntryIcon(
+ viewModel: AlternateBouncerUdfpsIconViewModel,
+ modifier: Modifier = Modifier,
+) {
+ AndroidView(
+ modifier = modifier,
+ factory = { context ->
+ val view =
+ DeviceEntryIconView(context, null).apply {
+ id = R.id.alternate_bouncer_udfps_icon_view
+ contentDescription =
+ context.resources.getString(R.string.accessibility_fingerprint_label)
+ }
+ AlternateBouncerUdfpsViewBinder.bind(view, viewModel)
+ view
+ },
+ )
+}
+
+/** TODO (b/353955910): Validate accessibility CUJs */
+@ExperimentalCoroutinesApi
+@Composable
+private fun UdfpsA11yOverlay(
+ viewModel: AlternateBouncerUdfpsAccessibilityOverlayViewModel,
+ modifier: Modifier = Modifier,
+) {
+ AndroidView(
+ factory = { context ->
+ val view =
+ UdfpsAccessibilityOverlay(context).apply {
+ id = R.id.alternate_bouncer_udfps_accessibility_overlay
+ }
+ UdfpsAccessibilityOverlayBinder.bind(view, viewModel)
+ view
+ },
+ modifier = modifier,
+ )
+}
+
+private object Colors {
+ val AlternateBouncerBackgroundColor: Color = Color.Black.copy(alpha = .66f)
+ val AlternateBouncerTextColor: Color = Color.White
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 4e117d6..6801cf2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -48,7 +49,7 @@
@Inject
constructor(
private val stackScrollView: Lazy<NotificationScrollView>,
- private val viewModel: NotificationsPlaceholderViewModel,
+ private val viewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val aodBurnInViewModel: AodBurnInViewModel,
sharedNotificationContainer: SharedNotificationContainer,
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
@@ -98,7 +99,7 @@
ConstrainedNotificationStack(
stackScrollView = stackScrollView.get(),
- viewModel = viewModel,
+ viewModel = rememberViewModel { viewModelFactory.create() },
modifier =
modifier
.fillMaxWidth()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 26ab10b..808e666 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -24,9 +24,10 @@
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.layout.layout
-import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.MovableElementKey
import com.android.compose.animation.scene.SceneScope
@@ -51,38 +52,48 @@
mediaHost: MediaHost,
modifier: Modifier = Modifier,
carouselController: MediaCarouselController,
+ offsetProvider: (() -> IntOffset)? = null,
) {
if (!isVisible) {
return
}
- val density = LocalDensity.current
val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
- val layoutWidth = 0
- val layoutHeight = with(density) { mediaHeight.toPx() }.toInt()
-
- // Notify controller to size the carousel for the current space
- mediaHost.measurementInput = MeasurementInput(layoutWidth, layoutHeight)
- carouselController.setSceneContainerSize(layoutWidth, layoutHeight)
-
MovableElement(
key = MediaCarousel.Elements.Content,
- modifier = modifier.height(mediaHeight).fillMaxWidth()
+ modifier = modifier.height(mediaHeight).fillMaxWidth(),
) {
content {
AndroidView(
modifier =
- Modifier.fillMaxSize().layout { measurable, constraints ->
- val placeable = measurable.measure(constraints)
+ Modifier.fillMaxSize()
+ .approachLayout(
+ isMeasurementApproachInProgress = { offsetProvider != null },
+ approachMeasure = { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ placeable.placeRelative(
+ offsetProvider?.invoke() ?: IntOffset.Zero
+ )
+ }
+ }
+ )
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
- // Notify controller to size the carousel for the current space
- mediaHost.measurementInput =
- MeasurementInput(placeable.width, placeable.height)
- carouselController.setSceneContainerSize(placeable.width, placeable.height)
+ // Notify controller to size the carousel for the current space
+ mediaHost.measurementInput =
+ MeasurementInput(placeable.width, placeable.height)
+ carouselController.setSceneContainerSize(
+ placeable.width,
+ placeable.height
+ )
- layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) }
- },
+ layout(placeable.width, placeable.height) {
+ placeable.placeRelative(0, 0)
+ }
+ },
factory = { context ->
FrameLayout(context).apply {
layoutParams =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
index 3f04f37..70c0db1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
@@ -27,7 +27,6 @@
/** [ElementContentPicker] implementation for the media carousel object. */
object MediaContentPicker : StaticElementContentPicker {
- const val SHADE_FRACTION = 0.66f
override val contents =
setOf(
Scenes.Lockscreen,
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 66be7bc..0cb8bd3 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,10 +20,8 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
@@ -32,7 +30,6 @@
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneContentViewModel
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
@@ -54,11 +51,10 @@
class NotificationsShadeScene
@Inject
constructor(
- private val contentViewModelFactory: NotificationsShadeSceneContentViewModel.Factory,
private val actionsViewModelFactory: NotificationsShadeSceneActionsViewModel.Factory,
private val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
- private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+ private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
@@ -84,8 +80,9 @@
override fun SceneScope.Content(
modifier: Modifier,
) {
- val viewModel = rememberViewModel { contentViewModelFactory.create() }
- val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle()
+ val notificationsPlaceholderViewModel = rememberViewModel {
+ notificationsPlaceholderViewModelFactory.create()
+ }
OverlayShade(
modifier = modifier,
@@ -110,8 +107,6 @@
shouldFillMaxSize = false,
shouldReserveSpaceForNavBar = false,
shadeMode = ShadeMode.Dual,
- onEmptySpaceClick =
- viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
modifier = Modifier.fillMaxWidth(),
)
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 8bba0f4..12edd04 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
@@ -121,7 +121,7 @@
private val shadeSession: SaveableSession,
private val notificationStackScrollView: Lazy<NotificationScrollView>,
private val viewModel: QuickSettingsSceneViewModel,
- private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+ private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
@@ -140,7 +140,8 @@
QuickSettingsScene(
notificationStackScrollView = notificationStackScrollView.get(),
viewModel = viewModel,
- notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
+ notificationsPlaceholderViewModel =
+ rememberViewModel { notificationsPlaceholderViewModelFactory.create() },
createTintedIconManager = tintedIconManagerFactory::create,
createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
statusBarIconController = statusBarIconController,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index b0c3fb31..f15e87b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -26,6 +26,7 @@
import com.android.compose.animation.scene.animateSceneDpAsState
import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
@@ -47,7 +48,7 @@
@Inject
constructor(
private val notificationStackScrolLView: Lazy<NotificationScrollView>,
- private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+ private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val viewModelFactory: GoneSceneActionsViewModel.Factory,
) : ComposableScene {
override val key = Scenes.Gone
@@ -73,7 +74,7 @@
Spacer(modifier.fillMaxSize())
SnoozeableHeadsUpNotificationSpace(
stackScrollView = notificationStackScrolLView.get(),
- viewModel = notificationsPlaceholderViewModel
+ viewModel = rememberViewModel { notificationsPlaceholderViewModelFactory.create() },
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
index a9da733..71fa6c9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
@@ -25,7 +25,6 @@
import com.android.compose.animation.scene.UserActionDistance
import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaContentPicker
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.shade.ui.composable.Shade
@@ -54,13 +53,7 @@
fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
fractionRange(start = .33f) {
- val qsTranslation =
- ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
- val qsExpansionDiff =
- ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
- translate(MediaCarousel.Elements.Content, y = -(qsExpansionDiff + qsTranslation))
fade(MediaCarousel.Elements.Content)
-
fade(ShadeHeader.Elements.Clock)
fade(ShadeHeader.Elements.CollapsedContentStart)
fade(ShadeHeader.Elements.CollapsedContentEnd)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
index 21dfc49..b677dff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
@@ -26,7 +26,6 @@
import com.android.compose.animation.scene.UserActionDistance
import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaContentPicker
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Scenes
@@ -62,12 +61,9 @@
fade(QuickSettings.Elements.FooterActions)
}
- val qsTranslation = ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
- val qsExpansionDiff =
- ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
-
- translate(QuickSettings.Elements.QuickQuickSettings, y = -qsTranslation)
- translate(MediaCarousel.Elements.Content, y = -(qsExpansionDiff + qsTranslation))
+ val qsTranslation = -ShadeHeader.Dimensions.CollapsedHeight * 0.66f
+ translate(QuickSettings.Elements.QuickQuickSettings, y = qsTranslation)
+ translate(MediaCarousel.Elements.Content, y = qsTranslation)
translate(Notifications.Elements.NotificationScrim, Edge.Top, false)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeMediaOffsetProvider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeMediaOffsetProvider.kt
new file mode 100644
index 0000000..e0b7909
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeMediaOffsetProvider.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.ui.composable
+
+import androidx.compose.ui.unit.IntOffset
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+
+/**
+ * Provider for the extra offset for the Media section in the shade to accommodate for the squishing
+ * qs or qqs tiles.
+ */
+interface ShadeMediaOffsetProvider {
+
+ /** Returns current offset to be applied to the Media Carousel */
+ val offset: IntOffset
+
+ /**
+ * [ShadeMediaOffsetProvider] implementation for Quick Settings.
+ *
+ * [updateLayout] should represent an access to some state to trigger Compose to relayout to
+ * track [QSSceneAdapter] internal state changes during the transition.
+ */
+ class Qs(private val updateLayout: () -> Unit, private val qsSceneAdapter: QSSceneAdapter) :
+ ShadeMediaOffsetProvider {
+
+ override val offset: IntOffset
+ get() =
+ calculateQsOffset(
+ updateLayout,
+ qsSceneAdapter.qsHeight,
+ qsSceneAdapter.squishedQsHeight
+ )
+ }
+
+ /**
+ * [ShadeMediaOffsetProvider] implementation for Quick Quick Settings.
+ *
+ * [updateLayout] should represent an access to some state to trigger Compose to relayout to
+ * track [QSSceneAdapter] internal state changes during the transition.
+ */
+ class Qqs(private val updateLayout: () -> Unit, private val qsSceneAdapter: QSSceneAdapter) :
+ ShadeMediaOffsetProvider {
+
+ override val offset: IntOffset
+ get() =
+ calculateQsOffset(
+ updateLayout,
+ qsSceneAdapter.qqsHeight,
+ qsSceneAdapter.squishedQqsHeight
+ )
+ }
+
+ companion object {
+
+ protected fun calculateQsOffset(
+ updateLayout: () -> Unit,
+ qsHeight: Int,
+ qsSquishedHeight: Int
+ ): IntOffset {
+ updateLayout()
+ val distanceFromBottomToActualBottom = qsHeight - qsSquishedHeight
+ return IntOffset(0, -distanceFromBottomToActualBottom)
+ }
+ }
+}
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 0e3fcf4..7920e74 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
@@ -118,7 +118,6 @@
object Shade {
object Elements {
- val MediaCarousel = ElementKey("ShadeMediaCarousel")
val BackgroundScrim =
ElementKey("ShadeBackgroundScrim", contentPicker = LowestZIndexContentPicker)
val SplitShadeStartColumn = ElementKey("SplitShadeStartColumn")
@@ -149,7 +148,7 @@
private val notificationStackScrollView: Lazy<NotificationScrollView>,
private val actionsViewModelFactory: ShadeSceneActionsViewModel.Factory,
private val contentViewModelFactory: ShadeSceneContentViewModel.Factory,
- private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+ private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
@@ -178,7 +177,8 @@
ShadeScene(
notificationStackScrollView.get(),
viewModel = rememberViewModel { contentViewModelFactory.create() },
- notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
+ notificationsPlaceholderViewModel =
+ rememberViewModel { notificationsPlaceholderViewModelFactory.create() },
createTintedIconManager = tintedIconManagerFactory::create,
createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
statusBarIconController = statusBarIconController,
@@ -283,6 +283,13 @@
val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
+ val mediaOffsetProvider = remember {
+ ShadeMediaOffsetProvider.Qqs(
+ { @Suppress("UNUSED_EXPRESSION") tileSquishiness },
+ viewModel.qsSceneAdapter,
+ )
+ }
+
Box(
modifier =
modifier.thenIf(shouldPunchHoleBehindScrim) {
@@ -335,12 +342,12 @@
)
}
- MediaCarousel(
+ ShadeMediaCarousel(
isVisible = isMediaVisible,
mediaHost = mediaHost,
+ mediaOffsetProvider = mediaOffsetProvider,
modifier =
- Modifier.fillMaxWidth()
- .layoutId(QSMediaMeasurePolicy.LayoutId.Media),
+ Modifier.layoutId(QSMediaMeasurePolicy.LayoutId.Media),
carouselController = mediaCarouselController,
)
}
@@ -497,6 +504,13 @@
val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }
+ val mediaOffsetProvider = remember {
+ ShadeMediaOffsetProvider.Qs(
+ { @Suppress("UNUSED_EXPRESSION") tileSquishiness },
+ viewModel.qsSceneAdapter,
+ )
+ }
+
Box {
Box(
modifier =
@@ -570,11 +584,13 @@
squishiness = { tileSquishiness },
)
}
- MediaCarousel(
+
+ ShadeMediaCarousel(
isVisible = isMediaVisible,
mediaHost = mediaHost,
+ mediaOffsetProvider = mediaOffsetProvider,
modifier =
- Modifier.fillMaxWidth().thenIf(
+ Modifier.thenIf(
MediaContentPicker.shouldElevateMedia(layoutState)
) {
Modifier.zIndex(1f)
@@ -620,3 +636,25 @@
)
}
}
+
+@Composable
+private fun SceneScope.ShadeMediaCarousel(
+ isVisible: Boolean,
+ mediaHost: MediaHost,
+ carouselController: MediaCarouselController,
+ mediaOffsetProvider: ShadeMediaOffsetProvider,
+ modifier: Modifier = Modifier,
+) {
+ MediaCarousel(
+ modifier = modifier.fillMaxWidth(),
+ isVisible = isVisible,
+ mediaHost = mediaHost,
+ carouselController = carouselController,
+ offsetProvider =
+ if (MediaContentPicker.shouldElevateMedia(layoutState)) {
+ null
+ } else {
+ { mediaOffsetProvider.offset }
+ }
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 9da2a1b..5ffb6f8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -50,7 +50,7 @@
) {
val accessibilityTitle = stringResource(R.string.accessibility_volume_settings)
val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle()
- val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null)
+ val components by viewModel.componentsLayout.collectAsStateWithLifecycle()
with(VolumePanelComposeScope(state)) {
components?.let { componentsState ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
new file mode 100644
index 0000000..5eabd22
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.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.compose.animation.scene
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.SpringSpec
+import com.android.compose.animation.scene.content.state.ContentState
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+internal fun CoroutineScope.animateContent(
+ transition: ContentState.Transition<*>,
+ oneOffAnimation: OneOffAnimation,
+ targetProgress: Float,
+ startTransition: () -> Unit,
+ finishTransition: () -> Unit,
+) {
+ // Start the transition. This will compute the TransformationSpec associated to [transition],
+ // which we need to initialize the Animatable that will actually animate it.
+ startTransition()
+
+ // The transition now contains the transformation spec that we should use to instantiate the
+ // Animatable.
+ val animationSpec = transition.transformationSpec.progressSpec
+ val visibilityThreshold =
+ (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
+ val replacedTransition = transition.replacedTransition
+ val initialProgress = replacedTransition?.progress ?: 0f
+ val initialVelocity = replacedTransition?.progressVelocity ?: 0f
+ val animatable =
+ Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
+ oneOffAnimation.animatable = it
+ }
+
+ // Animate the progress to its target value.
+ //
+ // Important: We start atomically to make sure that we start the coroutine even if it is
+ // cancelled right after it is launched, so that finishTransition() is correctly called.
+ // Otherwise, this transition will never be stopped and we will never settle to Idle.
+ oneOffAnimation.job =
+ launch(start = CoroutineStart.ATOMIC) {
+ try {
+ animatable.animateTo(targetProgress, animationSpec, initialVelocity)
+ } finally {
+ finishTransition()
+ }
+ }
+}
+
+internal class OneOffAnimation {
+ /**
+ * The animatable used to animate this transition.
+ *
+ * Note: This is lateinit because we need to first create this object so that
+ * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to
+ * the transition, which is needed to initialize this Animatable.
+ */
+ lateinit var animatable: Animatable<Float, AnimationVector1D>
+
+ /** The job that is animating [animatable]. */
+ lateinit var job: Job
+
+ val progress: Float
+ get() = animatable.value
+
+ val progressVelocity: Float
+ get() = animatable.velocity
+
+ fun finish(): Job = job
+}
+
+// TODO(b/290184746): Compute a good default visibility threshold that depends on the layout size
+// and screen density.
+internal const val ProgressVisibilityThreshold = 1e-3f
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index e13ca391..ae5a84b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -120,7 +120,7 @@
}
@Deprecated(
- "Use animateSceneFloatAsState() instead",
+ "Use animateContentFloatAsState() instead",
replaceWith = ReplaceWith("animateContentFloatAsState(value, key, canOverflow)")
)
@Composable
@@ -171,7 +171,7 @@
}
@Deprecated(
- "Use animateSceneDpAsState() instead",
+ "Use animateContentDpAsState() instead",
replaceWith = ReplaceWith("animateContentDpAsState(value, key, canOverflow)")
)
@Composable
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 1fc1f98..68a6c98 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
@@ -16,15 +16,10 @@
package com.android.compose.animation.scene
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.SpringSpec
import com.android.compose.animation.scene.content.state.TransitionState
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
/**
* Transition to [target] using a canned animation. This function will try to be smart and take over
@@ -50,7 +45,7 @@
return when (transitionState) {
is TransitionState.Idle -> {
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
@@ -80,13 +75,11 @@
} else {
// The transition is in progress: start the canned animation at the same
// progress as it was in.
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
isInitiatedByUserInput,
- initialProgress = progress,
- initialVelocity = transitionState.progressVelocity,
replacedTransition = transitionState,
)
}
@@ -102,13 +95,11 @@
layoutState.finishTransition(transitionState, target)
null
} else {
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
isInitiatedByUserInput,
- initialProgress = progress,
- initialVelocity = transitionState.progressVelocity,
reversed = true,
replacedTransition = transitionState,
)
@@ -140,7 +131,7 @@
animateToScene(layoutState, animateFrom, transitionKey = null)
}
- animate(
+ animateToScene(
layoutState,
target,
transitionKey,
@@ -154,103 +145,68 @@
}
}
-private fun CoroutineScope.animate(
+private fun CoroutineScope.animateToScene(
layoutState: MutableSceneTransitionLayoutStateImpl,
targetScene: SceneKey,
transitionKey: TransitionKey?,
isInitiatedByUserInput: Boolean,
replacedTransition: TransitionState.Transition?,
- initialProgress: Float = 0f,
- initialVelocity: Float = 0f,
reversed: Boolean = false,
fromScene: SceneKey = layoutState.transitionState.currentScene,
chain: Boolean = true,
): TransitionState.Transition {
+ val oneOffAnimation = OneOffAnimation()
val targetProgress = if (reversed) 0f else 1f
val transition =
if (reversed) {
- OneOffTransition(
+ OneOffSceneTransition(
key = transitionKey,
fromScene = targetScene,
toScene = fromScene,
currentScene = targetScene,
isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = false,
replacedTransition = replacedTransition,
+ oneOffAnimation = oneOffAnimation,
)
} else {
- OneOffTransition(
+ OneOffSceneTransition(
key = transitionKey,
fromScene = fromScene,
toScene = targetScene,
currentScene = targetScene,
isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = false,
replacedTransition = replacedTransition,
+ oneOffAnimation = oneOffAnimation,
)
}
- // Change the current layout state to start this new transition. This will compute the
- // TransformationSpec associated to this transition, which we need to initialize the Animatable
- // that will actually animate it.
- layoutState.startTransition(transition, chain)
-
- // The transition now contains the transformation spec that we should use to instantiate the
- // Animatable.
- val animationSpec = transition.transformationSpec.progressSpec
- val visibilityThreshold =
- (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
- val animatable =
- Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
- transition.animatable = it
- }
-
- // Animate the progress to its target value.
- // Important: We start atomically to make sure that we start the coroutine even if it is
- // cancelled right after it is launched, so that finishTransition() is correctly called.
- // Otherwise, this transition will never be stopped and we will never settle to Idle.
- transition.job =
- launch(start = CoroutineStart.ATOMIC) {
- try {
- animatable.animateTo(targetProgress, animationSpec, initialVelocity)
- } finally {
- layoutState.finishTransition(transition, targetScene)
- }
- }
+ animateContent(
+ transition = transition,
+ oneOffAnimation = oneOffAnimation,
+ targetProgress = targetProgress,
+ startTransition = { layoutState.startTransition(transition, chain) },
+ finishTransition = { layoutState.finishTransition(transition, targetScene) },
+ )
return transition
}
-private class OneOffTransition(
+private class OneOffSceneTransition(
override val key: TransitionKey?,
fromScene: SceneKey,
toScene: SceneKey,
override val currentScene: SceneKey,
override val isInitiatedByUserInput: Boolean,
- override val isUserInputOngoing: Boolean,
replacedTransition: TransitionState.Transition?,
+ private val oneOffAnimation: OneOffAnimation,
) : TransitionState.Transition(fromScene, toScene, replacedTransition) {
- /**
- * The animatable used to animate this transition.
- *
- * Note: This is lateinit because we need to first create this Transition object so that
- * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to
- * it, which is need to initialize this Animatable.
- */
- lateinit var animatable: Animatable<Float, AnimationVector1D>
-
- /** The job that is animating [animatable]. */
- lateinit var job: Job
-
override val progress: Float
- get() = animatable.value
+ get() = oneOffAnimation.progress
override val progressVelocity: Float
- get() = animatable.velocity
+ get() = oneOffAnimation.progressVelocity
- override fun finish(): Job = job
+ override val isUserInputOngoing: Boolean = false
+
+ override fun finish(): Job = oneOffAnimation.finish()
}
-
-// TODO(b/290184746): Compute a good default visibility threshold that depends on the layout size
-// and screen density.
-internal const val ProgressVisibilityThreshold = 1e-3f
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 a43028a..712fe6b 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
@@ -459,38 +459,11 @@
animateTo(targetScene = targetScene, targetOffset = targetOffset)
} else {
- // We are doing an overscroll animation between scenes. In this case, we can also start
- // from the idle position.
-
- val startFromIdlePosition = swipeTransition.dragOffset == 0f
-
- if (startFromIdlePosition) {
- // If there is a target scene, we start the overscroll animation.
- val result = swipes.findUserActionResultStrict(velocity)
- if (result == null) {
- // We will not animate
- swipeTransition.snapToScene(fromScene.key)
- return 0f
- }
-
- val newSwipeTransition =
- SwipeTransition(
- layoutState = layoutState,
- coroutineScope = draggableHandler.coroutineScope,
- fromScene = fromScene,
- result = result,
- swipes = swipes,
- layoutImpl = draggableHandler.layoutImpl,
- orientation = draggableHandler.orientation,
- )
- .apply { _currentScene = swipeTransition._currentScene }
-
- updateTransition(newSwipeTransition)
- animateTo(targetScene = fromScene, targetOffset = 0f)
- } else {
- // We were between two scenes: animate to the initial scene.
- animateTo(targetScene = fromScene, targetOffset = 0f)
+ // We are doing an overscroll preview animation between scenes.
+ check(fromScene == swipeTransition._currentScene) {
+ "canChangeScene is false but currentScene != fromScene"
}
+ animateTo(targetScene = fromScene, targetOffset = 0f)
}
// The onStop animation consumes any remaining velocity.
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 a30b780..79b3856 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
@@ -17,6 +17,8 @@
package com.android.compose.animation.scene
import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
@@ -140,6 +142,7 @@
fun fractionRange(
start: Float? = null,
end: Float? = null,
+ easing: Easing = LinearEasing,
builder: PropertyTransformationBuilder.() -> Unit,
)
}
@@ -182,6 +185,7 @@
fun timestampRange(
startMillis: Int? = null,
endMillis: Int? = null,
+ easing: Easing = LinearEasing,
builder: PropertyTransformationBuilder.() -> Unit,
)
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 6515cb8..a63b19a 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
@@ -18,6 +18,7 @@
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.DurationBasedAnimationSpec
+import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.VectorConverter
@@ -163,9 +164,10 @@
override fun fractionRange(
start: Float?,
end: Float?,
+ easing: Easing,
builder: PropertyTransformationBuilder.() -> Unit
) {
- range = TransformationRange(start, end)
+ range = TransformationRange(start, end, easing)
builder()
range = null
}
@@ -251,6 +253,7 @@
override fun timestampRange(
startMillis: Int?,
endMillis: Int?,
+ easing: Easing,
builder: PropertyTransformationBuilder.() -> Unit
) {
if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) {
@@ -263,7 +266,7 @@
val start = startMillis?.let { it.toFloat() / durationMillis }
val end = endMillis?.let { it.toFloat() / durationMillis }
- fractionRange(start, end, builder)
+ fractionRange(start, end, easing, builder)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 77ec891..eda8ede 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -16,6 +16,8 @@
package com.android.compose.animation.scene.transformation
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.LinearEasing
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost
import androidx.compose.ui.util.fastCoerceIn
@@ -90,11 +92,13 @@
data class TransformationRange(
val start: Float,
val end: Float,
+ val easing: Easing,
) {
constructor(
start: Float? = null,
- end: Float? = null
- ) : this(start ?: BoundUnspecified, end ?: BoundUnspecified)
+ end: Float? = null,
+ easing: Easing = LinearEasing,
+ ) : this(start ?: BoundUnspecified, end ?: BoundUnspecified, easing)
init {
require(!start.isSpecified() || (start in 0f..1f))
@@ -103,17 +107,20 @@
}
/** Reverse this range. */
- fun reversed() = TransformationRange(start = reverseBound(end), end = reverseBound(start))
+ fun reversed() =
+ TransformationRange(start = reverseBound(end), end = reverseBound(start), easing = easing)
/** Get the progress of this range given the global [transitionProgress]. */
fun progress(transitionProgress: Float): Float {
- return when {
- start.isSpecified() && end.isSpecified() ->
- ((transitionProgress - start) / (end - start)).fastCoerceIn(0f, 1f)
- !start.isSpecified() && !end.isSpecified() -> transitionProgress
- end.isSpecified() -> (transitionProgress / end).fastCoerceAtMost(1f)
- else -> ((transitionProgress - start) / (1f - start)).fastCoerceAtLeast(0f)
- }
+ val progress =
+ when {
+ start.isSpecified() && end.isSpecified() ->
+ ((transitionProgress - start) / (end - start)).fastCoerceIn(0f, 1f)
+ !start.isSpecified() && !end.isSpecified() -> transitionProgress
+ end.isSpecified() -> (transitionProgress / end).fastCoerceAtMost(1f)
+ else -> ((transitionProgress - start) / (1f - start)).fastCoerceAtLeast(0f)
+ }
+ return easing.transform(progress)
}
private fun Float.isSpecified() = this != BoundUnspecified
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
index 68240b5..bed6cef 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.TweenSpec
import androidx.compose.animation.core.spring
@@ -107,6 +108,13 @@
fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
fractionRange(start = 0.2f) { fade(TestElements.Foo) }
fractionRange(end = 0.9f) { fade(TestElements.Foo) }
+ fractionRange(
+ start = 0.1f,
+ end = 0.8f,
+ easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ ) {
+ fade(TestElements.Foo)
+ }
}
}
@@ -118,6 +126,11 @@
TransformationRange(start = 0.1f, end = 0.8f),
TransformationRange(start = 0.2f, end = TransformationRange.BoundUnspecified),
TransformationRange(start = TransformationRange.BoundUnspecified, end = 0.9f),
+ TransformationRange(
+ start = 0.1f,
+ end = 0.8f,
+ CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ ),
)
}
@@ -130,6 +143,13 @@
timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
timestampRange(startMillis = 200) { fade(TestElements.Foo) }
timestampRange(endMillis = 400) { fade(TestElements.Foo) }
+ timestampRange(
+ startMillis = 100,
+ endMillis = 300,
+ easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ ) {
+ fade(TestElements.Foo)
+ }
}
}
@@ -141,6 +161,11 @@
TransformationRange(start = 100 / 500f, end = 300 / 500f),
TransformationRange(start = 200 / 500f, end = TransformationRange.BoundUnspecified),
TransformationRange(start = TransformationRange.BoundUnspecified, end = 400 / 500f),
+ TransformationRange(
+ start = 100 / 500f,
+ end = 300 / 500f,
+ easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ ),
)
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EasingTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EasingTest.kt
new file mode 100644
index 0000000..07901f2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EasingTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.compose.animation.scene.transformation
+
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.testTransition
+import com.android.compose.test.assertSizeIsEqualTo
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class EasingTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun testFractionRangeEasing() {
+ val easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Foo)) },
+ toSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Bar)) },
+ transition = {
+ // Scale during 4 frames.
+ spec = tween(16 * 4, easing = LinearEasing)
+ fractionRange(easing = easing) {
+ scaleSize(TestElements.Foo, width = 0f, height = 0f)
+ scaleSize(TestElements.Bar, width = 0f, height = 0f)
+ }
+ },
+ ) {
+ // Foo is entering, is 100dp x 100dp at rest and is scaled by (2, 0.5) during the
+ // transition so it starts at 200dp x 50dp.
+ before { onElement(TestElements.Bar).assertDoesNotExist() }
+ at(0) {
+ onElement(TestElements.Foo).assertSizeIsEqualTo(100.dp, 100.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(0.dp, 0.dp)
+ }
+ at(16) {
+ // 25% linear progress is mapped to 68.5% eased progress
+ onElement(TestElements.Foo).assertSizeIsEqualTo(31.5.dp, 31.5.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(68.5.dp, 68.5.dp)
+ }
+ at(32) {
+ // 50% linear progress is mapped to 89.5% eased progress
+ onElement(TestElements.Foo).assertSizeIsEqualTo(10.5.dp, 10.5.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(89.5.dp, 89.5.dp)
+ }
+ at(48) {
+ // 75% linear progress is mapped to 97.8% eased progress
+ onElement(TestElements.Foo).assertSizeIsEqualTo(2.2.dp, 2.2.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(97.8.dp, 97.8.dp)
+ }
+ after {
+ onElement(TestElements.Foo).assertDoesNotExist()
+ onElement(TestElements.Bar).assertSizeIsEqualTo(100.dp, 100.dp)
+ }
+ }
+ }
+
+ @Test
+ fun testTimestampRangeEasing() {
+ val easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Foo)) },
+ toSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Bar)) },
+ transition = {
+ // Scale during 4 frames.
+ spec = tween(16 * 4, easing = LinearEasing)
+ timestampRange(easing = easing) {
+ scaleSize(TestElements.Foo, width = 0f, height = 0f)
+ scaleSize(TestElements.Bar, width = 0f, height = 0f)
+ }
+ },
+ ) {
+ // Foo is entering, is 100dp x 100dp at rest and is scaled by (2, 0.5) during the
+ // transition so it starts at 200dp x 50dp.
+ before { onElement(TestElements.Bar).assertDoesNotExist() }
+ at(0) {
+ onElement(TestElements.Foo).assertSizeIsEqualTo(100.dp, 100.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(0.dp, 0.dp)
+ }
+ at(16) {
+ // 25% linear progress is mapped to 68.5% eased progress
+ onElement(TestElements.Foo).assertSizeIsEqualTo(31.5.dp, 31.5.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(68.5.dp, 68.5.dp)
+ }
+ at(32) {
+ // 50% linear progress is mapped to 89.5% eased progress
+ onElement(TestElements.Foo).assertSizeIsEqualTo(10.5.dp, 10.5.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(89.5.dp, 89.5.dp)
+ }
+ at(48) {
+ // 75% linear progress is mapped to 97.8% eased progress
+ onElement(TestElements.Foo).assertSizeIsEqualTo(2.2.dp, 2.2.dp)
+ onElement(TestElements.Bar).assertSizeIsEqualTo(97.8.dp, 97.8.dp)
+ }
+ after {
+ onElement(TestElements.Foo).assertDoesNotExist()
+ onElement(TestElements.Bar).assertSizeIsEqualTo(100.dp, 100.dp)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 9b725eb..954155d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -88,7 +88,6 @@
val clockBuffers: ClockMessageBuffers? = null,
val keepAllLoaded: Boolean,
subTag: String,
- var isTransitClockEnabled: Boolean = false,
val assert: ThreadAssert = ThreadAssert(),
) {
private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
@@ -188,10 +187,6 @@
var isClockListChanged = false
for (clock in plugin.getClocks()) {
val id = clock.clockId
- if (!isTransitClockEnabled && id == "DIGITAL_CLOCK_METRO") {
- continue
- }
-
val info =
availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) {
isClockListChanged = true
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 9d62e38..8721c78 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -182,9 +182,6 @@
const val FLAG_NAME_WALLPAPER_PICKER_UI_FOR_AIWP = "wallpaper_picker_ui_for_aiwp"
/** Flag denoting transit clock are enabled in wallpaper picker. */
- const val FLAG_NAME_TRANSIT_CLOCK = "lockscreen_custom_transit_clock"
-
- /** Flag denoting transit clock are enabled in wallpaper picker. */
const val FLAG_NAME_PAGE_TRANSITIONS = "wallpaper_picker_page_transitions"
/** Flag denoting adding apply button to wallpaper picker's grid preview page. */
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index b4c839f..7577147 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -32157,4 +32157,631 @@
column="6"/>
</issue>
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt"
+ line="154"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(ALWAYS_ON_DISPLAY_CONSTANTS_URI,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java"
+ line="164"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt"
+ line="61"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" awaitClose { resolver.unregisterContentObserver(observer) }"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt"
+ line="67"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mSettingObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="123"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mSettingObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="180"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="211"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+ line="219"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="46"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="48"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" resolver.unregisterContentObserver(allowedObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="54"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" resolver.unregisterContentObserver(onObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+ line="55"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(mQuickPickupGesture, false, this,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+ line="511"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(mPickupGesture, false, this, UserHandle.USER_ALL);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+ line="513"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(mAlwaysOnEnabled, false, this,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+ line="514"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="2470"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3077"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3168"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3957"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+ line="3961"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+ line="486"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+ line="492"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" contentResolver.unregisterContentObserver(settingsObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+ line="543"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt"
+ line="82"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" contentResolver.unregisterContentObserver(settingsObserver)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt"
+ line="100"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mMenuTargetFeaturesContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+ line="275"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+ line="276"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+ line="277"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+ line="200"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(Global.getUriFor("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+ line="202"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+ line="212"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" context.contentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt"
+ line="54"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="253"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="256"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="259"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="262"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mAssistContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+ line="295"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java"
+ line="395"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java"
+ line="400"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java"
+ line="3675"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mSettingsChangeObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java"
+ line="4705"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver(Settings.Global.getUriFor("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+ line="191"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+ line="205"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" resolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+ line="216"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java"
+ line="178"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java"
+ line="186"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java"
+ line="83"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mContentObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java"
+ line="100"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+ line="219"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContentResolver.unregisterContentObserver(mObserver);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+ line="241"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+ line="243"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+ line="1235"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+ line="1236"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mContext.getContentResolver().unregisterContentObserver(this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+ line="1240"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver()`"
+ errorLine1=" mResolver.unregisterContentObserver(this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+ line="377"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+ line="379"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="RegisterContentObserverViaContentResolver"
+ message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`<SettingsProxy>/<UserSettingsProxy>.registerContentObserver()`"
+ errorLine1=" mResolver.registerContentObserver("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+ line="381"
+ column="23"/>
+ </issue>
+
</issues>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 9b1d4ec..752c93e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -148,8 +148,6 @@
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
- private AuthDialogPanelInteractionDetector mPanelInteractionDetector;
- @Mock
private UserManager mUserManager;
@Mock
private LockPatternUtils mLockPatternUtils;
@@ -1059,10 +1057,9 @@
super(context, null /* applicationCoroutineScope */,
mExecution, mCommandQueue, mActivityTaskManager, mWindowManager,
mFingerprintManager, mFaceManager, () -> mUdfpsController, mDisplayManager,
- mWakefulnessLifecycle, mPanelInteractionDetector, mUserManager,
- mLockPatternUtils, () -> mUdfpsLogger, () -> mLogContextInteractor,
- () -> mPromptSelectionInteractor, () -> mCredentialViewModel,
- () -> mPromptViewModel, mInteractionJankMonitor,
+ mWakefulnessLifecycle, mUserManager, mLockPatternUtils, () -> mUdfpsLogger,
+ () -> mLogContextInteractor, () -> mPromptSelectionInteractor,
+ () -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper);
}
@@ -1071,7 +1068,6 @@
boolean requireConfirmation, int userId, int[] sensorIds,
String opPackageName, boolean skipIntro, long operationId, long requestId,
WakefulnessLifecycle wakefulnessLifecycle,
- AuthDialogPanelInteractionDetector panelInteractionDetector,
UserManager userManager,
LockPatternUtils lockPatternUtils, PromptViewModel viewModel) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
deleted file mode 100644
index cd9189b..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.systemui.biometrics;
-
-import static org.junit.Assert.assertEquals;
-
-import android.hardware.biometrics.ComponentInfoInternal;
-import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.biometrics.SensorProperties;
-import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-
-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;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
- @Test
- public void testUdfpsBottomSpacerHeightForPortrait() {
- final int displayHeightPx = 3000;
- final int navbarHeightPx = 10;
- final int dialogBottomMarginPx = 20;
- final int buttonBarHeightPx = 100;
- final int textIndicatorHeightPx = 200;
-
- final int sensorLocationX = 540;
- final int sensorLocationY = 1600;
- final int sensorRadius = 100;
-
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
- "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
- "00000001" /* serialNumber */, "" /* softwareVersion */));
- componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
- "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
- "vendor/version/revision" /* softwareVersion */));
-
- final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
- 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* halControlsIllumination */,
- true /* resetLockoutRequiresHardwareAuthToken */,
- List.of(new SensorLocationInternal("" /* displayId */,
- sensorLocationX, sensorLocationY, sensorRadius)));
-
- assertEquals(970,
- UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForPortrait(
- props, displayHeightPx, textIndicatorHeightPx, buttonBarHeightPx,
- dialogBottomMarginPx, navbarHeightPx, 1.0f /* resolutionScale */
- ));
- }
-
- @Test
- public void testUdfpsBottomSpacerHeightForLandscape_whenMoreSpaceAboveIcon() {
- final int titleHeightPx = 320;
- final int subtitleHeightPx = 240;
- final int descriptionHeightPx = 200;
- final int topSpacerHeightPx = 550;
- final int textIndicatorHeightPx = 190;
- final int buttonBarHeightPx = 160;
- final int navbarBottomInsetPx = 75;
-
- assertEquals(885,
- UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForLandscape(
- titleHeightPx, subtitleHeightPx, descriptionHeightPx, topSpacerHeightPx,
- textIndicatorHeightPx, buttonBarHeightPx, navbarBottomInsetPx));
- }
-
- @Test
- public void testUdfpsBottomSpacerHeightForLandscape_whenMoreSpaceBelowIcon() {
- final int titleHeightPx = 315;
- final int subtitleHeightPx = 160;
- final int descriptionHeightPx = 75;
- final int topSpacerHeightPx = 220;
- final int textIndicatorHeightPx = 290;
- final int buttonBarHeightPx = 360;
- final int navbarBottomInsetPx = 205;
-
- assertEquals(-85,
- UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForLandscape(
- titleHeightPx, subtitleHeightPx, descriptionHeightPx, topSpacerHeightPx,
- textIndicatorHeightPx, buttonBarHeightPx, navbarBottomInsetPx));
- }
-
- @Test
- public void testUdfpsHorizontalSpacerWidthForLandscape() {
- final int displayWidthPx = 3000;
- final int dialogMarginPx = 20;
- final int navbarHorizontalInsetPx = 75;
-
- final int sensorLocationX = 540;
- final int sensorLocationY = 1600;
- final int sensorRadius = 100;
-
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
- "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
- "00000001" /* serialNumber */, "" /* softwareVersion */));
- componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
- "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
- "vendor/version/revision" /* softwareVersion */));
-
- final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
- 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* halControlsIllumination */,
- true /* resetLockoutRequiresHardwareAuthToken */,
- List.of(new SensorLocationInternal("" /* displayId */,
- sensorLocationX, sensorLocationY, sensorRadius)));
-
- assertEquals(1205,
- UdfpsDialogMeasureAdapter.calculateHorizontalSpacerWidthForLandscape(
- props, displayWidthPx, dialogMarginPx, navbarHorizontalInsetPx,
- 1.0f /* resolutionScale */));
- }
-}
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 0242c2d..e57a4cb 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
@@ -1039,6 +1039,22 @@
assertThat(showCommunalFromOccluded).isTrue()
}
+ @Test
+ fun showCommunalFromOccluded_enteredOccludedFromDreaming() =
+ testScope.runTest {
+ kosmos.setCommunalAvailable(true)
+ val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
+ assertThat(showCommunalFromOccluded).isFalse()
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.OCCLUDED,
+ testScope
+ )
+
+ assertThat(showCommunalFromOccluded).isTrue()
+ }
+
private fun smartspaceTimer(id: String, timestamp: Long = 0L): CommunalSmartspaceTimer {
return CommunalSmartspaceTimer(
smartspaceTargetId = id,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
new file mode 100644
index 0000000..1f73347
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.education.domain.ui.view
+
+import android.content.applicationContext
+import android.widget.Toast
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.domain.interactor.contextualEducationInteractor
+import com.android.systemui.education.domain.interactor.keyboardTouchpadEduInteractor
+import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+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
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ContextualEduUiCoordinatorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val interactor = kosmos.contextualEducationInteractor
+ private lateinit var underTest: ContextualEduUiCoordinator
+ @Mock private lateinit var toast: Toast
+
+ @get:Rule val mockitoRule = MockitoJUnit.rule()
+
+ @Before
+ fun setUp() {
+ val viewModel =
+ ContextualEduViewModel(
+ kosmos.applicationContext.resources,
+ kosmos.keyboardTouchpadEduInteractor
+ )
+ underTest =
+ ContextualEduUiCoordinator(kosmos.applicationCoroutineScope, viewModel) { _ -> toast }
+ underTest.start()
+ kosmos.keyboardTouchpadEduInteractor.start()
+ }
+
+ @Test
+ @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+ fun showToastOnNewEdu() =
+ testScope.runTest {
+ triggerEducation(BACK)
+ runCurrent()
+ verify(toast).show()
+ }
+
+ private suspend fun triggerEducation(gestureType: GestureType) {
+ for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+ interactor.incrementSignalCount(gestureType)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 03647b9..6e76cbc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -205,6 +205,38 @@
}
@Test
+ fun transitionValue_badTransitionResetsTransitionValue() =
+ testScope.runTest {
+ resetTransitionValueReplayCache(setOf(AOD, DOZING, LOCKSCREEN))
+ val transitionValues by collectValues(underTest.transitionValue(state = DOZING))
+
+ val toSteps =
+ listOf(
+ TransitionStep(AOD, DOZING, 0f, STARTED),
+ TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+ )
+ toSteps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
+
+ // This is an intentionally bad sequence that will leave the transitionValue for
+ // DOZING in a bad place, since no CANCELED will be issued for DOZING
+ val fromSteps =
+ listOf(
+ TransitionStep(AOD, LOCKSCREEN, 0f, STARTED),
+ TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING),
+ TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED),
+ )
+ fromSteps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
+
+ assertThat(transitionValues).isEqualTo(listOf(0f, 0.5f, 0f))
+ }
+
+ @Test
fun transitionValue_canceled_toAnotherState() =
testScope.runTest {
resetTransitionValueReplayCache(setOf(AOD, GONE, LOCKSCREEN))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
index 4a10d80..8e4876d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
@@ -42,6 +42,28 @@
val underTest = kosmos.dozingToLockscreenTransitionViewModel
@Test
+ fun lockscreenAlpha() =
+ testScope.runTest {
+ val lockscreenAlpha by collectValues(underTest.lockscreenAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.1f))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(1f))
+ lockscreenAlpha.forEach { assertThat(it).isEqualTo(1f) }
+ }
+
+ @Test
+ fun shortcutsAlpha() =
+ testScope.runTest {
+ val shortcutsAlpha by collectValues(underTest.shortcutsAlpha)
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.5f))
+ repository.sendTransitionStep(step(1f))
+ assertThat(shortcutsAlpha[0]).isEqualTo(0f)
+ assertThat(shortcutsAlpha[1]).isEqualTo(1f)
+ }
+
+ @Test
fun deviceEntryParentViewShows() =
testScope.runTest {
val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
index d1f908d..46b370f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
@@ -16,16 +16,27 @@
package com.android.systemui.lifecycle
+import android.view.View
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.Assert
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -110,4 +121,45 @@
assertThat(isActive).isFalse()
}
+
+ @Test
+ fun viewModel_viewBinder() = runTest {
+ Assert.setTestThread(Thread.currentThread())
+
+ val view: View = mock { on { isAttachedToWindow } doReturn false }
+ val viewModel = FakeViewModel()
+ backgroundScope.launch {
+ view.viewModel(
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = { viewModel },
+ ) {
+ awaitCancellation()
+ }
+ }
+ runCurrent()
+
+ assertThat(viewModel.isActivated).isFalse()
+
+ view.stub { on { isAttachedToWindow } doReturn true }
+ argumentCaptor<View.OnAttachStateChangeListener>()
+ .apply { verify(view).addOnAttachStateChangeListener(capture()) }
+ .allValues
+ .forEach { it.onViewAttachedToWindow(view) }
+ runCurrent()
+
+ assertThat(viewModel.isActivated).isTrue()
+ }
+}
+
+private class FakeViewModel : SysUiViewModel() {
+ var isActivated = false
+
+ override suspend fun onActivated() {
+ isActivated = true
+ try {
+ awaitCancellation()
+ } finally {
+ isActivated = false
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
new file mode 100644
index 0000000..5a73fe2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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
+
+import android.graphics.drawable.TestStubDrawable
+import android.service.quicksettings.Tile
+import android.widget.Switch
+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
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
+import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
+import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AirplaneModeMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val airplaneModeConfig = kosmos.qsAirplaneModeTileConfig
+
+ private lateinit var mapper: AirplaneModeMapper
+
+ @Before
+ fun setup() {
+ mapper =
+ AirplaneModeMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(R.drawable.qs_airplane_icon_off, TestStubDrawable())
+ addOverride(R.drawable.qs_airplane_icon_on, TestStubDrawable())
+ }
+ .resources,
+ context.theme,
+ )
+ }
+
+ @Test
+ fun enabledModel_mapsCorrectly() {
+ val inputModel = AirplaneModeTileModel(true)
+
+ val outputState = mapper.map(airplaneModeConfig, inputModel)
+
+ val expectedState =
+ createAirplaneModeState(
+ QSTileState.ActivationState.ACTIVE,
+ context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_ACTIVE],
+ R.drawable.qs_airplane_icon_on
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun disabledModel_mapsCorrectly() {
+ val inputModel = AirplaneModeTileModel(false)
+
+ val outputState = mapper.map(airplaneModeConfig, inputModel)
+
+ val expectedState =
+ createAirplaneModeState(
+ QSTileState.ActivationState.INACTIVE,
+ context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_INACTIVE],
+ R.drawable.qs_airplane_icon_off
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ private fun createAirplaneModeState(
+ activationState: QSTileState.ActivationState,
+ secondaryLabel: String,
+ iconRes: Int
+ ): QSTileState {
+ val label = context.getString(R.string.airplane_mode)
+ return QSTileState(
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
+ label,
+ activationState,
+ secondaryLabel,
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ label,
+ null,
+ QSTileState.SideViewIcon.None,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
index b4ff565..f1d08c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
@@ -18,6 +18,7 @@
import android.app.IUriGrantsManager
import android.content.ComponentName
+import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.graphics.drawable.TestStubDrawable
@@ -51,11 +52,13 @@
class CustomTileMapperTest : SysuiTestCase() {
private val uriGrantsManager: IUriGrantsManager = mock {}
+ private val mockContext =
+ mock<Context> { whenever(createContextAsUser(any(), any())).thenReturn(context) }
private val kosmos =
testKosmos().apply { customTileSpec = TileSpec.Companion.create(TEST_COMPONENT) }
private val underTest by lazy {
CustomTileMapper(
- context = mock { whenever(createContextAsUser(any(), any())).thenReturn(context) },
+ context = mockContext,
uriGrantsManager = uriGrantsManager,
)
}
@@ -164,7 +167,7 @@
)
val expected =
createTileState(
- activationState = QSTileState.ActivationState.INACTIVE,
+ activationState = QSTileState.ActivationState.UNAVAILABLE,
icon = DEFAULT_DRAWABLE,
)
@@ -173,7 +176,7 @@
}
@Test
- fun failedToLoadIconTileIsInactive() =
+ fun failedToLoadIconTileIsUnavailable() =
with(kosmos) {
testScope.runTest {
val actual =
@@ -187,13 +190,32 @@
val expected =
createTileState(
icon = null,
- activationState = QSTileState.ActivationState.INACTIVE,
+ activationState = QSTileState.ActivationState.UNAVAILABLE,
)
assertThat(actual).isEqualTo(expected)
}
}
+ @Test
+ fun nullUserContextDoesNotCauseExceptionReturnsNullIconAndUnavailableState() =
+ with(kosmos) {
+ testScope.runTest {
+ // map() will catch this exception
+ whenever(mockContext.createContextAsUser(any(), any()))
+ .thenThrow(IllegalStateException("Unable to create userContext"))
+
+ val actual = underTest.map(customTileQsTileConfig, createModel())
+
+ val expected =
+ createTileState(
+ icon = null,
+ activationState = QSTileState.ActivationState.UNAVAILABLE,
+ )
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
private fun Kosmos.createModel(
tileState: Int = Tile.STATE_ACTIVE,
tileIcon: Icon = createIcon(DRAWABLE, false),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 13d6411..1ea8abc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -204,7 +204,7 @@
val actualIcon = latest?.icon
assertThat(actualIcon).isEqualTo(expectedIcon)
- assertThat(latest?.iconId).isNull()
+ assertThat(latest?.iconId).isEqualTo(WifiIcons.WIFI_NO_INTERNET_ICONS[4])
assertThat(latest?.contentDescription.loadContentDescription(context))
.isEqualTo("$internet,test ssid")
val expectedSd = wifiIcon.contentDescription
@@ -443,15 +443,15 @@
* on the mentioned context. Since that context does not have a looper assigned to it, the
* handler instantiation will throw a RuntimeException.
*
- * TODO(b/338068066): Robolectric behavior differs in that it does not throw the exception
- * So either we should make Robolectric behvase similar to the device test, or change this
- * test to look for a different signal than the exception, when run by Robolectric. For now
- * we just assume the test is not Robolectric.
+ * TODO(b/338068066): Robolectric behavior differs in that it does not throw the exception So
+ * either we should make Robolectric behave similar to the device test, or change this test to
+ * look for a different signal than the exception, when run by Robolectric. For now we just
+ * assume the test is not Robolectric.
*/
@Test(expected = java.lang.RuntimeException::class)
fun mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException() =
testScope.runTest {
- assumeFalse(isRobolectricTest());
+ assumeFalse(isRobolectricTest())
collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
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 f26c39d..bbb467f 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
@@ -32,6 +32,7 @@
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.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
@@ -109,6 +110,7 @@
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
+ private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
private val sysUiState = kosmos.sysUiState
private val falsingCollector = mock<FalsingCollector>().also { kosmos.falsingCollector = it }
private val fakeSceneDataSource = kosmos.fakeSceneDataSource
@@ -228,6 +230,32 @@
}
@Test
+ fun hydrateVisibility_basedOnAlternateBouncer() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+ prepareState(
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ )
+
+ underTest.start()
+ assertThat(isVisible).isTrue()
+
+ // WHEN the device is occluded,
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ true,
+ mock()
+ )
+ // THEN scenes are not visible
+ assertThat(isVisible).isFalse()
+
+ // WHEN the alternate bouncer is visible
+ kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true)
+ // THEN scenes visible
+ assertThat(isVisible).isTrue()
+ }
+
+ @Test
fun startsInLockscreenScene() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -275,6 +303,66 @@
}
@Test
+ fun switchFromLockscreenToGoneAndHideAltBouncerWhenDeviceUnlocked() =
+ testScope.runTest {
+ val alternateBouncerVisible by
+ collectLastValue(bouncerRepository.alternateBouncerVisible)
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+
+ bouncerRepository.setAlternateVisible(true)
+ assertThat(alternateBouncerVisible).isTrue()
+
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ )
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ assertThat(alternateBouncerVisible).isFalse()
+ }
+
+ @Test
+ fun stayOnCurrentSceneAndHideAltBouncerWhenDeviceUnlocked_whenLeaveOpenShade() =
+ testScope.runTest {
+ val alternateBouncerVisible by
+ collectLastValue(bouncerRepository.alternateBouncerVisible)
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+
+ kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
+ bouncerRepository.setAlternateVisible(true)
+ assertThat(alternateBouncerVisible).isTrue()
+
+ val transitionState =
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ )
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+ runCurrent()
+
+ sceneInteractor.changeScene(Scenes.QuickSettings, "switching to qs for test")
+ transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings)
+ runCurrent()
+ assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
+
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
+ assertThat(alternateBouncerVisible).isFalse()
+ }
+
+ @Test
fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked_whenLeaveOpenShade() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
index ab184ab..f232d52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.volume.panel.domain.unavailableCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.ui.composable.enabledComponents
+import com.android.systemui.volume.shared.volumePanelLogger
import com.google.common.truth.Truth.assertThat
import javax.inject.Provider
import kotlinx.coroutines.test.runTest
@@ -49,6 +50,7 @@
enabledComponents,
{ defaultCriteria },
testScope.backgroundScope,
+ volumePanelLogger,
criteriaByKey,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index 420b955..51a70bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -24,21 +24,30 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.testKosmos
+import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory
import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository
import com.android.systemui.volume.panel.domain.interactor.criteriaByKey
+import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
import com.android.systemui.volume.panel.domain.unavailableCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.shared.model.mockVolumePanelUiComponentProvider
import com.android.systemui.volume.panel.ui.composable.componentByKey
import com.android.systemui.volume.panel.ui.layout.DefaultComponentsLayoutManager
import com.android.systemui.volume.panel.ui.layout.componentsLayoutManager
+import com.android.systemui.volume.shared.volumePanelLogger
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -55,6 +64,7 @@
volumePanelGlobalStateRepository.updateVolumePanelState { it.copy(isVisible = true) }
}
+ private val realDumpManager = DumpManager()
private val testableResources = context.orCreateTestableResources
private lateinit var underTest: VolumePanelViewModel
@@ -124,6 +134,60 @@
}
@Test
+ fun testDumpableRegister_unregister() =
+ with(kosmos) {
+ testScope.runTest {
+ val job = launch {
+ applicationCoroutineScope = this
+ underTest = createViewModel()
+
+ runCurrent()
+
+ assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME })
+ .isTrue()
+ }
+
+ runCurrent()
+ job.cancel()
+
+ assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME }).isTrue()
+ }
+ }
+
+ @Test
+ fun testDumpingState() =
+ test({
+ componentByKey =
+ mapOf(
+ COMPONENT_1 to mockVolumePanelUiComponentProvider,
+ COMPONENT_2 to mockVolumePanelUiComponentProvider,
+ BOTTOM_BAR to mockVolumePanelUiComponentProvider,
+ )
+ criteriaByKey = mapOf(COMPONENT_2 to Provider { unavailableCriteria })
+ }) {
+ testScope.runTest {
+ runCurrent()
+
+ StringWriter().use {
+ underTest.dump(PrintWriter(it), emptyArray())
+
+ assertThat(it.buffer.toString())
+ .isEqualTo(
+ "volumePanelState=" +
+ "VolumePanelState(orientation=1, isLargeScreen=false)\n" +
+ "componentsLayout=( " +
+ "headerComponents= " +
+ "contentComponents=" +
+ "test_component:1:visible=true, " +
+ "test_component:2:visible=false " +
+ "footerComponents= " +
+ "bottomBarComponent=test_bottom_bar:visible=true )\n"
+ )
+ }
+ }
+ }
+
+ @Test
fun dismissBroadcast_dismissesPanel() = test {
testScope.runTest {
runCurrent() // run the flows to let allow the receiver to be registered
@@ -140,11 +204,26 @@
private fun test(setup: Kosmos.() -> Unit = {}, test: Kosmos.() -> Unit) =
with(kosmos) {
setup()
- underTest = volumePanelViewModel
+ underTest = createViewModel()
+
test()
}
+ private fun Kosmos.createViewModel(): VolumePanelViewModel =
+ VolumePanelViewModel(
+ context.orCreateTestableResources.resources,
+ applicationCoroutineScope,
+ volumePanelComponentFactory,
+ configurationController,
+ broadcastDispatcher,
+ realDumpManager,
+ volumePanelLogger,
+ volumePanelGlobalStateInteractor,
+ )
+
private companion object {
+ const val DUMPABLE_NAME = "VolumePanelViewModel"
+
const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar"
const val COMPONENT_1: VolumePanelComponentKey = "test_component:1"
const val COMPONENT_2: VolumePanelComponentKey = "test_component:2"
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml b/packages/SystemUI/res/color/audio_sharing_btn_text_color.xml
similarity index 61%
copy from libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
copy to packages/SystemUI/res/color/audio_sharing_btn_text_color.xml
index 4bd9ca0..b72835e 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
+++ b/packages/SystemUI/res/color/audio_sharing_btn_text_color.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+<?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.
@@ -15,8 +14,8 @@
~ limitations under the License.
-->
-<network-security-config>
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="true">localhost</domain>
- </domain-config>
-</network-security-config>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_activated="true"
+ android:color="@color/qs_dialog_btn_filled_text_color" />
+ <item android:color="@color/qs_dialog_btn_outline_text" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml b/packages/SystemUI/res/drawable/audio_sharing_btn_background.xml
similarity index 67%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
rename to packages/SystemUI/res/drawable/audio_sharing_btn_background.xml
index 4bd9ca0..310e095 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
+++ b/packages/SystemUI/res/drawable/audio_sharing_btn_background.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,8 +15,7 @@
~ limitations under the License.
-->
-<network-security-config>
- <domain-config cleartextTrafficPermitted="true">
- <domain includeSubdomains="true">localhost</domain>
- </domain-config>
-</network-security-config>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_activated="true" android:drawable="@drawable/qs_dialog_btn_filled" />
+ <item android:drawable="@drawable/qs_dialog_btn_outline" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_bugreport.xml b/packages/SystemUI/res/drawable/ic_bugreport.xml
index ed1c6c7..badbd88 100644
--- a/packages/SystemUI/res/drawable/ic_bugreport.xml
+++ b/packages/SystemUI/res/drawable/ic_bugreport.xml
@@ -19,14 +19,14 @@
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?attr/colorControlNormal">
+ android:tint="?android:attr/colorControlNormal">
<path
- android:fillColor="#FF000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M20,10V8h-2.81c-0.45,-0.78 -1.07,-1.46 -1.82,-1.96L17,4.41L15.59,3l-2.17,2.17c-0.03,-0.01 -0.05,-0.01 -0.08,-0.01c-0.16,-0.04 -0.32,-0.06 -0.49,-0.09c-0.06,-0.01 -0.11,-0.02 -0.17,-0.03C12.46,5.02 12.23,5 12,5h0c-0.49,0 -0.97,0.07 -1.42,0.18l0.02,-0.01L8.41,3L7,4.41l1.62,1.63l0.01,0C7.88,6.54 7.26,7.22 6.81,8H4v2h2.09C6.03,10.33 6,10.66 6,11v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3h0c2.22,0 4.15,-1.21 5.19,-3H20v-2h-2.09l0,0c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1l0,0H20zM16,15c0,2.21 -1.79,4 -4,4c-2.21,0 -4,-1.79 -4,-4v-4c0,-2.21 1.79,-4 4,-4h0c2.21,0 4,1.79 4,4V15z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M10,14h4v2h-4z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M10,10h4v2h-4z"/>
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
deleted file mode 100644
index ff89ed9..0000000
--- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml
+++ /dev/null
@@ -1,208 +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.
- -->
-<com.android.systemui.biometrics.ui.BiometricPromptLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/contents"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <ImageView
- android:id="@+id/logo"
- android:layout_width="@dimen/biometric_auth_icon_size"
- android:layout_height="@dimen/biometric_auth_icon_size"
- android:layout_gravity="center"
- android:scaleType="fitXY"/>
-
- <TextView
- android:id="@+id/logo_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:singleLine="true"
- android:marqueeRepeatLimit="1"
- android:ellipsize="marquee"/>
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:singleLine="true"
- android:marqueeRepeatLimit="1"
- android:ellipsize="marquee"
- style="@style/TextAppearance.AuthCredential.OldTitle"/>
-
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:singleLine="true"
- android:marqueeRepeatLimit="1"
- android:ellipsize="marquee"
- style="@style/TextAppearance.AuthCredential.OldSubtitle"/>
-
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:scrollbars ="vertical"
- android:importantForAccessibility="no"
- style="@style/TextAppearance.AuthCredential.OldDescription"/>
-
- <Space
- android:id="@+id/space_above_content"
- android:layout_width="match_parent"
- android:layout_height="24dp"
- android:visibility="gone" />
-
- <LinearLayout
- android:id="@+id/customized_view_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:fadeScrollbars="false"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:scrollbars="vertical"
- android:visibility="gone" />
-
- <Space android:id="@+id/space_above_icon"
- android:layout_width="match_parent"
- android:layout_height="48dp" />
-
- <FrameLayout
- android:id="@+id/biometric_icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center">
-
- <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
- android:id="@+id/biometric_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY" />
-
- <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
- android:id="@+id/biometric_icon_overlay"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:contentDescription="@null"
- android:scaleType="fitXY" />
- </FrameLayout>
-
- <!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra
- padding so that the biometric icon is always in the right physical position. -->
- <Space android:id="@+id/space_below_icon"
- android:layout_width="match_parent"
- android:layout_height="12dp" />
-
- <TextView
- android:id="@+id/indicator"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingHorizontal="24dp"
- android:textSize="12sp"
- android:gravity="center_horizontal"
- android:accessibilityLiveRegion="polite"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:scrollHorizontally="true"
- android:fadingEdge="horizontal"
- android:textColor="@color/biometric_dialog_gray"/>
-
- <LinearLayout
- android:id="@+id/button_bar"
- android:layout_width="match_parent"
- android:layout_height="88dp"
- style="?android:attr/buttonBarStyle"
- android:orientation="horizontal"
- android:paddingTop="24dp">
-
- <Space android:id="@+id/leftSpacer"
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:visibility="visible" />
-
- <!-- Negative Button, reserved for app -->
- <Button android:id="@+id/button_negative"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_gravity="center_vertical"
- android:ellipsize="end"
- android:maxLines="2"
- android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
- android:visibility="gone"/>
- <!-- Cancel Button, replaces negative button when biometric is accepted -->
- <Button android:id="@+id/button_cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_gravity="center_vertical"
- android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
- android:text="@string/cancel"
- android:visibility="gone"/>
- <!-- "Use Credential" Button, replaces if device credential is allowed -->
- <Button android:id="@+id/button_use_credential"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_gravity="center_vertical"
- android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
- android:visibility="gone"/>
-
- <Space android:id="@+id/middleSpacer"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:visibility="visible"/>
-
- <!-- Positive Button -->
- <Button android:id="@+id/button_confirm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_gravity="center_vertical"
- android:ellipsize="end"
- android:maxLines="2"
- android:maxWidth="@dimen/biometric_dialog_button_positive_max_width"
- android:text="@string/biometric_dialog_confirm"
- android:visibility="gone"/>
- <!-- Try Again Button -->
- <Button android:id="@+id/button_try_again"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_gravity="center_vertical"
- android:ellipsize="end"
- android:maxLines="2"
- android:maxWidth="@dimen/biometric_dialog_button_positive_max_width"
- android:text="@string/biometric_dialog_try_again"
- android:visibility="gone"/>
-
- <Space android:id="@+id/rightSpacer"
- android:layout_width="8dp"
- android:layout_height="match_parent"
- android:visibility="visible" />
- </LinearLayout>
-
-</com.android.systemui.biometrics.ui.BiometricPromptLayout>
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 4535f67..b9314c7 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -41,6 +41,7 @@
android:textDirection="locale"
android:textAlignment="gravity"
android:paddingStart="20dp"
+ android:paddingEnd="10dp"
android:paddingTop="15dp"
android:maxLines="1"
android:ellipsize="end"
@@ -56,6 +57,7 @@
android:id="@+id/bluetooth_device_summary"
style="@style/BluetoothTileDialog.DeviceSummary"
android:paddingStart="20dp"
+ android:paddingEnd="10dp"
android:paddingBottom="15dp"
android:maxLines="1"
android:ellipsize="end"
@@ -73,11 +75,20 @@
android:orientation="vertical"/>
<View
+ android:id="@+id/divider"
+ android:layout_width="1dp"
+ android:layout_height="38dp"
+ app:layout_constraintStart_toEndOf="@+id/guideline"
+ app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <View
android:id="@+id/gear_icon"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
- app:layout_constraintStart_toEndOf="@+id/guideline"
+ app:layout_constraintStart_toEndOf="@+id/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index 27b8006..b4eaa40 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -258,7 +258,7 @@
<Button
android:id="@+id/audio_sharing_button"
- style="@style/Widget.Dialog.Button.BorderButton"
+ style="@style/BluetoothTileDialog.AudioSharingButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="9dp"
@@ -270,7 +270,6 @@
android:text="@string/quick_settings_bluetooth_audio_sharing_button"
android:drawableStart="@drawable/ic_bt_le_audio_sharing_18dp"
android:drawablePadding="10dp"
- android:drawableTint="?android:attr/textColorPrimary"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintEnd_toStartOf="@+id/done_button"
diff --git a/packages/SystemUI/res/layout/media_projection_app_selector.xml b/packages/SystemUI/res/layout/media_projection_app_selector.xml
index b77f78d..3b6a64f 100644
--- a/packages/SystemUI/res/layout/media_projection_app_selector.xml
+++ b/packages/SystemUI/res/layout/media_projection_app_selector.xml
@@ -37,6 +37,7 @@
android:background="@*android:drawable/bottomsheet_background">
<ImageView
+ android:id="@+id/media_projection_app_selector_icon"
android:layout_width="@dimen/media_projection_app_selector_icon_size"
android:layout_height="@dimen/media_projection_app_selector_icon_size"
android:layout_marginTop="@*android:dimen/chooser_edge_margin_normal"
diff --git a/packages/SystemUI/res/raw/action_key_edu.json b/packages/SystemUI/res/raw/action_key_edu.json
new file mode 100644
index 0000000..014d837
--- /dev/null
+++ b/packages/SystemUI/res/raw/action_key_edu.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":181,"w":554,"h":564,"nm":"Trackpad-JSON_ActionKey-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"BlankButton","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"actionKey_themed","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0.288,-0.035,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.579,-0.158],[0.605,0],[1.21,1.21],[0,1.684],[-1.184,1.184],[-1.684,0],[-1.21,-1.21],[0,-1.71],[0.184,-0.553],[0.316,-0.474],[0,0],[0,0]],"o":[[-0.474,0.316],[-0.553,0.158],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.184],[0,0.605],[-0.158,0.553],[0,0],[0,0],[0,0]],"v":[[10.241,12.155],[8.663,12.866],[6.926,13.103],[2.585,11.287],[0.809,6.946],[2.585,2.605],[6.926,0.789],[11.307,2.605],[13.122,6.946],[12.846,8.682],[12.136,10.222],[16.911,14.997],[15.017,16.891]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.184],[-1.684,0],[-1.184,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0],[1.21,1.184],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,11.327],[-16.911,6.985],[-15.096,2.605],[-10.754,0.789],[-6.374,2.605],[-4.558,6.985],[-6.374,11.327],[-10.754,13.142]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[-8.268,9.432],[-7.242,6.985],[-8.268,4.499],[-10.754,3.473],[-13.201,4.499],[-14.188,6.985],[-13.201,9.432],[-10.754,10.419]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[9.413,9.432],[10.439,6.985],[9.413,4.499],[6.926,3.473],[4.479,4.499],[3.453,6.985],[4.479,9.432],[6.926,10.419]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0],[1.21,1.21],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,-6.354],[-16.911,-10.695],[-15.096,-15.076],[-10.754,-16.891],[-6.374,-15.076],[-4.558,-10.695],[-6.374,-6.354],[-10.754,-4.539]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0]],"v":[[2.585,-6.354],[0.77,-10.695],[2.585,-15.076],[6.926,-16.891],[11.307,-15.076],[13.122,-10.695],[11.307,-6.354],[6.926,-4.539]],"c":true}},"nm":"Path 6","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[-8.268,-8.248],[-7.242,-10.695],[-8.268,-13.182],[-10.754,-14.208],[-13.201,-13.182],[-14.188,-10.695],[-13.201,-8.248],[-10.754,-7.222]],"c":true}},"nm":"Path 7","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[9.413,-8.248],[10.439,-10.695],[9.413,-13.182],[6.926,-14.208],[4.479,-13.182],[3.453,-10.695],[4.479,-8.248],[6.926,-7.222]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"icon","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.33],"y":[0.52]},"t":34,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.36],"y":[0]},"t":37,"s":[100]},{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":46,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.33],"y":[0.52]},"t":124,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.36],"y":[0]},"t":127,"s":[100]},{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":130,"s":[100]},{"t":136,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.92549020052,0.752941191196,0.423529416323,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.072,"y":0.635},"o":{"x":0.424,"y":0.112},"t":27,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.313,"y":0.131},"t":39,"s":[40,49.21,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.07,"y":0.63},"o":{"x":0.42,"y":0.11},"t":117,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.313,"y":0.131},"t":129,"s":[40,49.21,0],"to":[0,0,0],"ti":[0,0,0]},{"t":147,"s":[40,39.79,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"AllApps_Tray_themed","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-8.859,0]},"a":{"a":0,"k":[277,256.562,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-129.938,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[275.625,25.594]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"560x52","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-151.594,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[15.75,1.969]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"handle","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":45,"s":[277,516,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.214,"y":0.214},"t":75,"s":[277,265.422,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.999,"y":1},"o":{"x":0.3,"y":0},"t":135,"s":[277,265.422,0],"to":[0,0,0],"ti":[0,0,0]},{"t":147,"s":[277,516,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[316.969,320.906]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":13.78},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"all apps","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"AK_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Scale Down","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":51,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":135,"s":[50]},{"t":144,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.153,0.153,0.153],"y":[0.074,0.074,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.841,0.841,0.841],"y":[1,1,1]},"o":{"x":[0.161,0.161,0.161],"y":[0,0,0]},"t":75,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":135,"s":[95,95,100]},{"t":165,"s":[100,100,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"shapes":[],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[133,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[229,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[421,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"actionKey_themed","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[325,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"AllApps_Tray_themed","tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"AK_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/action_key_success.json b/packages/SystemUI/res/raw/action_key_success.json
new file mode 100644
index 0000000..cae7344
--- /dev/null
+++ b/packages/SystemUI/res/raw/action_key_success.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":50,"w":554,"h":564,"nm":"Trackpad-JSON_ActionKey-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}]},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[95.049,95.049,100]}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}]},"p":{"a":0,"k":[81,127,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":-0.289},"p":{"a":0,"k":[14.364,-33.591,0]},"a":{"a":0,"k":[-0.125,0,0]},"s":{"a":0,"k":[104.744,104.744,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":11},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[95,95,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":88},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Checkbox - Widget","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"actionKey_themed-static","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0.288,-0.035,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.579,-0.158],[0.605,0],[1.21,1.21],[0,1.684],[-1.184,1.184],[-1.684,0],[-1.21,-1.21],[0,-1.71],[0.184,-0.553],[0.316,-0.474],[0,0],[0,0]],"o":[[-0.474,0.316],[-0.553,0.158],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.184],[0,0.605],[-0.158,0.553],[0,0],[0,0],[0,0]],"v":[[10.241,12.155],[8.663,12.866],[6.926,13.103],[2.585,11.287],[0.809,6.946],[2.585,2.605],[6.926,0.789],[11.307,2.605],[13.122,6.946],[12.846,8.682],[12.136,10.222],[16.911,14.997],[15.017,16.891]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.184],[-1.684,0],[-1.184,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0],[1.21,1.184],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,11.327],[-16.911,6.985],[-15.096,2.605],[-10.754,0.789],[-6.374,2.605],[-4.558,6.985],[-6.374,11.327],[-10.754,13.142]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[-8.268,9.432],[-7.242,6.985],[-8.268,4.499],[-10.754,3.473],[-13.201,4.499],[-14.188,6.985],[-13.201,9.432],[-10.754,10.419]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[9.413,9.432],[10.439,6.985],[9.413,4.499],[6.926,3.473],[4.479,4.499],[3.453,6.985],[4.479,9.432],[6.926,10.419]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0],[1.21,1.21],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,-6.354],[-16.911,-10.695],[-15.096,-15.076],[-10.754,-16.891],[-6.374,-15.076],[-4.558,-10.695],[-6.374,-6.354],[-10.754,-4.539]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0]],"v":[[2.585,-6.354],[0.77,-10.695],[2.585,-15.076],[6.926,-16.891],[11.307,-15.076],[13.122,-10.695],[11.307,-6.354],[6.926,-4.539]],"c":true}},"nm":"Path 6","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[-8.268,-8.248],[-7.242,-10.695],[-8.268,-13.182],[-10.754,-14.208],[-13.201,-13.182],[-14.188,-10.695],[-13.201,-8.248],[-10.754,-7.222]],"c":true}},"nm":"Path 7","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[9.413,-8.248],[10.439,-10.695],[9.413,-13.182],[6.926,-14.208],[4.479,-13.182],[3.453,-10.695],[4.479,-8.248],[6.926,-7.222]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"icon","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.92549020052,0.752941191196,0.423529416323,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"BlankButton","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"AK_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Scale Down","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":51,"s":[70]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":135,"s":[70]},{"t":144,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.153,0.153,0.153],"y":[0.074,0.074,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.841,0.841,0.841],"y":[1,1,1]},"o":{"x":[0.161,0.161,0.161],"y":[0,0,0]},"t":75,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":135,"s":[95,95,100]},{"t":165,"s":[100,100,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"shapes":[],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,198.5,0]},"a":{"a":0,"k":[95,95,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":190,"h":190,"ip":6,"op":50,"st":6,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"actionKey_themed-static","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[325,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[421,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[229,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[133,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"AK_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e4f900d..25bca25 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -278,4 +278,7 @@
<!-- Id for the udfps accessibility overlay -->
<item type="id" name="udfps_accessibility_overlay" />
<item type="id" name="udfps_accessibility_overlay_top_guideline" />
+
+ <!-- Ids for communal hub widgets -->
+ <item type="id" name="communal_widget_disposable_tag"/>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8751596..f8303ea 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -280,13 +280,19 @@
<!-- For updated Screen Recording permission dialog (i.e. with PSS)-->
<!-- Title for the screen prompting the user to begin recording their screen [CHAR LIMIT=NONE]-->
- <string name="screenrecord_permission_dialog_title">Start Recording?</string>
+ <string name="screenrecord_permission_dialog_title">Record your screen?</string>
+ <!-- Screen recording permission option for recording just a single app [CHAR LIMIT=50] -->
+ <string name="screenrecord_permission_dialog_option_text_single_app">Record one app</string>
+ <!-- Screen recording permission option for recording the whole screen [CHAR LIMIT=50] -->
+ <string name="screenrecord_permission_dialog_option_text_entire_screen">Record entire screen</string>
<!-- Message reminding the user that sensitive information may be captured during a full screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
- <string name="screenrecord_permission_dialog_warning_entire_screen">While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen">When you’re recording your entire screen, anything shown on your screen is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
<!-- Message reminding the user that sensitive information may be captured during a single app screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
- <string name="screenrecord_permission_dialog_warning_single_app">While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
- <!-- Button to start a screen recording in the updated screen record dialog that allows to select an app to record [CHAR LIMIT=50]-->
- <string name="screenrecord_permission_dialog_continue">Start recording</string>
+ <string name="screenrecord_permission_dialog_warning_single_app">When you’re recording an app, anything shown or played in that app is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <!-- Button to start a screen recording of the entire screen in the updated screen record dialog that allows to select an app to record [CHAR LIMIT=50]-->
+ <string name="screenrecord_permission_dialog_continue_entire_screen">Record screen</string>
+ <!-- Title of the activity that allows to select an app to screen record [CHAR LIMIT=70] -->
+ <string name="screenrecord_app_selector_title">Choose app to record</string>
<!-- Label for a switch to enable recording audio [CHAR LIMIT=NONE]-->
<string name="screenrecord_audio_label">Record audio</string>
@@ -971,8 +977,8 @@
<string name="hearing_devices_presets_error">Couldn\'t update preset</string>
<!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]-->
<string name="hearing_devices_preset_label">Preset</string>
- <!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40]-->
- <string name="live_caption_title">Live Caption</string>
+ <!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40] [BACKUP_MESSAGE_ID=8916875614623730005]-->
+ <string name="quick_settings_hearing_devices_live_caption_title">Live Caption</string>
<!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
<string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
@@ -1358,12 +1364,8 @@
<!-- Media projection permission dialog warning text for system services. [CHAR LIMIT=NONE] -->
<string name="media_projection_sys_service_dialog_warning">The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.</string>
- <!-- Permission dropdown option for sharing or recording the whole screen. [CHAR LIMIT=30] -->
- <string name="screen_share_permission_dialog_option_entire_screen">Entire screen</string>
- <!-- Permission dropdown option for sharing or recording single app. [CHAR LIMIT=30] -->
- <string name="screen_share_permission_dialog_option_single_app">A single app</string>
- <!-- Title of the dialog that allows to select an app to share or record [CHAR LIMIT=NONE] -->
- <string name="screen_share_permission_app_selector_title">Share or record an app</string>
+ <!-- Title of the activity that allows users to select an app to share or record [CHAR LIMIT=NONE] -->
+ <string name="screen_share_generic_app_selector_title">Share or record an app</string>
<!-- Media projection that launched from 1P/3P apps -->
<!-- 1P/3P app media projection permission dialog title. [CHAR LIMIT=NONE] -->
@@ -1381,6 +1383,8 @@
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen">Share screen</string>
<!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
<string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
+ <!-- Title of the activity that allows users to select an app to share to a 1P/3P app [CHAR LIMIT=70] -->
+ <string name="media_projection_entry_share_app_selector_title">Choose app to share</string>
<!-- Casting that launched by SysUI (i.e. when there is no app name) -->
<!-- System casting media projection permission dialog title. [CHAR LIMIT=100] -->
@@ -1395,6 +1399,8 @@
<string name="media_projection_entry_cast_permission_dialog_warning_single_app">When you’re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
<!-- System casting media projection permission button to continue for SysUI casting. [CHAR LIMIT=60] -->
<string name="media_projection_entry_cast_permission_dialog_continue_entire_screen">Cast screen</string>
+ <!-- Title of the activity that allows users to select an app to cast [CHAR LIMIT=70] -->
+ <string name="media_projection_entry_cast_app_selector_title">Choose app to cast</string>
<!-- Other sharing (not recording nor casting) that launched by SysUI (currently not in use) -->
<!-- System sharing media projection permission dialog title. [CHAR LIMIT=100] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7fa7088..3ef6243 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1455,6 +1455,12 @@
<item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
</style>
+ <style name="BluetoothTileDialog.AudioSharingButton" parent="Widget.Dialog.Button">
+ <item name="android:background">@drawable/audio_sharing_btn_background</item>
+ <item name="android:textColor">@color/audio_sharing_btn_text_color</item>
+ <item name="android:drawableTint">@color/audio_sharing_btn_text_color</item>
+ </style>
+
<style name="BroadcastDialog">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 12d881b..c0b6acf 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics
import android.Manifest
+import android.app.ActivityTaskManager
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
@@ -35,6 +36,7 @@
import android.hardware.biometrics.SensorPropertiesInternal
import android.os.UserManager
import android.util.DisplayMetrics
+import android.util.Log
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowManager
@@ -45,6 +47,8 @@
import com.android.systemui.biometrics.shared.model.PromptKind
object Utils {
+ private const val TAG = "SysUIBiometricUtils"
+
/** Base set of layout flags for fingerprint overlay widgets. */
const val FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
@@ -148,4 +152,39 @@
draw(canvas)
return bitmap
}
+
+ // LINT.IfChange
+ @JvmStatic
+ /**
+ * Checks if a client package is running in the background or it's a system app.
+ *
+ * @param clientPackage The name of the package to be checked.
+ * @param clientClassNameIfItIsConfirmDeviceCredentialActivity The class name of
+ * ConfirmDeviceCredentialActivity.
+ * @return Whether the client package is running in background
+ */
+ fun ActivityTaskManager.isSystemAppOrInBackground(
+ context: Context,
+ clientPackage: String,
+ clientClassNameIfItIsConfirmDeviceCredentialActivity: String?
+ ): Boolean {
+ Log.v(TAG, "Checking if the authenticating is in background, clientPackage:$clientPackage")
+ val tasks = getTasks(Int.MAX_VALUE)
+ if (tasks == null || tasks.isEmpty()) {
+ Log.w(TAG, "No running tasks reported")
+ return false
+ }
+
+ val topActivity = tasks[0].topActivity
+ val isSystemApp = isSystem(context, clientPackage)
+ val topPackageEqualsToClient = topActivity!!.packageName == clientPackage
+ val isClientConfirmDeviceCredentialActivity =
+ clientClassNameIfItIsConfirmDeviceCredentialActivity != null
+ // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on
+ // class name.
+ return !(isSystemApp || topPackageEqualsToClient) ||
+ (isClientConfirmDeviceCredentialActivity &&
+ topActivity.className != clientClassNameIfItIsConfirmDeviceCredentialActivity)
+ }
+ // LINT.ThenChange(frameworks/base/services/core/java/com/android/server/biometrics/Utils.java)
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 5d804cc..6209ed8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -172,5 +172,10 @@
*/
oneway void onImeSwitcherLongPress() = 57;
- // Next id = 58
+ /**
+ * Updates contextual education stats when target gesture type is triggered.
+ */
+ oneway void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) = 58;
+
+ // Next id = 59
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 68d2eb3..41ad437 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -26,6 +26,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -105,8 +106,8 @@
* @return updated set of flags from InputMethodService based off {@param oldHints}
* Leaves original hints unmodified
*/
- public static int calculateBackDispositionHints(int oldHints, int backDisposition,
- boolean imeShown, boolean showImeSwitcher) {
+ public static int calculateBackDispositionHints(int oldHints,
+ @BackDispositionMode int backDisposition, boolean imeShown, boolean showImeSwitcher) {
int hints = oldHints;
switch (backDisposition) {
case InputMethodService.BACK_DISPOSITION_DEFAULT:
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 317201d..f358ba2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -125,6 +125,7 @@
taskbarMarginLeft, taskbarMarginBottom, floatingRotationButtonPositionLeft);
final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
+ mKeyButtonView.setDiameter(diameter);
mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
taskbarMarginBottom));
}
@@ -195,6 +196,7 @@
public void updateIcon(int lightIconColor, int darkIconColor) {
mAnimatedDrawable = (AnimatedVectorDrawable) mKeyButtonView.getContext()
.getDrawable(mRotationButtonController.getIconResId());
+ mAnimatedDrawable.setBounds(0, 0, mKeyButtonView.getWidth(), mKeyButtonView.getHeight());
mKeyButtonView.setImageDrawable(mAnimatedDrawable);
mKeyButtonView.setColors(lightIconColor, darkIconColor);
}
@@ -248,8 +250,14 @@
updateDimensionResources();
if (mIsShowing) {
+ updateIcon(mRotationButtonController.getLightIconColor(),
+ mRotationButtonController.getDarkIconColor());
final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.reset();
+ mAnimatedDrawable.start();
+ }
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index 2145166..75412f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -37,6 +37,7 @@
private static final float BACKGROUND_ALPHA = 0.92f;
private KeyButtonRipple mRipple;
+ private int mDiameter;
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final Configuration mLastConfiguration;
@@ -93,10 +94,25 @@
mRipple.setDarkIntensity(darkIntensity);
}
+ /**
+ * Sets the view's diameter.
+ *
+ * @param diameter the diameter value for the view
+ */
+ void setDiameter(int diameter) {
+ mDiameter = diameter;
+ }
+
@Override
public void draw(Canvas canvas) {
int d = Math.min(getWidth(), getHeight());
canvas.drawOval(0, 0, d, d, mOvalBgPaint);
super.draw(canvas);
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(mDiameter, mDiameter);
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 781f6dd..831543d 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -74,7 +74,6 @@
clockBuffers,
/* keepAllLoaded = */ false,
/* subTag = */ "System",
- /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK),
new ThreadAssert());
registry.registerListeners();
return registry;
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e055e7c..9b5d5b6 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -66,6 +66,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.util.Preconditions;
import com.android.settingslib.Utils;
import com.android.systemui.biometrics.data.repository.FacePropertyRepository;
@@ -168,7 +169,7 @@
ViewGroup mScreenDecorHwcWindow;
@VisibleForTesting
ScreenDecorHwcLayer mScreenDecorHwcLayer;
- private WindowManager mWindowManager;
+ private ViewCaptureAwareWindowManager mWindowManager;
private int mRotation;
private UserSettingObserver mColorInversionSetting;
@Nullable
@@ -338,7 +339,8 @@
ScreenDecorationsLogger logger,
FacePropertyRepository facePropertyRepository,
JavaAdapter javaAdapter,
- CameraProtectionLoader cameraProtectionLoader) {
+ CameraProtectionLoader cameraProtectionLoader,
+ ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
mContext = context;
mSecureSettings = secureSettings;
mCommandRegistry = commandRegistry;
@@ -353,6 +355,7 @@
mLogger = logger;
mFacePropertyRepository = facePropertyRepository;
mJavaAdapter = javaAdapter;
+ mWindowManager = viewCaptureAwareWindowManager;
}
private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
@@ -484,7 +487,6 @@
private void startOnScreenDecorationsThread() {
Trace.beginSection("ScreenDecorations#startOnScreenDecorationsThread");
- mWindowManager = mContext.getSystemService(WindowManager.class);
mContext.getDisplay().getDisplayInfo(mDisplayInfo);
mRotation = mDisplayInfo.rotation;
mDisplaySize.x = mDisplayInfo.getNaturalWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 5924149..f4a1f05 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -29,6 +29,7 @@
import androidx.annotation.MainThread;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -54,6 +55,7 @@
private Context mContext;
private final WindowManager mWindowManager;
+ private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final DisplayManager mDisplayManager;
private final AccessibilityManager mAccessibilityManager;
@@ -97,6 +99,7 @@
@Inject
public AccessibilityFloatingMenuController(Context context,
WindowManager windowManager,
+ ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
DisplayManager displayManager,
AccessibilityManager accessibilityManager,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
@@ -106,6 +109,7 @@
DisplayTracker displayTracker) {
mContext = context;
mWindowManager = windowManager;
+ mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mDisplayManager = displayManager;
mAccessibilityManager = accessibilityManager;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
@@ -187,7 +191,7 @@
final Context windowContext = mContext.createWindowContext(defaultDisplay,
TYPE_NAVIGATION_BAR_PANEL, /* options= */ null);
mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager,
- mAccessibilityManager, mSecureSettings);
+ mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings);
}
mFloatingMenu.show();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index 6b1240b..623536f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -23,6 +23,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.util.settings.SecureSettings;
/**
@@ -30,13 +31,14 @@
* of {@link IAccessibilityFloatingMenu}.
*/
class MenuViewLayerController implements IAccessibilityFloatingMenu {
- private final WindowManager mWindowManager;
+ private final ViewCaptureAwareWindowManager mWindowManager;
private final MenuViewLayer mMenuViewLayer;
private boolean mIsShowing;
MenuViewLayerController(Context context, WindowManager windowManager,
+ ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
AccessibilityManager accessibilityManager, SecureSettings secureSettings) {
- mWindowManager = windowManager;
+ mWindowManager = viewCaptureAwareWindowManager;
MenuViewModel menuViewModel = new MenuViewModel(
context, accessibilityManager, secureSettings);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 083f1db..d08653c3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -228,7 +228,7 @@
mHearingDeviceItemList = getHearingDevicesList();
if (mPresetsController != null) {
activeHearingDevice = getActiveHearingDevice(mHearingDeviceItemList);
- mPresetsController.setActiveHearingDevice(activeHearingDevice);
+ mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
} else {
activeHearingDevice = null;
}
@@ -336,7 +336,7 @@
}
final CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice(
mHearingDeviceItemList);
- mPresetsController.setActiveHearingDevice(activeHearingDevice);
+ mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
mPresetInfoAdapter = new ArrayAdapter<>(dialog.getContext(),
R.layout.hearing_devices_preset_spinner_selected,
@@ -499,7 +499,8 @@
final List<ResolveInfo> resolved = packageManager.queryIntentActivities(LIVE_CAPTION_INTENT,
/* flags= */ 0);
if (!resolved.isEmpty()) {
- return new ToolItem(context.getString(R.string.live_caption_title),
+ return new ToolItem(
+ context.getString(R.string.quick_settings_hearing_devices_live_caption_title),
context.getDrawable(R.drawable.ic_volume_odi_captions),
LIVE_CAPTION_INTENT);
}
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 f81124e..aa95fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -113,7 +113,7 @@
@Override
public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
@@ -137,7 +137,7 @@
@Override
public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
@@ -177,22 +177,33 @@
}
/**
- * Sets the hearing device for this controller to control the preset.
+ * Sets the hearing device for this controller to control the preset if it supports
+ * {@link HapClientProfile}.
*
* @param activeHearingDevice the {@link CachedBluetoothDevice} need to be hearing aid device
+ * and support {@link HapClientProfile}.
*/
- public void setActiveHearingDevice(CachedBluetoothDevice activeHearingDevice) {
- mActiveHearingDevice = activeHearingDevice;
+ public void setHearingDeviceIfSupportHap(CachedBluetoothDevice activeHearingDevice) {
+ if (mHapClientProfile == null || activeHearingDevice == null) {
+ mActiveHearingDevice = null;
+ return;
+ }
+ if (activeHearingDevice.getProfiles().stream().anyMatch(
+ profile -> profile instanceof HapClientProfile)) {
+ mActiveHearingDevice = activeHearingDevice;
+ } else {
+ mActiveHearingDevice = null;
+ }
}
/**
* Selects the currently active preset for {@code mActiveHearingDevice} individual device or
- * the device group accoridng to whether it supports synchronized presets or not.
+ * the device group according to whether it supports synchronized presets or not.
*
* @param presetIndex an index of one of the available presets
*/
public void selectPreset(int presetIndex) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
mSelectedPresetIndex = presetIndex;
@@ -217,7 +228,7 @@
* @return a list of all known preset info
*/
public List<BluetoothHapPresetInfo> getAllPresetInfo() {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return emptyList();
}
return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
@@ -230,14 +241,14 @@
* @return active preset index
*/
public int getActivePresetIndex() {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
}
return mHapClientProfile.getActivePresetIndex(mActiveHearingDevice.getDevice());
}
private void selectPresetSynchronously(int groupId, int presetIndex) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
@@ -250,7 +261,7 @@
}
private void selectPresetIndependently(int presetIndex) {
- if (mActiveHearingDevice == null) {
+ if (mActiveHearingDevice == null || mHapClientProfile == null) {
return;
}
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 9521be1..723587e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,11 +17,9 @@
package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
-import static com.android.systemui.Flags.constraintBp;
import android.animation.Animator;
import android.annotation.IntDef;
@@ -30,8 +28,6 @@
import android.app.AlertDialog;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
@@ -41,17 +37,11 @@
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.UserManager;
import android.util.Log;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -60,7 +50,6 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.ScrollView;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
@@ -74,7 +63,6 @@
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
import com.android.systemui.biometrics.shared.model.BiometricModalities;
import com.android.systemui.biometrics.shared.model.PromptKind;
-import com.android.systemui.biometrics.ui.BiometricPromptLayout;
import com.android.systemui.biometrics.ui.CredentialView;
import com.android.systemui.biometrics.ui.binder.BiometricViewBinder;
import com.android.systemui.biometrics.ui.binder.BiometricViewSizeBinder;
@@ -111,7 +99,6 @@
private static final int ANIMATION_DURATION_SHOW_MS = 250;
private static final int ANIMATION_DURATION_AWAY_MS = 350;
- private static final int ANIMATE_CREDENTIAL_START_DELAY_MS = 300;
private static final int STATE_UNKNOWN = 0;
private static final int STATE_ANIMATING_IN = 1;
@@ -136,13 +123,11 @@
private final Config mConfig;
private final int mEffectiveUserId;
- private final Handler mHandler;
private final IBinder mWindowToken = new Binder();
private final WindowManager mWindowManager;
private final Interpolator mLinearOutSlowIn;
private final LockPatternUtils mLockPatternUtils;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final AuthDialogPanelInteractionDetector mPanelInteractionDetector;
private final InteractionJankMonitor mInteractionJankMonitor;
private final CoroutineScope mApplicationCoroutineScope;
@@ -159,10 +144,7 @@
private final AuthPanelController mPanelController;
private final ViewGroup mLayout;
private final ImageView mBackgroundView;
- private final ScrollView mBiometricScrollView;
private final View mPanelView;
- private final List<FingerprintSensorPropertiesInternal> mFpProps;
- private final List<FaceSensorPropertiesInternal> mFaceProps;
private final float mTranslationY;
@VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
private final Set<Integer> mFailedModalities = new HashSet<Integer>();
@@ -229,13 +211,7 @@
@Override
public void onUseDeviceCredential() {
mConfig.mCallback.onDeviceCredentialPressed(getRequestId());
- if (constraintBp()) {
- addCredentialView(false /* animatePanel */, true /* animateContents */);
- } else {
- mHandler.postDelayed(() -> {
- addCredentialView(false /* animatePanel */, true /* animateContents */);
- }, mConfig.mSkipAnimation ? 0 : ANIMATE_CREDENTIAL_START_DELAY_MS);
- }
+ addCredentialView(false /* animatePanel */, true /* animateContents */);
// TODO(b/313469218): Remove Config
mConfig.mPromptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
@@ -303,36 +279,12 @@
@Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@Nullable List<FaceSensorPropertiesInternal> faceProps,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
- @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
- @NonNull UserManager userManager,
- @NonNull LockPatternUtils lockPatternUtils,
- @NonNull InteractionJankMonitor jankMonitor,
- @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractor,
- @NonNull PromptViewModel promptViewModel,
- @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
- @NonNull @Background DelayableExecutor bgExecutor,
- @NonNull VibratorHelper vibratorHelper) {
- this(config, applicationCoroutineScope, fpProps, faceProps,
- wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
- jankMonitor, promptSelectorInteractor, promptViewModel,
- credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor,
- vibratorHelper);
- }
-
- @VisibleForTesting
- AuthContainerView(@NonNull Config config,
- @NonNull CoroutineScope applicationCoroutineScope,
- @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
- @Nullable List<FaceSensorPropertiesInternal> faceProps,
- @NonNull WakefulnessLifecycle wakefulnessLifecycle,
- @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
@NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
@NonNull PromptViewModel promptViewModel,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
- @NonNull Handler mainHandler,
@NonNull @Background DelayableExecutor bgExecutor,
@NonNull VibratorHelper vibratorHelper) {
super(config.mContext);
@@ -340,10 +292,8 @@
mConfig = config;
mLockPatternUtils = lockPatternUtils;
mEffectiveUserId = userManager.getCredentialOwnerProfile(mConfig.mUserId);
- mHandler = mainHandler;
mWindowManager = mContext.getSystemService(WindowManager.class);
mWakefulnessLifecycle = wakefulnessLifecycle;
- mPanelInteractionDetector = panelInteractionDetector;
mApplicationCoroutineScope = applicationCoroutineScope;
mPromptViewModel = promptViewModel;
@@ -352,8 +302,6 @@
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mBiometricCallback = new BiometricCallback();
- mFpProps = fpProps;
- mFaceProps = faceProps;
final BiometricModalities biometricModalities = new BiometricModalities(
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
@@ -367,7 +315,7 @@
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
final PromptKind kind = mPromptViewModel.getPromptKind().getValue();
- if (constraintBp() && kind.isBiometric()) {
+ if (kind.isBiometric()) {
if (kind.isTwoPaneLandscapeBiometric()) {
mLayout = (ConstraintLayout) layoutInflater.inflate(
R.layout.biometric_prompt_two_pane_layout, this, false /* attachToRoot */);
@@ -379,26 +327,16 @@
mLayout = (FrameLayout) layoutInflater.inflate(
R.layout.auth_container_view, this, false /* attachToRoot */);
}
- mBiometricScrollView = mLayout.findViewById(R.id.biometric_scrollview);
addView(mLayout);
mBackgroundView = mLayout.findViewById(R.id.background);
mPanelView = mLayout.findViewById(R.id.panel);
- if (!constraintBp()) {
- final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- android.R.attr.colorBackgroundFloating});
- mPanelView.setBackgroundColor(ta.getColor(0, Color.WHITE));
- ta.recycle();
- }
mPanelController = new AuthPanelController(mContext, mPanelView);
mBackgroundExecutor = bgExecutor;
mInteractionJankMonitor = jankMonitor;
mCredentialViewModelProvider = credentialViewModelProvider;
- showPrompt(config, layoutInflater, promptViewModel,
- Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
- Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds),
- vibratorHelper);
+ showPrompt(promptViewModel, vibratorHelper);
// TODO: De-dupe the logic with AuthCredentialPasswordView
setOnKeyListener((v, keyCode, event) -> {
@@ -415,52 +353,25 @@
requestFocus();
}
- private void showPrompt(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
- @NonNull PromptViewModel viewModel,
- @Nullable FingerprintSensorPropertiesInternal fpProps,
- @Nullable FaceSensorPropertiesInternal faceProps,
- @NonNull VibratorHelper vibratorHelper
- ) {
+ private void showPrompt(@NonNull PromptViewModel viewModel,
+ @NonNull VibratorHelper vibratorHelper) {
if (mPromptViewModel.getPromptKind().getValue().isBiometric()) {
- addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
+ addBiometricView(viewModel, vibratorHelper);
} else if (mPromptViewModel.getPromptKind().getValue().isCredential()) {
- if (constraintBp()) {
- addCredentialView(true, false);
- }
+ addCredentialView(true, false);
} else {
mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
}
}
- private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
- @NonNull PromptViewModel viewModel,
- @Nullable FingerprintSensorPropertiesInternal fpProps,
- @Nullable FaceSensorPropertiesInternal faceProps,
+ private void addBiometricView(@NonNull PromptViewModel viewModel,
@NonNull VibratorHelper vibratorHelper) {
-
- if (constraintBp()) {
- mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null,
- // TODO(b/201510778): This uses the wrong timeout in some cases
- getJankListener(mLayout, TRANSIT,
- BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
- mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
- vibratorHelper);
- } else {
- final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
- R.layout.biometric_prompt_layout, null, false);
- mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
- // TODO(b/201510778): This uses the wrong timeout in some cases
- getJankListener(view, TRANSIT,
- BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
- mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
- vibratorHelper);
-
- // TODO(b/251476085): migrate these dependencies
- if (fpProps != null && fpProps.isAnyUdfpsType()) {
- view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps),
- config.mScaleProvider);
- }
- }
+ mBiometricView = BiometricViewBinder.bind(mLayout, viewModel,
+ // TODO(b/201510778): This uses the wrong timeout in some cases
+ getJankListener(mLayout, TRANSIT,
+ BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
+ mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
+ vibratorHelper);
}
@VisibleForTesting
@@ -524,9 +435,6 @@
@Override
public void onOrientationChanged() {
- if (!constraintBp()) {
- updatePositionByCapability(true /* invalidate */);
- }
}
@Override
@@ -538,23 +446,6 @@
}
mWakefulnessLifecycle.addObserver(this);
- if (constraintBp()) {
- // Do nothing on attachment with constraintLayout
- } else if (mPromptViewModel.getPromptKind().getValue().isBiometric()) {
- mBiometricScrollView.addView(mBiometricView.asView());
- } else if (mPromptViewModel.getPromptKind().getValue().isCredential()) {
- addCredentialView(true /* animatePanel */, false /* animateContents */);
- } else {
- throw new IllegalStateException("Unknown configuration: "
- + mConfig.mPromptInfo.getAuthenticators());
- }
-
- if (!constraintBp()) {
- mPanelInteractionDetector.enable(
- () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
- updatePositionByCapability(false /* invalidate */);
- }
-
if (mConfig.mSkipIntro) {
mContainerState = STATE_SHOWING;
} else {
@@ -618,120 +509,8 @@
};
}
- private void updatePositionByCapability(boolean forceInvalidate) {
- final FingerprintSensorPropertiesInternal fpProp = Utils.findFirstSensorProperties(
- mFpProps, mConfig.mSensorIds);
- final FaceSensorPropertiesInternal faceProp = Utils.findFirstSensorProperties(
- mFaceProps, mConfig.mSensorIds);
- if (fpProp != null && fpProp.isAnyUdfpsType()) {
- maybeUpdatePositionForUdfps(forceInvalidate /* invalidate */);
- }
- if (faceProp != null && mBiometricView != null && mBiometricView.isFaceOnly()) {
- alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */);
- }
- if (fpProp != null && fpProp.sensorType == TYPE_POWER_BUTTON) {
- alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */);
- }
- }
-
- private static boolean shouldUpdatePositionForUdfps(@NonNull View view) {
- if (view instanceof BiometricPromptLayout) {
- // this will force the prompt to align itself on the edge of the screen
- // instead of centering (temporary workaround to prevent small implicit view
- // from breaking due to the way gravity / margins are set in the legacy
- // AuthPanelController
- return true;
- }
-
- return false;
- }
-
- private boolean maybeUpdatePositionForUdfps(boolean invalidate) {
- final Display display = getDisplay();
- if (display == null) {
- return false;
- }
-
- final DisplayInfo cachedDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(cachedDisplayInfo);
- if (mBiometricView == null || !shouldUpdatePositionForUdfps(mBiometricView.asView())) {
- return false;
- }
-
- final int displayRotation = cachedDisplayInfo.rotation;
- switch (displayRotation) {
- case Surface.ROTATION_0:
- mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
- setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
- break;
-
- case Surface.ROTATION_90:
- mPanelController.setPosition(AuthPanelController.POSITION_RIGHT);
- setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
- break;
-
- case Surface.ROTATION_270:
- mPanelController.setPosition(AuthPanelController.POSITION_LEFT);
- setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
- break;
-
- case Surface.ROTATION_180:
- default:
- Log.e(TAG, "Unsupported display rotation: " + displayRotation);
- mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
- setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
- break;
- }
-
- if (invalidate) {
- mPanelView.invalidateOutline();
- }
-
- return true;
- }
-
- private boolean alwaysUpdatePositionAtScreenBottom(boolean invalidate) {
- final Display display = getDisplay();
- if (display == null) {
- return false;
- }
- if (mBiometricView == null || !shouldUpdatePositionForUdfps(mBiometricView.asView())) {
- return false;
- }
-
- final int displayRotation = display.getRotation();
- switch (displayRotation) {
- case Surface.ROTATION_0:
- case Surface.ROTATION_90:
- case Surface.ROTATION_270:
- case Surface.ROTATION_180:
- mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
- setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
- break;
- default:
- Log.e(TAG, "Unsupported display rotation: " + displayRotation);
- mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
- setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
- break;
- }
-
- if (invalidate) {
- mPanelView.invalidateOutline();
- }
-
- return true;
- }
-
- private void setScrollViewGravity(int gravity) {
- final FrameLayout.LayoutParams params =
- (FrameLayout.LayoutParams) mBiometricScrollView.getLayoutParams();
- params.gravity = gravity;
- mBiometricScrollView.setLayoutParams(params);
- }
-
@Override
public void onDetachedFromWindow() {
- mPanelInteractionDetector.disable();
OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
if (dispatcher != null) {
findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
@@ -834,6 +613,11 @@
}
@Override
+ public String getClassNameIfItIsConfirmDeviceCredentialActivity() {
+ return mConfig.mPromptInfo.getClassNameIfItIsConfirmDeviceCredentialActivity();
+ }
+
+ @Override
public long getRequestId() {
return mConfig.mRequestId;
}
@@ -878,7 +662,7 @@
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
- if (Flags.customBiometricPrompt() && constraintBp()) {
+ if (Flags.customBiometricPrompt()) {
// TODO(b/288175645): resetPrompt calls should be lifecycle aware
mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b466f31..037f5b7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
@@ -173,7 +172,6 @@
@NonNull private final SparseBooleanArray mSfpsEnrolledForUser;
@NonNull private final SensorPrivacyManager mSensorPrivacyManager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final AuthDialogPanelInteractionDetector mPanelInteractionDetector;
private boolean mAllFingerprintAuthenticatorsRegistered;
@NonNull private final UserManager mUserManager;
@NonNull private final LockPatternUtils mLockPatternUtils;
@@ -187,7 +185,7 @@
final TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onTaskStackChanged() {
- if (!isOwnerInForeground()) {
+ if (isOwnerInBackground()) {
mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground);
}
}
@@ -227,21 +225,20 @@
}
}
- private boolean isOwnerInForeground() {
+ private boolean isOwnerInBackground() {
if (mCurrentDialog != null) {
final String clientPackage = mCurrentDialog.getOpPackageName();
- final List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(clientPackage)
- && !Utils.isSystem(mContext, clientPackage)) {
- Log.w(TAG, "Evicting client due to: " + topPackage);
- return false;
- }
+ final String clientClassNameIfItIsConfirmDeviceCredentialActivity =
+ mCurrentDialog.getClassNameIfItIsConfirmDeviceCredentialActivity();
+ final boolean isInBackground = Utils.isSystemAppOrInBackground(mActivityTaskManager,
+ mContext, clientPackage,
+ clientClassNameIfItIsConfirmDeviceCredentialActivity);
+ if (isInBackground) {
+ Log.w(TAG, "Evicting client due to top activity is not : " + clientPackage);
}
+ return isInBackground;
}
- return true;
+ return false;
}
private void cancelIfOwnerIsNotInForeground() {
@@ -728,7 +725,6 @@
Provider<UdfpsController> udfpsControllerFactory,
@NonNull DisplayManager displayManager,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
- @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull Lazy<UdfpsLogger> udfpsLogger,
@@ -779,7 +775,6 @@
});
mWakefulnessLifecycle = wakefulnessLifecycle;
- mPanelInteractionDetector = panelInteractionDetector;
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
@@ -1229,7 +1224,6 @@
operationId,
requestId,
mWakefulnessLifecycle,
- mPanelInteractionDetector,
mUserManager,
mLockPatternUtils,
viewModel);
@@ -1259,9 +1253,9 @@
}
mCurrentDialog = newDialog;
- // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+ // TODO(b/353597496): We should check whether |allowBackgroundAuthentication| should be
// removed.
- if (!promptInfo.isAllowBackgroundAuthentication() && !isOwnerInForeground()) {
+ if (!promptInfo.isAllowBackgroundAuthentication() && isOwnerInBackground()) {
cancelIfOwnerIsNotInForeground();
} else {
mCurrentDialog.show(mWindowManager);
@@ -1306,7 +1300,6 @@
PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds,
String opPackageName, boolean skipIntro, long operationId, long requestId,
@NonNull WakefulnessLifecycle wakefulnessLifecycle,
- @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull PromptViewModel viewModel) {
@@ -1323,7 +1316,7 @@
config.mSensorIds = sensorIds;
config.mScaleProvider = this::getScaleFactor;
return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
- wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
+ wakefulnessLifecycle, userManager, lockPatternUtils,
mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index 3fd488c..8611916 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -94,6 +94,12 @@
*/
String getOpPackageName();
+ /**
+ * Get the class name of ConfirmDeviceCredentialActivity. Returns null if the direct caller is
+ * not ConfirmDeviceCredentialActivity.
+ */
+ String getClassNameIfItIsConfirmDeviceCredentialActivity();
+
/** The requestId of the underlying operation within the framework. */
long getRequestId();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
deleted file mode 100644
index 04c2351..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ /dev/null
@@ -1,68 +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.biometrics
-
-import android.annotation.MainThread
-import android.util.Log
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import dagger.Lazy
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
-
-class AuthDialogPanelInteractionDetector
-@Inject
-constructor(
- @Application private val scope: CoroutineScope,
- private val shadeInteractorLazy: Lazy<ShadeInteractor>,
-) {
- private var shadeExpansionCollectorJob: Job? = null
-
- @MainThread
- fun enable(onShadeInteraction: Runnable) {
- if (shadeExpansionCollectorJob != null) {
- Log.e(TAG, "Already enabled")
- return
- }
- //TODO(b/313957306) delete this check
- if (shadeInteractorLazy.get().isUserInteracting.value) {
- // Workaround for b/311266890. This flow is in an error state that breaks this.
- Log.e(TAG, "isUserInteracting already true, skipping enable")
- return
- }
- shadeExpansionCollectorJob =
- scope.launch {
- Log.i(TAG, "Enable detector")
- // wait for it to emit true once
- shadeInteractorLazy.get().isUserInteracting.first { it }
- Log.i(TAG, "Detector detected shade interaction")
- onShadeInteraction.run()
- }
- shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
- }
-
- @MainThread
- fun disable() {
- Log.i(TAG, "Disable detector")
- shadeExpansionCollectorJob?.cancel()
- }
-}
-
-private const val TAG = "AuthDialogPanelInteractionDetector"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
deleted file mode 100644
index 02eae9ced..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * 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.systemui.biometrics;
-
-import android.annotation.IdRes;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.os.Build;
-import android.util.Log;
-import android.view.Surface;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-import android.widget.FrameLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.res.R;
-
-/**
- * Adapter that remeasures an auth dialog view to ensure that it matches the location of a physical
- * under-display fingerprint sensor (UDFPS).
- */
-public class UdfpsDialogMeasureAdapter {
- private static final String TAG = "UdfpsDialogMeasurementAdapter";
- private static final boolean DEBUG = Build.IS_USERDEBUG || Build.IS_ENG;
-
- @NonNull private final ViewGroup mView;
- @NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
- @Nullable private WindowManager mWindowManager;
- private int mBottomSpacerHeight;
-
- public UdfpsDialogMeasureAdapter(
- @NonNull ViewGroup view, @NonNull FingerprintSensorPropertiesInternal sensorProps) {
- mView = view;
- mSensorProps = sensorProps;
- mWindowManager = mView.getContext().getSystemService(WindowManager.class);
- }
-
- @NonNull
- FingerprintSensorPropertiesInternal getSensorProps() {
- return mSensorProps;
- }
-
- @NonNull
- public AuthDialog.LayoutParams onMeasureInternal(
- int width, int height, @NonNull AuthDialog.LayoutParams layoutParams,
- float scaleFactor) {
-
- final int displayRotation = mView.getDisplay().getRotation();
- switch (displayRotation) {
- case Surface.ROTATION_0:
- return onMeasureInternalPortrait(width, height, scaleFactor);
- case Surface.ROTATION_90:
- case Surface.ROTATION_270:
- return onMeasureInternalLandscape(width, height, scaleFactor);
- default:
- Log.e(TAG, "Unsupported display rotation: " + displayRotation);
- return layoutParams;
- }
- }
-
- /**
- * @return the actual (and possibly negative) bottom spacer height. If negative, this indicates
- * that the UDFPS sensor is too low. Our current xml and custom measurement logic is very hard
- * too cleanly support this case. So, let's have the onLayout code translate the sensor location
- * instead.
- */
- public int getBottomSpacerHeight() {
- return mBottomSpacerHeight;
- }
-
- /**
- * @return sensor diameter size as scaleFactor
- */
- public int getSensorDiameter(float scaleFactor) {
- return (int) (scaleFactor * mSensorProps.getLocation().sensorRadius * 2);
- }
-
- @NonNull
- private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height,
- float scaleFactor) {
- final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
-
- // Figure out where the bottom of the sensor anim should be.
- final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
- final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
- final int dialogMargin = getDialogMarginPx();
- final int displayHeight = getMaximumWindowBounds(windowMetrics).height();
- final Insets navbarInsets = getNavbarInsets(windowMetrics);
- mBottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
- mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight,
- dialogMargin, navbarInsets.bottom, scaleFactor);
-
- // Go through each of the children and do the custom measurement.
- int totalHeight = 0;
- final int numChildren = mView.getChildCount();
- final int sensorDiameter = getSensorDiameter(scaleFactor);
- for (int i = 0; i < numChildren; i++) {
- final View child = mView.getChildAt(i);
- if (child.getId() == R.id.biometric_icon_frame) {
- final FrameLayout iconFrame = (FrameLayout) child;
- final View icon = iconFrame.getChildAt(0);
- // Create a frame that's exactly the height of the sensor circle.
- iconFrame.measure(
- MeasureSpec.makeMeasureSpec(
- child.getLayoutParams().width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
-
- // Ensure that the icon is never larger than the sensor.
- icon.measure(
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));
- } else if (child.getId() == R.id.space_above_icon
- || child.getId() == R.id.space_above_content
- || child.getId() == R.id.button_bar) {
- child.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(
- child.getLayoutParams().height, MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.space_below_icon) {
- // Set the spacer height so the fingerprint icon is on the physical sensor area
- final int clampedSpacerHeight = Math.max(mBottomSpacerHeight, 0);
- child.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(clampedSpacerHeight, MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.description
- || child.getId() == R.id.customized_view_container) {
- //skip description view and compute later
- continue;
- } else if (child.getId() == R.id.logo) {
- child.measure(
- MeasureSpec.makeMeasureSpec(child.getLayoutParams().width,
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
- MeasureSpec.EXACTLY));
- } else {
- child.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
- }
-
- if (child.getVisibility() != View.GONE) {
- totalHeight += child.getMeasuredHeight();
- }
- }
-
- //re-calculate the height of body content
- View description = mView.findViewById(R.id.description);
- View contentView = mView.findViewById(R.id.customized_view_container);
- if (description != null && description.getVisibility() != View.GONE) {
- totalHeight += measureDescription(description, displayHeight, width, totalHeight);
- } else if (contentView != null && contentView.getVisibility() != View.GONE) {
- totalHeight += measureDescription(contentView, displayHeight, width, totalHeight);
- }
-
- return new AuthDialog.LayoutParams(width, totalHeight);
- }
-
- private int measureDescription(View bodyContent, int displayHeight, int currWidth,
- int currHeight) {
- int newHeight = bodyContent.getMeasuredHeight() + currHeight;
- int limit = (int) (displayHeight * 0.75);
- if (newHeight > limit) {
- bodyContent.measure(
- MeasureSpec.makeMeasureSpec(currWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(limit - currHeight, MeasureSpec.EXACTLY));
- }
- return bodyContent.getMeasuredHeight();
- }
-
- @NonNull
- private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height,
- float scaleFactor) {
- final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
-
- // Find the spacer height needed to vertically align the icon with the sensor.
- final int titleHeight = getViewHeightPx(R.id.title);
- final int subtitleHeight = getViewHeightPx(R.id.subtitle);
- final int descriptionHeight = getViewHeightPx(R.id.description);
- final int topSpacerHeight = getViewHeightPx(R.id.space_above_icon);
- final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
- final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
-
- final Insets navbarInsets = getNavbarInsets(windowMetrics);
- final int bottomSpacerHeight = calculateBottomSpacerHeightForLandscape(titleHeight,
- subtitleHeight, descriptionHeight, topSpacerHeight, textIndicatorHeight,
- buttonBarHeight, navbarInsets.bottom);
-
- // Find the spacer width needed to horizontally align the icon with the sensor.
- final int displayWidth = getMaximumWindowBounds(windowMetrics).width();
- final int dialogMargin = getDialogMarginPx();
- final int horizontalInset = navbarInsets.left + navbarInsets.right;
- final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
- mSensorProps, displayWidth, dialogMargin, horizontalInset, scaleFactor);
-
- final int sensorDiameter = getSensorDiameter(scaleFactor);
- final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth;
-
- int remeasuredHeight = 0;
- final int numChildren = mView.getChildCount();
- for (int i = 0; i < numChildren; i++) {
- final View child = mView.getChildAt(i);
- if (child.getId() == R.id.biometric_icon_frame) {
- final FrameLayout iconFrame = (FrameLayout) child;
- final View icon = iconFrame.getChildAt(0);
- // Create a frame that's exactly the height of the sensor circle.
- iconFrame.measure(
- MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
-
- // Ensure that the icon is never larger than the sensor.
- icon.measure(
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));
- } else if (child.getId() == R.id.space_above_icon) {
- // Adjust the width and height of the top spacer if necessary.
- final int newTopSpacerHeight = child.getLayoutParams().height
- - Math.min(bottomSpacerHeight, 0);
- child.measure(
- MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(newTopSpacerHeight, MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.button_bar) {
- // Adjust the width of the button bar while preserving its height.
- child.measure(
- MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(
- child.getLayoutParams().height, MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.space_below_icon) {
- // Adjust the bottom spacer height to align the fingerprint icon with the sensor.
- final int newBottomSpacerHeight = Math.max(bottomSpacerHeight, 0);
- child.measure(
- MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(newBottomSpacerHeight, MeasureSpec.EXACTLY));
- } else {
- // Use the remeasured width for all other child views.
- child.measure(
- MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
- }
-
- if (child.getVisibility() != View.GONE) {
- remeasuredHeight += child.getMeasuredHeight();
- }
- }
-
- return new AuthDialog.LayoutParams(remeasuredWidth, remeasuredHeight);
- }
-
- private int getViewHeightPx(@IdRes int viewId) {
- final View view = mView.findViewById(viewId);
- return view != null && view.getVisibility() != View.GONE ? view.getMeasuredHeight() : 0;
- }
-
- private int getDialogMarginPx() {
- return mView.getResources().getDimensionPixelSize(R.dimen.biometric_dialog_border_padding);
- }
-
- @NonNull
- private static Insets getNavbarInsets(@Nullable WindowMetrics windowMetrics) {
- return windowMetrics != null
- ? windowMetrics.getWindowInsets().getInsets(WindowInsets.Type.navigationBars())
- : Insets.NONE;
- }
-
- @NonNull
- private static Rect getMaximumWindowBounds(@Nullable WindowMetrics windowMetrics) {
- return windowMetrics != null ? windowMetrics.getBounds() : new Rect();
- }
-
- /**
- * For devices in portrait orientation where the sensor is too high up, calculates the amount of
- * padding necessary to center the biometric icon within the sensor's physical location.
- */
- @VisibleForTesting
- static int calculateBottomSpacerHeightForPortrait(
- @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx,
- int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx,
- int navbarBottomInsetPx, float scaleFactor) {
- final SensorLocationInternal location = sensorProperties.getLocation();
- final int sensorDistanceFromBottom = displayHeightPx
- - (int) (scaleFactor * location.sensorLocationY)
- - (int) (scaleFactor * location.sensorRadius);
-
- final int spacerHeight = sensorDistanceFromBottom
- - textIndicatorHeightPx
- - buttonBarHeightPx
- - dialogMarginPx
- - navbarBottomInsetPx;
-
- if (DEBUG) {
- Log.d(TAG, "Display height: " + displayHeightPx
- + ", Distance from bottom: " + sensorDistanceFromBottom
- + ", Bottom margin: " + dialogMarginPx
- + ", Navbar bottom inset: " + navbarBottomInsetPx
- + ", Bottom spacer height (portrait): " + spacerHeight
- + ", Scale Factor: " + scaleFactor);
- }
-
- return spacerHeight;
- }
-
- /**
- * For devices in landscape orientation where the sensor is too high up, calculates the amount
- * of padding necessary to center the biometric icon within the sensor's physical location.
- */
- @VisibleForTesting
- static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtitleHeightPx,
- int descriptionHeightPx, int topSpacerHeightPx, int textIndicatorHeightPx,
- int buttonBarHeightPx, int navbarBottomInsetPx) {
-
- final int dialogHeightAboveIcon = titleHeightPx
- + subtitleHeightPx
- + descriptionHeightPx
- + topSpacerHeightPx;
-
- final int dialogHeightBelowIcon = textIndicatorHeightPx + buttonBarHeightPx;
-
- final int bottomSpacerHeight = dialogHeightAboveIcon
- - dialogHeightBelowIcon
- - navbarBottomInsetPx;
-
- if (DEBUG) {
- Log.d(TAG, "Title height: " + titleHeightPx
- + ", Subtitle height: " + subtitleHeightPx
- + ", Description height: " + descriptionHeightPx
- + ", Top spacer height: " + topSpacerHeightPx
- + ", Text indicator height: " + textIndicatorHeightPx
- + ", Button bar height: " + buttonBarHeightPx
- + ", Navbar bottom inset: " + navbarBottomInsetPx
- + ", Bottom spacer height (landscape): " + bottomSpacerHeight);
- }
-
- return bottomSpacerHeight;
- }
-
- /**
- * For devices in landscape orientation where the sensor is too left/right, calculates the
- * amount of padding necessary to center the biometric icon within the sensor's physical
- * location.
- */
- @VisibleForTesting
- static int calculateHorizontalSpacerWidthForLandscape(
- @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx,
- int dialogMarginPx, int navbarHorizontalInsetPx, float scaleFactor) {
- final SensorLocationInternal location = sensorProperties.getLocation();
- final int sensorDistanceFromEdge = displayWidthPx
- - (int) (scaleFactor * location.sensorLocationY)
- - (int) (scaleFactor * location.sensorRadius);
-
- final int horizontalPadding = sensorDistanceFromEdge
- - dialogMarginPx
- - navbarHorizontalInsetPx;
-
- if (DEBUG) {
- Log.d(TAG, "Display width: " + displayWidthPx
- + ", Distance from edge: " + sensorDistanceFromEdge
- + ", Dialog margin: " + dialogMarginPx
- + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
- + ", Horizontal spacer width (landscape): " + horizontalPadding
- + ", Scale Factor: " + scaleFactor);
- }
-
- return horizontalPadding;
- }
-}
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 5e2b5ff..6da5e42 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
@@ -188,7 +188,6 @@
val hasCredentialViewShown = promptKind.value.isCredential()
val showBpForCredential =
Flags.customBiometricPrompt() &&
- com.android.systemui.Flags.constraintBp() &&
!Utils.isBiometricAllowed(promptInfo) &&
isDeviceCredentialAllowed(promptInfo) &&
promptInfo.contentView != null &&
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 348b423..695707d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -44,7 +44,7 @@
val logoDescription: String? = info.logoDescription
val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
val componentNameForConfirmDeviceCredentialActivity: ComponentName? =
- info.componentNameForConfirmDeviceCredentialActivity
+ info.realCallerForConfirmDeviceCredentialActivity
val allowBackgroundAuthentication = info.isAllowBackgroundAuthentication
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
deleted file mode 100644
index b450896..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
+++ /dev/null
@@ -1,188 +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.biometrics.ui;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Insets;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthDialog;
-import com.android.systemui.biometrics.UdfpsDialogMeasureAdapter;
-import com.android.systemui.res.R;
-
-import kotlin.Pair;
-
-/**
- * Contains the Biometric views (title, subtitle, icon, buttons, etc.).
- *
- * TODO(b/251476085): get the udfps junk out of here, at a minimum. Likely can be replaced with a
- * normal LinearLayout.
- */
-public class BiometricPromptLayout extends LinearLayout {
-
- private static final String TAG = "BiometricPromptLayout";
-
- @NonNull
- private final WindowManager mWindowManager;
- @Nullable
- private AuthController.ScaleFactorProvider mScaleFactorProvider;
- @Nullable
- private UdfpsDialogMeasureAdapter mUdfpsAdapter;
-
- private final boolean mUseCustomBpSize;
- private final int mCustomBpWidth;
- private final int mCustomBpHeight;
-
- public BiometricPromptLayout(Context context) {
- this(context, null);
- }
-
- public BiometricPromptLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mWindowManager = context.getSystemService(WindowManager.class);
-
- mUseCustomBpSize = getResources().getBoolean(R.bool.use_custom_bp_size);
- mCustomBpWidth = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_width);
- mCustomBpHeight = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_height);
- }
-
- @Deprecated
- public void setUdfpsAdapter(@NonNull UdfpsDialogMeasureAdapter adapter,
- @NonNull AuthController.ScaleFactorProvider scaleProvider) {
- mUdfpsAdapter = adapter;
- mScaleFactorProvider = scaleProvider != null ? scaleProvider : () -> 1.0f;
- }
-
- @Deprecated
- public boolean isUdfps() {
- return mUdfpsAdapter != null;
- }
-
- @Deprecated
- public Pair<Integer, Integer> getUpdatedFingerprintAffordanceSize() {
- if (mUdfpsAdapter != null) {
- final int sensorDiameter = mUdfpsAdapter.getSensorDiameter(
- mScaleFactorProvider.provide());
- return new Pair(sensorDiameter, sensorDiameter);
- }
- return null;
- }
-
- @NonNull
- private AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
- int totalHeight = 0;
- final int numChildren = getChildCount();
- for (int i = 0; i < numChildren; i++) {
- final View child = getChildAt(i);
-
- if (child.getId() == R.id.space_above_icon
- || child.getId() == R.id.space_above_content
- || child.getId() == R.id.space_below_icon
- || child.getId() == R.id.button_bar) {
- child.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
- MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.biometric_icon_frame) {
- final View iconView = findViewById(R.id.biometric_icon);
- child.measure(
- MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().width,
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().height,
- MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.logo) {
- child.measure(
- MeasureSpec.makeMeasureSpec(child.getLayoutParams().width,
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
- MeasureSpec.EXACTLY));
- } else if (child.getId() == R.id.biometric_icon) {
- child.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
- } else {
- child.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
- }
-
- if (child.getVisibility() != View.GONE) {
- totalHeight += child.getMeasuredHeight();
- }
- }
-
- final AuthDialog.LayoutParams params = new AuthDialog.LayoutParams(width, totalHeight);
- if (mUdfpsAdapter != null) {
- return mUdfpsAdapter.onMeasureInternal(width, height, params,
- (mScaleFactorProvider != null) ? mScaleFactorProvider.provide() : 1.0f);
- } else {
- return params;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- if (mUseCustomBpSize) {
- width = mCustomBpWidth;
- height = mCustomBpHeight;
- } else {
- width = Math.min(width, height);
- }
-
- // add nav bar insets since the parent AuthContainerView
- // uses LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- final Insets insets = mWindowManager.getMaximumWindowMetrics().getWindowInsets()
- .getInsets(WindowInsets.Type.navigationBars());
- final AuthDialog.LayoutParams params = onMeasureInternal(width, height);
- setMeasuredDimension(params.mMediumWidth + insets.left + insets.right,
- params.mMediumHeight + insets.bottom);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- if (mUdfpsAdapter != null) {
- // Move the UDFPS icon and indicator text if necessary. This probably only needs to
- // happen for devices where the UDFPS sensor is too low.
- // TODO(b/201510778): Update this logic to support cases where the sensor or text
- // overlap the button bar area.
- final float bottomSpacerHeight = mUdfpsAdapter.getBottomSpacerHeight();
- Log.w(TAG, "bottomSpacerHeight: " + bottomSpacerHeight);
- if (bottomSpacerHeight < 0) {
- final FrameLayout iconFrame = findViewById(R.id.biometric_icon_frame);
- iconFrame.setTranslationY(-bottomSpacerHeight);
- final TextView indicator = findViewById(R.id.indicator);
- indicator.setTranslationY(-bottomSpacerHeight);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
index 7ccac03..0b474f8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
@@ -38,14 +38,13 @@
import android.widget.Space
import android.widget.TextView
import com.android.settingslib.Utils
-import com.android.systemui.biometrics.ui.BiometricPromptLayout
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import kotlin.math.ceil
private const val TAG = "BiometricCustomizedViewBinder"
-/** Sub-binder for [BiometricPromptLayout.customized_view_container]. */
+/** Sub-binder for Biometric Prompt Customized View */
object BiometricCustomizedViewBinder {
fun bind(
customizedViewContainer: LinearLayout,
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 43ba097..a20a17f 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
@@ -45,13 +45,10 @@
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieCompositionFactory
-import com.android.systemui.Flags.constraintBp
-import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.biometrics.shared.model.asBiometricModality
-import com.android.systemui.biometrics.ui.BiometricPromptLayout
import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode
import com.android.systemui.biometrics.ui.viewmodel.PromptMessage
import com.android.systemui.biometrics.ui.viewmodel.PromptSize
@@ -72,28 +69,18 @@
/** Top-most view binder for BiometricPrompt views. */
object BiometricViewBinder {
- /** Binds a [BiometricPromptLayout] to a [PromptViewModel]. */
+ /** Binds a Biometric Prompt View to a [PromptViewModel]. */
@SuppressLint("ClickableViewAccessibility")
@JvmStatic
fun bind(
view: View,
viewModel: PromptViewModel,
- panelViewController: AuthPanelController?,
jankListener: BiometricJankListener,
backgroundView: View,
legacyCallback: Spaghetti.Callback,
applicationScope: CoroutineScope,
vibratorHelper: VibratorHelper,
): Spaghetti {
- /**
- * 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 (!constraintBp()) {
- view.visibility = View.INVISIBLE
- }
val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
val textColorError =
@@ -104,9 +91,7 @@
R.style.TextAppearance_AuthCredential_Indicator,
intArrayOf(android.R.attr.textColor)
)
- val textColorHint =
- if (constraintBp()) attributes.getColor(0, 0)
- else view.resources.getColor(R.color.biometric_dialog_gray, view.context.theme)
+ val textColorHint = attributes.getColor(0, 0)
attributes.recycle()
val logoView = view.requireViewById<ImageView>(R.id.logo)
@@ -116,12 +101,7 @@
val descriptionView = view.requireViewById<TextView>(R.id.description)
val customizedViewContainer =
view.requireViewById<LinearLayout>(R.id.customized_view_container)
- val udfpsGuidanceView =
- if (constraintBp()) {
- view.requireViewById<View>(R.id.panel)
- } else {
- backgroundView
- }
+ val udfpsGuidanceView = view.requireViewById<View>(R.id.panel)
// set selected to enable marquee unless a screen reader is enabled
titleView.isSelected =
@@ -130,14 +110,6 @@
!accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
-
- val iconSizeOverride =
- if (constraintBp()) {
- null
- } else {
- (view as BiometricPromptLayout).updatedFingerprintAffordanceSize
- }
-
val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
// Negative-side (left) buttons
@@ -213,7 +185,7 @@
subtitleView.text = viewModel.subtitle.first()
descriptionView.text = viewModel.description.first()
- if (Flags.customBiometricPrompt() && constraintBp()) {
+ if (Flags.customBiometricPrompt()) {
BiometricCustomizedViewBinder.bind(
customizedViewContainer,
viewModel.contentView.first(),
@@ -250,22 +222,6 @@
descriptionView,
customizedViewContainer,
),
- viewsToFadeInOnSizeChange =
- listOf(
- logoView,
- logoDescriptionView,
- titleView,
- subtitleView,
- descriptionView,
- customizedViewContainer,
- indicatorMessageView,
- negativeButton,
- cancelButton,
- retryButton,
- confirmationButton,
- credentialFallbackButton,
- ),
- panelViewController = panelViewController,
jankListener = jankListener,
)
}
@@ -275,7 +231,6 @@
if (!showWithoutIcon) {
PromptIconViewBinder.bind(
iconView,
- iconSizeOverride,
viewModel,
)
}
@@ -329,20 +284,6 @@
}
}
- // set padding
- launch {
- viewModel.promptPadding.collect { promptPadding ->
- if (!constraintBp()) {
- view.setPadding(
- promptPadding.left,
- promptPadding.top,
- promptPadding.right,
- promptPadding.bottom
- )
- }
- }
- }
-
// configure & hide/disable buttons
launch {
viewModel.credentialKind
@@ -546,24 +487,6 @@
fun onAuthenticatedAndConfirmed()
}
- @Deprecated("TODO(b/330788871): remove after replacing AuthContainerView")
- enum class BiometricState {
- /** Authentication hardware idle. */
- STATE_IDLE,
- /** UI animating in, authentication hardware active. */
- STATE_AUTHENTICATING_ANIMATING_IN,
- /** UI animated in, authentication hardware active. */
- STATE_AUTHENTICATING,
- /** UI animated in, authentication hardware active. */
- STATE_HELP,
- /** Hard error, e.g. ERROR_TIMEOUT. Authentication hardware idle. */
- STATE_ERROR,
- /** Authenticated, waiting for user confirmation. Authentication hardware idle. */
- STATE_PENDING_CONFIRMATION,
- /** Authenticated, dialog animating away soon. */
- STATE_AUTHENTICATED,
- }
-
private var lifecycleScope: CoroutineScope? = null
private var modalities: BiometricModalities = BiometricModalities()
private var legacyCallback: Callback? = null
@@ -699,15 +622,8 @@
}
fun startTransitionToCredentialUI(isError: Boolean) {
- if (!constraintBp()) {
- applicationScope.launch {
- viewModel.onSwitchToCredential()
- legacyCallback?.onUseDeviceCredential()
- }
- } else {
- viewModel.onSwitchToCredential()
- legacyCallback?.onUseDeviceCredential()
- }
+ viewModel.onSwitchToCredential()
+ legacyCallback?.onUseDeviceCredential()
}
fun cancelAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index b9ec2de..85c3ae3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -38,10 +38,7 @@
import androidx.constraintlayout.widget.Guideline
import androidx.core.animation.addListener
import androidx.core.view.doOnLayout
-import androidx.core.view.isGone
import androidx.lifecycle.lifecycleScope
-import com.android.systemui.Flags.constraintBp
-import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.ui.viewmodel.PromptPosition
import com.android.systemui.biometrics.ui.viewmodel.PromptSize
@@ -49,7 +46,6 @@
import com.android.systemui.biometrics.ui.viewmodel.isLarge
import com.android.systemui.biometrics.ui.viewmodel.isLeft
import com.android.systemui.biometrics.ui.viewmodel.isMedium
-import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.biometrics.ui.viewmodel.isTop
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -71,8 +67,6 @@
view: View,
viewModel: PromptViewModel,
viewsToHideWhenSmall: List<View>,
- viewsToFadeInOnSizeChange: List<View>,
- panelViewController: AuthPanelController?,
jankListener: BiometricJankListener,
) {
val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java))
@@ -92,553 +86,366 @@
}
}
- if (constraintBp()) {
- val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline)
- val topGuideline = view.requireViewById<Guideline>(R.id.topGuideline)
- val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline)
- val midGuideline = view.findViewById<Guideline>(R.id.midGuideline)
+ val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline)
+ val topGuideline = view.requireViewById<Guideline>(R.id.topGuideline)
+ val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline)
+ val midGuideline = view.findViewById<Guideline>(R.id.midGuideline)
- val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
- val panelView = view.requireViewById<View>(R.id.panel)
- val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
- val pxToDp =
- TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- 1f,
- view.resources.displayMetrics
- )
- val cornerRadiusPx = (pxToDp * cornerRadius).toInt()
+ val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
+ val panelView = view.requireViewById<View>(R.id.panel)
+ val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
+ val pxToDp =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 1f,
+ view.resources.displayMetrics
+ )
+ val cornerRadiusPx = (pxToDp * cornerRadius).toInt()
- var currentSize: PromptSize? = null
- var currentPosition: PromptPosition = PromptPosition.Bottom
- panelView.outlineProvider =
- object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- when (currentPosition) {
- PromptPosition.Right -> {
- outline.setRoundRect(
- 0,
- 0,
- view.width + cornerRadiusPx,
- view.height,
- cornerRadiusPx.toFloat()
- )
- }
- PromptPosition.Left -> {
- outline.setRoundRect(
- -cornerRadiusPx,
- 0,
- view.width,
- view.height,
- cornerRadiusPx.toFloat()
- )
- }
- PromptPosition.Bottom,
- PromptPosition.Top -> {
- outline.setRoundRect(
- 0,
- 0,
- view.width,
- view.height + cornerRadiusPx,
- cornerRadiusPx.toFloat()
- )
- }
- }
- }
- }
-
- // ConstraintSets for animating between prompt sizes
- val mediumConstraintSet = ConstraintSet()
- mediumConstraintSet.clone(view as ConstraintLayout)
-
- val smallConstraintSet = ConstraintSet()
- smallConstraintSet.clone(mediumConstraintSet)
-
- val largeConstraintSet = ConstraintSet()
- largeConstraintSet.clone(mediumConstraintSet)
- largeConstraintSet.constrainMaxWidth(R.id.panel, 0)
- largeConstraintSet.setGuidelineBegin(R.id.leftGuideline, 0)
- largeConstraintSet.setGuidelineEnd(R.id.rightGuideline, 0)
-
- // TODO: Investigate better way to handle 180 rotations
- val flipConstraintSet = ConstraintSet()
-
- view.doOnLayout {
- fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
- viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
- largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
- largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
-
- if (hideSensorIcon) {
- smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
- mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
- }
- }
-
- view.repeatWhenAttached {
- lifecycleScope.launch {
- viewModel.iconPosition.collect { position ->
- if (position != Rect()) {
- val iconParams =
- iconHolderView.layoutParams as ConstraintLayout.LayoutParams
-
- if (position.left != 0) {
- iconParams.endToEnd = ConstraintSet.UNSET
- iconParams.leftMargin = position.left
- mediumConstraintSet.clear(
- R.id.biometric_icon,
- ConstraintSet.RIGHT
- )
- mediumConstraintSet.connect(
- R.id.biometric_icon,
- ConstraintSet.LEFT,
- ConstraintSet.PARENT_ID,
- ConstraintSet.LEFT
- )
- mediumConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.LEFT,
- position.left
- )
- smallConstraintSet.clear(
- R.id.biometric_icon,
- ConstraintSet.RIGHT
- )
- smallConstraintSet.connect(
- R.id.biometric_icon,
- ConstraintSet.LEFT,
- ConstraintSet.PARENT_ID,
- ConstraintSet.LEFT
- )
- smallConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.LEFT,
- position.left
- )
- }
- if (position.top != 0) {
- iconParams.bottomToBottom = ConstraintSet.UNSET
- iconParams.topMargin = position.top
- mediumConstraintSet.clear(
- R.id.biometric_icon,
- ConstraintSet.BOTTOM
- )
- mediumConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.TOP,
- position.top
- )
- smallConstraintSet.clear(
- R.id.biometric_icon,
- ConstraintSet.BOTTOM
- )
- smallConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.TOP,
- position.top
- )
- }
- if (position.right != 0) {
- iconParams.startToStart = ConstraintSet.UNSET
- iconParams.rightMargin = position.right
- mediumConstraintSet.clear(
- R.id.biometric_icon,
- ConstraintSet.LEFT
- )
- mediumConstraintSet.connect(
- R.id.biometric_icon,
- ConstraintSet.RIGHT,
- ConstraintSet.PARENT_ID,
- ConstraintSet.RIGHT
- )
- mediumConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.RIGHT,
- position.right
- )
- smallConstraintSet.clear(
- R.id.biometric_icon,
- ConstraintSet.LEFT
- )
- smallConstraintSet.connect(
- R.id.biometric_icon,
- ConstraintSet.RIGHT,
- ConstraintSet.PARENT_ID,
- ConstraintSet.RIGHT
- )
- smallConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.RIGHT,
- position.right
- )
- }
- if (position.bottom != 0) {
- iconParams.topToTop = ConstraintSet.UNSET
- iconParams.bottomMargin = position.bottom
- mediumConstraintSet.clear(
- R.id.biometric_icon,
- ConstraintSet.TOP
- )
- mediumConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.BOTTOM,
- position.bottom
- )
- smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.TOP)
- smallConstraintSet.setMargin(
- R.id.biometric_icon,
- ConstraintSet.BOTTOM,
- position.bottom
- )
- }
- iconHolderView.layoutParams = iconParams
- }
- }
- }
-
- lifecycleScope.launch {
- viewModel.iconSize.collect { iconSize ->
- iconHolderView.layoutParams.width = iconSize.first
- iconHolderView.layoutParams.height = iconSize.second
- mediumConstraintSet.constrainWidth(R.id.biometric_icon, iconSize.first)
- mediumConstraintSet.constrainHeight(
- R.id.biometric_icon,
- iconSize.second
+ var currentSize: PromptSize? = null
+ var currentPosition: PromptPosition = PromptPosition.Bottom
+ panelView.outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ when (currentPosition) {
+ PromptPosition.Right -> {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width + cornerRadiusPx,
+ view.height,
+ cornerRadiusPx.toFloat()
)
}
- }
-
- lifecycleScope.launch {
- viewModel.guidelineBounds.collect { bounds ->
- val bottomInset =
- windowManager.maximumWindowMetrics.windowInsets
- .getInsets(WindowInsets.Type.navigationBars())
- .bottom
- mediumConstraintSet.setGuidelineEnd(R.id.bottomGuideline, bottomInset)
-
- if (bounds.left >= 0) {
- mediumConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
- smallConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
- } else if (bounds.left < 0) {
- mediumConstraintSet.setGuidelineEnd(
- leftGuideline.id,
- abs(bounds.left)
- )
- smallConstraintSet.setGuidelineEnd(
- leftGuideline.id,
- abs(bounds.left)
- )
- }
-
- if (bounds.right >= 0) {
- mediumConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
- smallConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
- } else if (bounds.right < 0) {
- mediumConstraintSet.setGuidelineBegin(
- rightGuideline.id,
- abs(bounds.right)
- )
- smallConstraintSet.setGuidelineBegin(
- rightGuideline.id,
- abs(bounds.right)
- )
- }
-
- if (bounds.top >= 0) {
- mediumConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
- smallConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
- } else if (bounds.top < 0) {
- mediumConstraintSet.setGuidelineEnd(
- topGuideline.id,
- abs(bounds.top)
- )
- smallConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top))
- }
-
- if (midGuideline != null) {
- val left =
- if (bounds.left >= 0) {
- abs(bounds.left)
- } else {
- view.width - abs(bounds.left)
- }
- val right =
- if (bounds.right >= 0) {
- view.width - abs(bounds.right)
- } else {
- abs(bounds.right)
- }
- val mid = (left + right) / 2
- mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid)
- }
+ PromptPosition.Left -> {
+ outline.setRoundRect(
+ -cornerRadiusPx,
+ 0,
+ view.width,
+ view.height,
+ cornerRadiusPx.toFloat()
+ )
}
- }
-
- lifecycleScope.launch {
- combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
- (hideSensorIcon, size) ->
- setVisibilities(hideSensorIcon, size)
- }
- }
-
- lifecycleScope.launch {
- combine(viewModel.position, viewModel.size, ::Pair).collect {
- (position, size) ->
- if (position.isLeft) {
- if (size.isSmall) {
- flipConstraintSet.clone(smallConstraintSet)
- } else {
- flipConstraintSet.clone(mediumConstraintSet)
- }
-
- // Move all content to other panel
- flipConstraintSet.connect(
- R.id.scrollView,
- ConstraintSet.LEFT,
- R.id.midGuideline,
- ConstraintSet.LEFT
- )
- flipConstraintSet.connect(
- R.id.scrollView,
- ConstraintSet.RIGHT,
- R.id.rightGuideline,
- ConstraintSet.RIGHT
- )
- } else if (position.isTop) {
- // Top position is only used for 180 rotation Udfps
- // Requires repositioning due to sensor location at top of screen
- mediumConstraintSet.connect(
- R.id.scrollView,
- ConstraintSet.TOP,
- R.id.indicator,
- ConstraintSet.BOTTOM
- )
- mediumConstraintSet.connect(
- R.id.scrollView,
- ConstraintSet.BOTTOM,
- R.id.button_bar,
- ConstraintSet.TOP
- )
- mediumConstraintSet.connect(
- R.id.panel,
- ConstraintSet.TOP,
- R.id.biometric_icon,
- ConstraintSet.TOP
- )
- mediumConstraintSet.setMargin(
- R.id.panel,
- ConstraintSet.TOP,
- (-24 * pxToDp).toInt()
- )
- mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f)
- }
-
- when {
- size.isSmall -> {
- if (position.isLeft) {
- flipConstraintSet.applyTo(view)
- } else {
- smallConstraintSet.applyTo(view)
- }
- }
- size.isMedium && currentSize.isSmall -> {
- val autoTransition = AutoTransition()
- autoTransition.setDuration(
- ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
- )
-
- TransitionManager.beginDelayedTransition(view, autoTransition)
-
- if (position.isLeft) {
- flipConstraintSet.applyTo(view)
- } else {
- mediumConstraintSet.applyTo(view)
- }
- }
- size.isMedium -> {
- if (position.isLeft) {
- flipConstraintSet.applyTo(view)
- } else {
- mediumConstraintSet.applyTo(view)
- }
- }
- size.isLarge && currentSize.isMedium -> {
- val autoTransition = AutoTransition()
- autoTransition.setDuration(
- ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
- )
-
- TransitionManager.beginDelayedTransition(view, autoTransition)
- largeConstraintSet.applyTo(view)
- }
- }
-
- currentSize = size
- currentPosition = position
- notifyAccessibilityChanged()
-
- panelView.invalidateOutline()
- view.invalidate()
- view.requestLayout()
+ PromptPosition.Bottom,
+ PromptPosition.Top -> {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width,
+ view.height + cornerRadiusPx,
+ cornerRadiusPx.toFloat()
+ )
}
}
}
}
- } else if (panelViewController != null) {
- val iconHolderView = view.requireViewById<View>(R.id.biometric_icon_frame)
- val iconPadding = view.resources.getDimension(R.dimen.biometric_dialog_icon_padding)
- val fullSizeYOffset =
- view.resources.getDimension(
- R.dimen.biometric_dialog_medium_to_large_translation_offset
- )
- // cache the original position of the icon view (as done in legacy view)
- // this must happen before any size changes can be made
- view.doOnLayout {
- // TODO(b/251476085): this old way of positioning has proven itself unreliable
- // remove this and associated thing like (UdfpsDialogMeasureAdapter) and
- // pin to the physical sensor
- val iconHolderOriginalY = iconHolderView.y
+ // ConstraintSets for animating between prompt sizes
+ val mediumConstraintSet = ConstraintSet()
+ mediumConstraintSet.clone(view as ConstraintLayout)
- // bind to prompt
- // TODO(b/251476085): migrate the legacy panel controller and simplify this
- view.repeatWhenAttached {
- var currentSize: PromptSize? = null
- lifecycleScope.launch {
- /**
- * 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 isIconViewLoaded once constraint
- * layout is implemented
- */
- combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect {
- (isIconViewLoaded, size) ->
- if (!isIconViewLoaded) {
- return@collect
+ val smallConstraintSet = ConstraintSet()
+ smallConstraintSet.clone(mediumConstraintSet)
+
+ val largeConstraintSet = ConstraintSet()
+ largeConstraintSet.clone(mediumConstraintSet)
+ largeConstraintSet.constrainMaxWidth(R.id.panel, 0)
+ largeConstraintSet.setGuidelineBegin(R.id.leftGuideline, 0)
+ largeConstraintSet.setGuidelineEnd(R.id.rightGuideline, 0)
+
+ // TODO: Investigate better way to handle 180 rotations
+ val flipConstraintSet = ConstraintSet()
+
+ view.doOnLayout {
+ fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
+ viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
+ largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
+
+ if (hideSensorIcon) {
+ smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ }
+ }
+
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ viewModel.iconPosition.collect { position ->
+ if (position != Rect()) {
+ val iconParams =
+ iconHolderView.layoutParams as ConstraintLayout.LayoutParams
+
+ if (position.left != 0) {
+ iconParams.endToEnd = ConstraintSet.UNSET
+ iconParams.leftMargin = position.left
+ mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.RIGHT)
+ mediumConstraintSet.connect(
+ R.id.biometric_icon,
+ ConstraintSet.LEFT,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.LEFT
+ )
+ mediumConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.LEFT,
+ position.left
+ )
+ smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.RIGHT)
+ smallConstraintSet.connect(
+ R.id.biometric_icon,
+ ConstraintSet.LEFT,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.LEFT
+ )
+ smallConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.LEFT,
+ position.left
+ )
}
-
- // prepare for animated size transitions
- for (v in viewsToHideWhenSmall) {
- v.showContentOrHide(forceHide = size.isSmall)
+ if (position.top != 0) {
+ iconParams.bottomToBottom = ConstraintSet.UNSET
+ iconParams.topMargin = position.top
+ mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.BOTTOM)
+ mediumConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.TOP,
+ position.top
+ )
+ smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.BOTTOM)
+ smallConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.TOP,
+ position.top
+ )
}
-
- if (viewModel.hideSensorIcon.first()) {
- iconHolderView.visibility = View.GONE
+ if (position.right != 0) {
+ iconParams.startToStart = ConstraintSet.UNSET
+ iconParams.rightMargin = position.right
+ mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.LEFT)
+ mediumConstraintSet.connect(
+ R.id.biometric_icon,
+ ConstraintSet.RIGHT,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.RIGHT
+ )
+ mediumConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.RIGHT,
+ position.right
+ )
+ smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.LEFT)
+ smallConstraintSet.connect(
+ R.id.biometric_icon,
+ ConstraintSet.RIGHT,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.RIGHT
+ )
+ smallConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.RIGHT,
+ position.right
+ )
}
-
- if (currentSize == null && size.isSmall) {
- iconHolderView.alpha = 0f
+ if (position.bottom != 0) {
+ iconParams.topToTop = ConstraintSet.UNSET
+ iconParams.bottomMargin = position.bottom
+ mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.TOP)
+ mediumConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.BOTTOM,
+ position.bottom
+ )
+ smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.TOP)
+ smallConstraintSet.setMargin(
+ R.id.biometric_icon,
+ ConstraintSet.BOTTOM,
+ position.bottom
+ )
}
- if ((currentSize.isSmall && size.isMedium) || size.isSmall) {
- viewsToFadeInOnSizeChange.forEach { it.alpha = 0f }
- }
+ iconHolderView.layoutParams = iconParams
+ }
+ }
+ }
- // propagate size changes to legacy panel controller and animate
- // transitions
- view.doOnLayout {
- val width = view.measuredWidth
- val height = view.measuredHeight
+ lifecycleScope.launch {
+ viewModel.iconSize.collect { iconSize ->
+ iconHolderView.layoutParams.width = iconSize.first
+ iconHolderView.layoutParams.height = iconSize.second
+ mediumConstraintSet.constrainWidth(R.id.biometric_icon, iconSize.first)
+ mediumConstraintSet.constrainHeight(R.id.biometric_icon, iconSize.second)
+ }
+ }
- when {
- size.isSmall -> {
- iconHolderView.alpha = 1f
- val bottomInset =
- windowManager.maximumWindowMetrics.windowInsets
- .getInsets(WindowInsets.Type.navigationBars())
- .bottom
- iconHolderView.y =
- if (view.isLandscape()) {
- (view.height -
- iconHolderView.height -
- bottomInset) / 2f
- } else {
- view.height -
- iconHolderView.height -
- iconPadding -
- bottomInset
- }
- val newHeight =
- iconHolderView.height + (2 * iconPadding.toInt()) -
- iconHolderView.paddingTop -
- iconHolderView.paddingBottom
- panelViewController.updateForContentDimensions(
- width,
- newHeight + bottomInset,
- 0, /* animateDurationMs */
- )
- }
- size.isMedium && currentSize.isSmall -> {
- val duration = ANIMATE_SMALL_TO_MEDIUM_DURATION_MS
- panelViewController.updateForContentDimensions(
- width,
- height,
- duration,
- )
- startMonitoredAnimation(
- listOf(
- iconHolderView.asVerticalAnimator(
- duration = duration.toLong(),
- toY =
- iconHolderOriginalY -
- viewsToHideWhenSmall
- .filter { it.isGone }
- .sumOf { it.height },
- ),
- viewsToFadeInOnSizeChange.asFadeInAnimator(
- duration = duration.toLong(),
- delay = duration.toLong(),
- ),
- )
- )
- }
- size.isMedium && currentSize.isNullOrNotSmall -> {
- panelViewController.updateForContentDimensions(
- width,
- height,
- 0, /* animateDurationMs */
- )
- }
- size.isLarge -> {
- val duration = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS
- panelViewController.setUseFullScreen(true)
- panelViewController.updateForContentDimensions(
- panelViewController.containerWidth,
- panelViewController.containerHeight,
- duration,
- )
+ lifecycleScope.launch {
+ viewModel.guidelineBounds.collect { bounds ->
+ val bottomInset =
+ windowManager.maximumWindowMetrics.windowInsets
+ .getInsets(WindowInsets.Type.navigationBars())
+ .bottom
+ mediumConstraintSet.setGuidelineEnd(R.id.bottomGuideline, bottomInset)
- startMonitoredAnimation(
- listOf(
- view.asVerticalAnimator(
- duration.toLong() * 2 / 3,
- toY = view.y - fullSizeYOffset
- ),
- listOf(view)
- .asFadeInAnimator(
- duration = duration.toLong() / 2,
- delay = duration.toLong(),
- ),
- )
- )
- // TODO(b/251476085): clean up (copied from legacy)
- if (view.isAttachedToWindow) {
- val parent = view.parent as? ViewGroup
- parent?.removeView(view)
- }
- }
+ if (bounds.left >= 0) {
+ mediumConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
+ smallConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
+ } else if (bounds.left < 0) {
+ mediumConstraintSet.setGuidelineEnd(leftGuideline.id, abs(bounds.left))
+ smallConstraintSet.setGuidelineEnd(leftGuideline.id, abs(bounds.left))
+ }
+
+ if (bounds.right >= 0) {
+ mediumConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
+ smallConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
+ } else if (bounds.right < 0) {
+ mediumConstraintSet.setGuidelineBegin(
+ rightGuideline.id,
+ abs(bounds.right)
+ )
+ smallConstraintSet.setGuidelineBegin(
+ rightGuideline.id,
+ abs(bounds.right)
+ )
+ }
+
+ if (bounds.top >= 0) {
+ mediumConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
+ smallConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
+ } else if (bounds.top < 0) {
+ mediumConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top))
+ smallConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top))
+ }
+
+ if (midGuideline != null) {
+ val left =
+ if (bounds.left >= 0) {
+ abs(bounds.left)
+ } else {
+ view.width - abs(bounds.left)
}
+ val right =
+ if (bounds.right >= 0) {
+ view.width - abs(bounds.right)
+ } else {
+ abs(bounds.right)
+ }
+ val mid = (left + right) / 2
+ mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid)
+ }
+ }
+ }
- currentSize = size
- view.visibility = View.VISIBLE
- viewModel.setIsIconViewLoaded(false)
- notifyAccessibilityChanged()
+ lifecycleScope.launch {
+ combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
+ (hideSensorIcon, size) ->
+ setVisibilities(hideSensorIcon, size)
+ }
+ }
+
+ lifecycleScope.launch {
+ combine(viewModel.position, viewModel.size, ::Pair).collect { (position, size)
+ ->
+ if (position.isLeft) {
+ if (size.isSmall) {
+ flipConstraintSet.clone(smallConstraintSet)
+ } else {
+ flipConstraintSet.clone(mediumConstraintSet)
+ }
+
+ // Move all content to other panel
+ flipConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.LEFT,
+ R.id.midGuideline,
+ ConstraintSet.LEFT
+ )
+ flipConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.RIGHT,
+ R.id.rightGuideline,
+ ConstraintSet.RIGHT
+ )
+ } else if (position.isTop) {
+ // Top position is only used for 180 rotation Udfps
+ // Requires repositioning due to sensor location at top of screen
+ mediumConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.TOP,
+ R.id.indicator,
+ ConstraintSet.BOTTOM
+ )
+ mediumConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.BOTTOM,
+ R.id.button_bar,
+ ConstraintSet.TOP
+ )
+ mediumConstraintSet.connect(
+ R.id.panel,
+ ConstraintSet.TOP,
+ R.id.biometric_icon,
+ ConstraintSet.TOP
+ )
+ mediumConstraintSet.setMargin(
+ R.id.panel,
+ ConstraintSet.TOP,
+ (-24 * pxToDp).toInt()
+ )
+ mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f)
+ }
+
+ when {
+ size.isSmall -> {
+ if (position.isLeft) {
+ flipConstraintSet.applyTo(view)
+ } else {
+ smallConstraintSet.applyTo(view)
+ }
+ }
+ size.isMedium && currentSize.isSmall -> {
+ val autoTransition = AutoTransition()
+ autoTransition.setDuration(
+ ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
+ )
+
+ TransitionManager.beginDelayedTransition(view, autoTransition)
+
+ if (position.isLeft) {
+ flipConstraintSet.applyTo(view)
+ } else {
+ mediumConstraintSet.applyTo(view)
+ }
+ }
+ size.isMedium -> {
+ if (position.isLeft) {
+ flipConstraintSet.applyTo(view)
+ } else {
+ mediumConstraintSet.applyTo(view)
+ }
+ }
+ size.isLarge && currentSize.isMedium -> {
+ val autoTransition = AutoTransition()
+ autoTransition.setDuration(
+ ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
+ )
+
+ TransitionManager.beginDelayedTransition(view, autoTransition)
+ largeConstraintSet.applyTo(view)
}
}
+
+ currentSize = size
+ currentPosition = position
+ notifyAccessibilityChanged()
+
+ panelView.invalidateOutline()
+ view.invalidate()
+ view.requestLayout()
}
}
}
@@ -646,17 +453,6 @@
}
}
-private fun View.isLandscape(): Boolean {
- val r = context.display.rotation
- return if (
- context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
- ) {
- r == Surface.ROTATION_0 || r == Surface.ROTATION_180
- } else {
- r == Surface.ROTATION_90 || r == Surface.ROTATION_270
- }
-}
-
private fun View.showContentOrHide(forceHide: Boolean = false) {
val isTextViewWithBlankText = this is TextView && this.text.isBlank()
val isImageViewWithoutImage = this is ImageView && this.drawable == null
@@ -667,26 +463,3 @@
View.VISIBLE
}
}
-
-private fun View.asVerticalAnimator(
- duration: Long,
- toY: Float,
- fromY: Float = this.y
-): ValueAnimator {
- val animator = ValueAnimator.ofFloat(fromY, toY)
- animator.duration = duration
- animator.addUpdateListener { y = it.animatedValue as Float }
- return animator
-}
-
-private fun List<View>.asFadeInAnimator(duration: Long, delay: Long): ValueAnimator {
- forEach { it.alpha = 0f }
- val animator = ValueAnimator.ofFloat(0f, 1f)
- animator.duration = duration
- animator.startDelay = delay
- animator.addUpdateListener {
- val alpha = it.animatedValue as Float
- forEach { view -> view.alpha = alpha }
- }
- return animator
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 18e2a56..49f4b05 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -10,7 +10,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialPatternView
@@ -82,7 +81,7 @@
subtitleView.textOrHide = header.subtitle
descriptionView.textOrHide = header.description
- if (Flags.customBiometricPrompt() && constraintBp()) {
+ if (Flags.customBiometricPrompt()) {
BiometricCustomizedViewBinder.bind(
customizedViewContainer,
header.contentView,
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 9e4aaaa..eab3b26 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
@@ -22,9 +22,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
-import com.airbnb.lottie.LottieOnCompositionLoadedListener
import com.android.settingslib.widget.LottieColorUtils
-import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel.AuthType
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
@@ -44,55 +42,12 @@
@JvmStatic
fun bind(
iconView: LottieAnimationView,
- iconViewLayoutParamSizeOverride: Pair<Int, Int>?,
promptViewModel: PromptViewModel
) {
val viewModel = promptViewModel.iconViewModel
iconView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.onConfigurationChanged(iconView.context.resources.configuration)
- if (iconViewLayoutParamSizeOverride != null) {
- iconView.layoutParams.width = iconViewLayoutParamSizeOverride.first
- iconView.layoutParams.height = iconViewLayoutParamSizeOverride.second
- }
-
- if (!constraintBp()) {
- launch {
- var lottieOnCompositionLoadedListener: LottieOnCompositionLoadedListener? =
- null
-
- 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
- iconView.layoutParams.height = iconSize.second
- }
- }
- }
- }
launch {
viewModel.iconAsset
@@ -154,7 +109,7 @@
setAnimation(asset)
if (animatingFromSfpsAuthenticating(asset)) {
// Skipping to error / success / unlock segment of animation
- setMinFrame(151)
+ setMinFrame(158)
} else {
frame = 0
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 31af126..761c3da 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -6,7 +6,6 @@
import android.hardware.biometrics.PromptContentView
import android.text.InputType
import com.android.internal.widget.LockPatternView
-import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.CredentialStatus
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
@@ -39,7 +38,7 @@
credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
credentialInteractor.showTitleOnly
) { request, showTitleOnly ->
- val flagEnabled = customBiometricPrompt() && constraintBp()
+ val flagEnabled = customBiometricPrompt()
val showTitleOnlyForCredential = showTitleOnly && flagEnabled
BiometricPromptHeaderViewModelImpl(
request,
@@ -82,8 +81,8 @@
val errorMessage: Flow<String> =
combine(credentialInteractor.verificationError, credentialInteractor.prompt) { error, p ->
when (error) {
- is CredentialStatus.Fail.Error -> error.error
- ?: applicationContext.asBadCredentialErrorMessage(p)
+ is CredentialStatus.Fail.Error ->
+ error.error ?: applicationContext.asBadCredentialErrorMessage(p)
is CredentialStatus.Fail.Throttled -> error.error
null -> ""
}
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 214420d..25d43d9 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
@@ -36,7 +36,6 @@
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import com.android.launcher3.icons.IconProvider
-import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.Utils.isSystem
@@ -470,7 +469,7 @@
promptSelectorInteractor.prompt
.map {
when {
- !(customBiometricPrompt() && constraintBp()) || it == null -> Pair(null, "")
+ !(customBiometricPrompt()) || it == null -> Pair(null, "")
else -> context.getUserBadgedLogoInfo(it, iconProvider, activityTaskManager)
}
}
@@ -487,7 +486,7 @@
/** Custom content view for the prompt. */
val contentView: Flow<PromptContentView?> =
promptSelectorInteractor.prompt
- .map { if (customBiometricPrompt() && constraintBp()) it?.contentView else null }
+ .map { if (customBiometricPrompt()) it?.contentView else null }
.distinctUntilChanged()
private val originalDescription =
@@ -1045,7 +1044,7 @@
val packageName =
when {
componentNameForLogo != null -> componentNameForLogo.packageName
- // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+ // TODO(b/353597496): We should check whether |allowBackgroundAuthentication| should be
// removed.
// This is being consistent with the check in [AuthController.showDialog()].
allowBackgroundAuthentication || isSystem(context, opPackageName) -> opPackageName
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
index 19ea007..c2a4ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -28,7 +28,6 @@
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
import com.airbnb.lottie.model.KeyPath
-import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
@@ -152,21 +151,6 @@
->
val topLeft = Point(sensorLocation.left, sensorLocation.top)
- if (!constraintBp()) {
- if (sensorLocation.isSensorVerticalInDefaultOrientation) {
- if (displayRotation == DisplayRotation.ROTATION_0) {
- topLeft.x -= bounds!!.width()
- } else if (displayRotation == DisplayRotation.ROTATION_270) {
- topLeft.y -= bounds!!.height()
- }
- } else {
- if (displayRotation == DisplayRotation.ROTATION_180) {
- topLeft.y -= bounds!!.height()
- } else if (displayRotation == DisplayRotation.ROTATION_270) {
- topLeft.x -= bounds!!.width()
- }
- }
- }
defaultOverlayViewParams.apply {
x = topLeft.x
y = topLeft.y
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
index e44f054..817f2d7 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
@@ -34,7 +34,9 @@
internal sealed class AudioSharingButtonState {
object Gone : AudioSharingButtonState()
- data class Visible(@StringRes val resId: Int) : AudioSharingButtonState()
+
+ data class Visible(@StringRes val resId: Int, val isActive: Boolean) :
+ AudioSharingButtonState()
}
/** Holds business logic for the audio sharing state. */
@@ -73,7 +75,8 @@
// Show sharing audio when broadcasting
BluetoothUtils.isBroadcasting(localBluetoothManager) ->
AudioSharingButtonState.Visible(
- R.string.quick_settings_bluetooth_audio_sharing_button_sharing
+ R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
+ isActive = true
)
// When not broadcasting, don't show button if there's connected source in any device
deviceItem.any {
@@ -85,7 +88,8 @@
// Show audio sharing when there's a connected LE audio device
deviceItem.any { BluetoothUtils.isActiveLeAudioDevice(it.cachedBluetoothDevice) } ->
AudioSharingButtonState.Visible(
- R.string.quick_settings_bluetooth_audio_sharing_button
+ R.string.quick_settings_bluetooth_audio_sharing_button,
+ isActive = false
)
else -> AudioSharingButtonState.Gone
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index f5b9a05..2808dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -212,11 +212,13 @@
internal fun onAudioSharingButtonUpdated(
dialog: SystemUIDialog,
visibility: Int,
- label: String?
+ label: String?,
+ isActive: Boolean
) {
getAudioSharingButtonView(dialog).apply {
this.visibility = visibility
label?.let { text = it }
+ this.isActivated = isActive
}
}
@@ -370,6 +372,7 @@
private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon)
private val iconGear = view.requireViewById<ImageView>(R.id.gear_icon_image)
private val gearView = view.requireViewById<View>(R.id.gear_icon)
+ private val divider = view.requireViewById<View>(R.id.divider)
internal fun bind(
item: DeviceItem,
@@ -402,6 +405,8 @@
iconGear.apply { drawable?.let { it.mutate()?.setTint(tintColor) } }
+ divider.setBackgroundColor(tintColor)
+
// update text styles
nameView.setTextAppearance(
if (item.isActive) R.style.BluetoothTileDialog_DeviceName_Active
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
index eaddc42..985b158 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
@@ -104,8 +104,7 @@
)
controller?.let {
dialogTransitionAnimator.show(dialog, it, animateBackgroundBoundsChange = true)
- }
- ?: dialog.show()
+ } ?: dialog.show()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
@@ -149,14 +148,23 @@
if (BluetoothUtils.isAudioSharingEnabled()) {
audioSharingInteractor.audioSharingButtonStateUpdate
.onEach {
- if (it is AudioSharingButtonState.Visible) {
- dialogDelegate.onAudioSharingButtonUpdated(
- dialog,
- VISIBLE,
- context.getString(it.resId)
- )
- } else {
- dialogDelegate.onAudioSharingButtonUpdated(dialog, GONE, null)
+ when (it) {
+ is AudioSharingButtonState.Visible -> {
+ dialogDelegate.onAudioSharingButtonUpdated(
+ dialog,
+ VISIBLE,
+ context.getString(it.resId),
+ it.isActive
+ )
+ }
+ is AudioSharingButtonState.Gone -> {
+ dialogDelegate.onAudioSharingButtonUpdated(
+ dialog,
+ GONE,
+ label = null,
+ isActive = false
+ )
+ }
}
}
.launchIn(this)
@@ -305,6 +313,7 @@
companion object {
private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
private const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT
+
private fun getSubtitleResId(isBluetoothEnabled: Boolean) =
if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle
else R.string.bt_is_off
@@ -336,7 +345,10 @@
interface BluetoothTileDialogCallback {
fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View)
+
fun onSeeAllClicked(view: View)
+
fun onPairNewDeviceClicked(view: View)
+
fun onAudioSharingButtonClicked(view: View)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index a401600..8b9c0a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -145,8 +145,7 @@
)
}
}
- }
- ?: emptyList()
+ } ?: emptyList()
_selectedDots.value =
linkedSetOf<PatternDotViewModel>().apply {
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 718ef51..7d518f4 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -23,19 +23,20 @@
import android.view.Surface
import android.view.View
import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.Utils
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.surfaceeffects.ripple.RippleView
+import com.android.systemui.res.R
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
@@ -57,6 +58,7 @@
featureFlags: FeatureFlags,
private val context: Context,
private val windowManager: WindowManager,
+ private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger
) {
@@ -161,12 +163,12 @@
override fun onViewAttachedToWindow(view: View) {
layoutRipple()
rippleView.startRipple(Runnable {
- windowManager.removeView(rippleView)
+ viewCaptureAwareWindowManager.removeView(rippleView)
})
rippleView.removeOnAttachStateChangeListener(this)
}
})
- windowManager.addView(rippleView, windowLayoutParams)
+ viewCaptureAwareWindowManager.addView(rippleView, windowLayoutParams)
uiEventLogger.log(WiredChargingRippleEvent.CHARGING_RIPPLE_PLAYED)
}
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 6aaaf3d..fbeb715 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
@@ -184,7 +184,11 @@
keyguardTransitionInteractor.startedKeyguardTransitionStep
.filter { step -> step.to == KeyguardState.OCCLUDED }
.combine(isCommunalAvailable, ::Pair)
- .map { (step, available) -> available && step.from == KeyguardState.GLANCEABLE_HUB }
+ .map { (step, available) ->
+ available &&
+ (step.from == KeyguardState.GLANCEABLE_HUB ||
+ step.from == KeyguardState.DREAMING)
+ }
.flowOn(bgDispatcher)
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
new file mode 100644
index 0000000..7a05671
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.binder
+
+import android.content.Context
+import android.util.SizeF
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import com.android.app.tracing.coroutines.launch
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.util.WidgetViewFactory
+import com.android.systemui.util.kotlin.DisposableHandles
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+
+object CommunalAppWidgetHostViewBinder {
+ private const val TAG = "CommunalAppWidgetHostViewBinder"
+
+ fun bind(
+ context: Context,
+ applicationScope: CoroutineScope,
+ container: FrameLayout,
+ model: CommunalContentModel.WidgetContent.Widget,
+ size: SizeF,
+ factory: WidgetViewFactory,
+ ): DisposableHandle {
+ val disposables = DisposableHandles()
+
+ val loadingJob =
+ applicationScope.launch("$TAG#createWidgetView") {
+ val widget = factory.createWidget(context, model, size)
+ // TODO(b/358662507): Remove this workaround
+ (container.parent as? ViewGroup)?.let { parent ->
+ val index = parent.indexOfChild(container)
+ parent.removeView(container)
+ parent.addView(container, index)
+ }
+ container.setView(widget)
+ }
+
+ disposables += DisposableHandle { loadingJob.cancel() }
+ disposables += DisposableHandle { container.removeAllViews() }
+
+ return disposables
+ }
+}
+
+private fun ViewGroup.setView(view: View) {
+ if (view.parent == this) {
+ return
+ }
+ (view.parent as? ViewGroup)?.removeView(view)
+ addView(view)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
new file mode 100644
index 0000000..56b769e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.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.
+ */
+
+package com.android.systemui.communal.ui.view.layout.sections
+
+import android.util.SizeF
+import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+import android.widget.FrameLayout
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.ui.binder.CommunalAppWidgetHostViewBinder
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
+import com.android.systemui.communal.util.WidgetViewFactory
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+
+class CommunalAppWidgetSection
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val factory: WidgetViewFactory,
+) {
+
+ private companion object {
+ val DISPOSABLE_TAG = R.id.communal_widget_disposable_tag
+ }
+
+ @Composable
+ fun Widget(
+ viewModel: BaseCommunalViewModel,
+ model: CommunalContentModel.WidgetContent.Widget,
+ size: SizeF,
+ modifier: Modifier = Modifier,
+ ) {
+ val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
+
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ )
+
+ // Need to attach the disposable handle to the view here instead of storing
+ // the state in the composable in order to properly support lazy lists. In a
+ // lazy list, when the composable is no longer in view - it will exit
+ // composition and any state inside the composable will be lost. However,
+ // the View instance will be re-used. Therefore we can store data on the view
+ // in order to preserve it.
+ setTag(
+ DISPOSABLE_TAG,
+ CommunalAppWidgetHostViewBinder.bind(
+ context = context,
+ container = this,
+ model = model,
+ size = size,
+ factory = factory,
+ applicationScope = applicationScope,
+ )
+ )
+
+ accessibilityDelegate = viewModel.widgetAccessibilityDelegate
+ }
+ },
+ update = { container ->
+ container.importantForAccessibility =
+ if (isFocusable) {
+ IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ } else {
+ IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ }
+ },
+ onRelease = { view ->
+ val disposable = (view.getTag(DISPOSABLE_TAG) as? DisposableHandle)
+ disposable?.dispose()
+ },
+ modifier = modifier,
+ // For reusing composition in lazy lists.
+ onReset = {},
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
new file mode 100644
index 0000000..0e39a99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.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.systemui.communal.util
+
+import android.content.Context
+import android.os.Bundle
+import android.util.SizeF
+import com.android.app.tracing.coroutines.withContext
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
+import com.android.systemui.dagger.qualifiers.UiBackground
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+
+/** Factory for creating [CommunalAppWidgetHostView] in a background thread. */
+class WidgetViewFactory
+@Inject
+constructor(
+ @UiBackground private val uiBgContext: CoroutineContext,
+ private val appWidgetHost: CommunalAppWidgetHost,
+) {
+ suspend fun createWidget(
+ context: Context,
+ model: CommunalContentModel.WidgetContent.Widget,
+ size: SizeF,
+ ): CommunalAppWidgetHostView =
+ withContext("$TAG#createWidget", uiBgContext) {
+ appWidgetHost
+ .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
+ .apply {
+ updateAppWidgetSize(
+ /* newOptions = */ Bundle(),
+ /* minWidth = */ size.width.toInt(),
+ /* minHeight = */ size.height.toInt(),
+ /* maxWidth = */ size.width.toInt(),
+ /* maxHeight = */ size.height.toInt(),
+ /* ignorePadding = */ true,
+ )
+ }
+ }
+
+ private companion object {
+ const val TAG = "WidgetViewFactory"
+ }
+}
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 2bcbc9a..668fef6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -42,6 +42,7 @@
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.ui.compose.CommunalHub
+import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -60,6 +61,7 @@
private var windowManagerService: IWindowManager? = null,
private val uiEventLogger: UiEventLogger,
private val widgetConfiguratorFactory: WidgetConfigurationController.Factory,
+ private val widgetSection: CommunalAppWidgetSection,
@CommunalLog logBuffer: LogBuffer,
) : ComponentActivity() {
companion object {
@@ -204,6 +206,7 @@
onOpenWidgetPicker = ::onOpenWidgetPicker,
widgetConfigurator = widgetConfigurator,
onEditDone = ::onEditDone,
+ widgetSection = widgetSection,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 3273111..79f4568 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -33,6 +33,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialModule;
import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
@@ -77,7 +78,7 @@
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
import com.android.systemui.toast.ToastModule;
-import com.android.systemui.touchpad.tutorial.TouchpadKeyboardTutorialModule;
+import com.android.systemui.touchpad.tutorial.TouchpadTutorialModule;
import com.android.systemui.unfold.SysUIUnfoldStartableModule;
import com.android.systemui.unfold.UnfoldTransitionModule;
import com.android.systemui.util.kotlin.SysUICoroutinesModule;
@@ -122,6 +123,7 @@
KeyboardShortcutsModule.class,
KeyguardBlueprintModule.class,
KeyguardSectionsModule.class,
+ KeyboardTouchpadTutorialModule.class,
MediaModule.class,
MediaMuteAwaitConnectionCli.StartableModule.class,
MultiUserUtilsModule.class,
@@ -142,7 +144,7 @@
SysUIUnfoldStartableModule.class,
UnfoldTransitionModule.Startables.class,
ToastModule.class,
- TouchpadKeyboardTutorialModule.class,
+ TouchpadTutorialModule.class,
VolumeModule.class,
WallpaperModule.class,
ShortcutHelperModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index b0f2c18..cbea876 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -258,13 +258,6 @@
@Binds
@IntoMap
- @ClassKey(KeyboardTouchpadTutorialCoreStartable::class)
- abstract fun bindKeyboardTouchpadTutorialCoreStartable(
- listener: KeyboardTouchpadTutorialCoreStartable
- ): CoreStartable
-
- @Binds
- @IntoMap
@ClassKey(PhysicalKeyboardCoreStartable::class)
abstract fun bindKeyboardCoreStartable(listener: PhysicalKeyboardCoreStartable): CoreStartable
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 9b95ac4..39f4e31 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
@@ -119,21 +119,24 @@
* Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
* UI.
*/
- val canSwipeToEnter: StateFlow<Boolean?> =
+ val canSwipeToEnter: StateFlow<Boolean?> by lazy {
combine(
authenticationInteractor.authenticationMethod.map {
it == AuthenticationMethodModel.None
},
isLockscreenEnabled,
deviceUnlockedInteractor.deviceUnlockStatus,
- isDeviceEntered
- ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered ->
- val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled
- (isSwipeAuthMethod ||
- (deviceUnlockStatus.isUnlocked &&
- deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
- !isDeviceEntered
- }
+ isDeviceEntered) {
+ isNoneAuthMethod,
+ isLockscreenEnabled,
+ deviceUnlockStatus,
+ isDeviceEntered ->
+ val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled
+ (isSwipeAuthMethod ||
+ (deviceUnlockStatus.isUnlocked &&
+ deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
+ !isDeviceEntered
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -142,6 +145,7 @@
// from upstream data sources.
initialValue = null,
)
+ }
/**
* Attempt to enter the device and dismiss the lockscreen. If authentication is required to
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
index 0b33614..6fd4d33 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
@@ -16,8 +16,6 @@
package com.android.systemui.doze;
-import java.util.concurrent.Executor;
-
/**
* Forwards the currently used brightness to {@link DozeHost}.
*/
@@ -25,9 +23,8 @@
private final DozeHost mHost;
- public DozeBrightnessHostForwarder(DozeMachine.Service wrappedService, DozeHost host,
- Executor bgExecutor) {
- super(wrappedService, bgExecutor);
+ public DozeBrightnessHostForwarder(DozeMachine.Service wrappedService, DozeHost host) {
+ super(wrappedService);
mHost = host;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 5bfcc97..cdcb03e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -253,9 +253,11 @@
/**
* Appends display state changed event to the logs
* @param displayState new DozeMachine state
+ * @param afterRequest whether the request has successfully been sent else false for it's
+ * about to be requested
*/
- public void traceDisplayState(int displayState) {
- mLogger.logDisplayStateChanged(displayState);
+ public void traceDisplayState(int displayState, boolean afterRequest) {
+ mLogger.logDisplayStateChanged(displayState, afterRequest);
}
/**
@@ -402,18 +404,22 @@
/**
* Appends new AOD screen brightness to logs
* @param brightness display brightness setting between 1 and 255
+ * @param afterRequest whether the request has successfully been sent else false for it's
+ * about to be requested
*/
- public void traceDozeScreenBrightness(int brightness) {
- mLogger.logDozeScreenBrightness(brightness);
+ public void traceDozeScreenBrightness(int brightness, boolean afterRequest) {
+ mLogger.logDozeScreenBrightness(brightness, afterRequest);
}
/**
* Appends new AOD screen brightness to logs
* @param brightness display brightness setting between {@link PowerManager#BRIGHTNESS_MIN} and
* {@link PowerManager#BRIGHTNESS_MAX}
+ * @param afterRequest whether the request has successfully been sent else false for it's
+ * about to be requested
*/
- public void traceDozeScreenBrightnessFloat(float brightness) {
- mLogger.logDozeScreenBrightnessFloat(brightness);
+ public void traceDozeScreenBrightnessFloat(float brightness, boolean afterRequest) {
+ mLogger.logDozeScreenBrightnessFloat(brightness, afterRequest);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index a31dbec..7128731 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -32,23 +32,18 @@
import javax.inject.Inject
/** Interface for logging messages to the [DozeLog]. */
-class DozeLogger @Inject constructor(
- @DozeLog private val buffer: LogBuffer
-) {
+class DozeLogger @Inject constructor(@DozeLog private val buffer: LogBuffer) {
fun logPickupWakeup(isWithinVibrationThreshold: Boolean) {
- buffer.log(TAG, DEBUG, {
- bool1 = isWithinVibrationThreshold
- }, {
- "PickupWakeup withinVibrationThreshold=$bool1"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = isWithinVibrationThreshold },
+ { "PickupWakeup withinVibrationThreshold=$bool1" }
+ )
}
fun logPulseStart(@Reason reason: Int) {
- buffer.log(TAG, INFO, {
- int1 = reason
- }, {
- "Pulse start, reason=${reasonToString(int1)}"
- })
+ buffer.log(TAG, INFO, { int1 = reason }, { "Pulse start, reason=${reasonToString(int1)}" })
}
fun logPulseFinish() {
@@ -60,52 +55,51 @@
}
fun logDozing(isDozing: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = isDozing
- }, {
- "Dozing=$bool1"
- })
+ buffer.log(TAG, INFO, { bool1 = isDozing }, { "Dozing=$bool1" })
}
fun logDozingChanged(isDozing: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = isDozing
- }, {
- "Dozing changed dozing=$bool1"
- })
+ buffer.log(TAG, INFO, { bool1 = isDozing }, { "Dozing changed dozing=$bool1" })
}
fun logPowerSaveChanged(powerSaveActive: Boolean, nextState: DozeMachine.State) {
- buffer.log(TAG, INFO, {
- bool1 = powerSaveActive
- str1 = nextState.name
- }, {
- "Power save active=$bool1 nextState=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ bool1 = powerSaveActive
+ str1 = nextState.name
+ },
+ { "Power save active=$bool1 nextState=$str1" }
+ )
}
fun logAlwaysOnSuppressedChange(isAodSuppressed: Boolean, nextState: DozeMachine.State) {
- buffer.log(TAG, INFO, {
- bool1 = isAodSuppressed
- str1 = nextState.name
- }, {
- "Always on (AOD) suppressed changed, suppressed=$bool1 nextState=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ bool1 = isAodSuppressed
+ str1 = nextState.name
+ },
+ { "Always on (AOD) suppressed changed, suppressed=$bool1 nextState=$str1" }
+ )
}
- fun logFling(
- expand: Boolean,
- aboveThreshold: Boolean,
- screenOnFromTouch: Boolean
- ) {
- buffer.log(TAG, DEBUG, {
- bool1 = expand
- bool2 = aboveThreshold
- bool4 = screenOnFromTouch
- }, {
- "Fling expand=$bool1 aboveThreshold=$bool2 thresholdNeeded=$bool3 " +
- "screenOnFromTouch=$bool4"
- })
+ fun logFling(expand: Boolean, aboveThreshold: Boolean, screenOnFromTouch: Boolean) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = expand
+ bool2 = aboveThreshold
+ bool4 = screenOnFromTouch
+ },
+ {
+ "Fling expand=$bool1 aboveThreshold=$bool2 thresholdNeeded=$bool3 " +
+ "screenOnFromTouch=$bool4"
+ }
+ )
}
fun logEmergencyCall() {
@@ -113,280 +107,314 @@
}
fun logKeyguardBouncerChanged(isShowing: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = isShowing
- }, {
- "Keyguard bouncer changed, showing=$bool1"
- })
+ buffer.log(TAG, INFO, { bool1 = isShowing }, { "Keyguard bouncer changed, showing=$bool1" })
}
fun logScreenOn(isPulsing: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = isPulsing
- }, {
- "Screen on, pulsing=$bool1"
- })
+ buffer.log(TAG, INFO, { bool1 = isPulsing }, { "Screen on, pulsing=$bool1" })
}
fun logScreenOff(why: Int) {
- buffer.log(TAG, INFO, {
- int1 = why
- }, {
- "Screen off, why=$int1"
- })
+ buffer.log(TAG, INFO, { int1 = why }, { "Screen off, why=$int1" })
}
fun logMissedTick(delay: String) {
- buffer.log(TAG, ERROR, {
- str1 = delay
- }, {
- "Missed AOD time tick by $str1"
- })
+ buffer.log(TAG, ERROR, { str1 = delay }, { "Missed AOD time tick by $str1" })
}
fun logTimeTickScheduled(whenAt: Long, triggerAt: Long) {
- buffer.log(TAG, DEBUG, {
- long1 = whenAt
- long2 = triggerAt
- }, {
- "Time tick scheduledAt=${DATE_FORMAT.format(Date(long1))} " +
- "triggerAt=${DATE_FORMAT.format(Date(long2))}"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ long1 = whenAt
+ long2 = triggerAt
+ },
+ {
+ "Time tick scheduledAt=${DATE_FORMAT.format(Date(long1))} " +
+ "triggerAt=${DATE_FORMAT.format(Date(long2))}"
+ }
+ )
}
fun logKeyguardVisibilityChange(isVisible: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = isVisible
- }, {
- "Keyguard visibility change, isVisible=$bool1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { bool1 = isVisible },
+ { "Keyguard visibility change, isVisible=$bool1" }
+ )
}
fun logPendingUnscheduleTimeTick(isPending: Boolean, isTimeTickScheduled: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = isPending
- bool2 = isTimeTickScheduled
- }, {
- "Pending unschedule time tick, isPending=$bool1, isTimeTickScheduled:$bool2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ bool1 = isPending
+ bool2 = isTimeTickScheduled
+ },
+ { "Pending unschedule time tick, isPending=$bool1, isTimeTickScheduled:$bool2" }
+ )
}
fun logDozeStateChanged(state: DozeMachine.State) {
- buffer.log(TAG, INFO, {
- str1 = state.name
- }, {
- "Doze state changed to $str1"
- })
+ buffer.log(TAG, INFO, { str1 = state.name }, { "Doze state changed to $str1" })
}
fun logStateChangedSent(state: DozeMachine.State) {
- buffer.log(TAG, INFO, {
- str1 = state.name
- }, {
- "Doze state sent to all DozeMachineParts stateSent=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { str1 = state.name },
+ { "Doze state sent to all DozeMachineParts stateSent=$str1" }
+ )
}
fun logDisplayStateDelayedByUdfps(delayedDisplayState: Int) {
- buffer.log(TAG, INFO, {
- str1 = Display.stateToString(delayedDisplayState)
- }, {
- "Delaying display state change to: $str1 due to UDFPS activity"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { str1 = Display.stateToString(delayedDisplayState) },
+ { "Delaying display state change to: $str1 due to UDFPS activity" }
+ )
}
- fun logDisplayStateChanged(displayState: Int) {
- buffer.log(TAG, INFO, {
- str1 = Display.stateToString(displayState)
- }, {
- "Display state changed to $str1"
- })
+ fun logDisplayStateChanged(displayState: Int, afterRequest: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = Display.stateToString(displayState)
+ bool1 = afterRequest
+ },
+ { "Display state ${if (bool1) "changed" else "requested"} to $str1" }
+ )
}
fun logWakeDisplay(isAwake: Boolean, @Reason reason: Int) {
- buffer.log(TAG, DEBUG, {
- bool1 = isAwake
- int1 = reason
- }, {
- "Display wakefulness changed, isAwake=$bool1, reason=${reasonToString(int1)}"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = isAwake
+ int1 = reason
+ },
+ { "Display wakefulness changed, isAwake=$bool1, reason=${reasonToString(int1)}" }
+ )
}
fun logProximityResult(isNear: Boolean, millis: Long, @Reason reason: Int) {
- buffer.log(TAG, DEBUG, {
- bool1 = isNear
- long1 = millis
- int1 = reason
- }, {
- "Proximity result reason=${reasonToString(int1)} near=$bool1 millis=$long1"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = isNear
+ long1 = millis
+ int1 = reason
+ },
+ { "Proximity result reason=${reasonToString(int1)} near=$bool1 millis=$long1" }
+ )
}
fun logPostureChanged(posture: Int, partUpdated: String) {
- buffer.log(TAG, INFO, {
- int1 = posture
- str1 = partUpdated
- }, {
- "Posture changed, posture=${DevicePostureController.devicePostureToString(int1)}" +
- " partUpdated=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ int1 = posture
+ str1 = partUpdated
+ },
+ {
+ "Posture changed, posture=${DevicePostureController.devicePostureToString(int1)}" +
+ " partUpdated=$str1"
+ }
+ )
}
/**
- * Log why a pulse was dropped and the current doze machine state. The state can be null
- * if the DozeMachine is the middle of transitioning between states.
+ * Log why a pulse was dropped and the current doze machine state. The state can be null if the
+ * DozeMachine is the middle of transitioning between states.
*/
fun logPulseDropped(from: String, state: DozeMachine.State?) {
- buffer.log(TAG, INFO, {
- str1 = from
- str2 = state?.name
- }, {
- "Pulse dropped, cannot pulse from=$str1 state=$str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = from
+ str2 = state?.name
+ },
+ { "Pulse dropped, cannot pulse from=$str1 state=$str2" }
+ )
}
fun logSensorEventDropped(sensorEvent: Int, reason: String) {
- buffer.log(TAG, INFO, {
- int1 = sensorEvent
- str1 = reason
- }, {
- "SensorEvent [$int1] dropped, reason=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ int1 = sensorEvent
+ str1 = reason
+ },
+ { "SensorEvent [$int1] dropped, reason=$str1" }
+ )
}
fun logPulseEvent(pulseEvent: String, dozing: Boolean, pulseReason: String) {
- buffer.log(TAG, DEBUG, {
- str1 = pulseEvent
- bool1 = dozing
- str2 = pulseReason
- }, {
- "Pulse-$str1 dozing=$bool1 pulseReason=$str2"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = pulseEvent
+ bool1 = dozing
+ str2 = pulseReason
+ },
+ { "Pulse-$str1 dozing=$bool1 pulseReason=$str2" }
+ )
}
fun logPulseDropped(reason: String) {
- buffer.log(TAG, INFO, {
- str1 = reason
- }, {
- "Pulse dropped, why=$str1"
- })
+ buffer.log(TAG, INFO, { str1 = reason }, { "Pulse dropped, why=$str1" })
}
fun logPulseTouchDisabledByProx(disabled: Boolean) {
- buffer.log(TAG, DEBUG, {
- bool1 = disabled
- }, {
- "Pulse touch modified by prox, disabled=$bool1"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = disabled },
+ { "Pulse touch modified by prox, disabled=$bool1" }
+ )
}
fun logSensorTriggered(@Reason reason: Int) {
- buffer.log(TAG, DEBUG, {
- int1 = reason
- }, {
- "Sensor triggered, type=${reasonToString(int1)}"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ { int1 = reason },
+ { "Sensor triggered, type=${reasonToString(int1)}" }
+ )
}
fun logAlwaysOnSuppressed(state: DozeMachine.State, reason: String) {
- buffer.log(TAG, INFO, {
- str1 = state.name
- str2 = reason
- }, {
- "Always-on state suppressed, suppressed state=$str1 reason=$str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = state.name
+ str2 = reason
+ },
+ { "Always-on state suppressed, suppressed state=$str1 reason=$str2" }
+ )
}
fun logImmediatelyEndDoze(reason: String) {
- buffer.log(TAG, INFO, {
- str1 = reason
- }, {
- "Doze immediately ended due to $str1"
- })
+ buffer.log(TAG, INFO, { str1 = reason }, { "Doze immediately ended due to $str1" })
}
- fun logDozeScreenBrightness(brightness: Int) {
- buffer.log(TAG, INFO, {
- int1 = brightness
- }, {
- "Doze screen brightness set (int), brightness=$int1"
- })
+ fun logDozeScreenBrightness(brightness: Int, afterRequest: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ int1 = brightness
+ bool1 = afterRequest
+ },
+ {
+ "Doze screen brightness ${if (bool1) "set" else "requested"}" +
+ " (int), brightness=$int1"
+ }
+ )
}
- fun logDozeScreenBrightnessFloat(brightness: Float) {
- buffer.log(TAG, INFO, {
- double1 = brightness.toDouble()
- }, {
- "Doze screen brightness set (float), brightness=$double1"
- })
+ fun logDozeScreenBrightnessFloat(brightness: Float, afterRequest: Boolean) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ double1 = brightness.toDouble()
+ bool1 = afterRequest
+ },
+ {
+ "Doze screen brightness ${if (bool1) "set" else "requested"}" +
+ " (float), brightness=$double1"
+ }
+ )
}
fun logSetAodDimmingScrim(scrimOpacity: Long) {
- buffer.log(TAG, INFO, {
- long1 = scrimOpacity
- }, {
- "Doze aod dimming scrim opacity set, opacity=$long1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { long1 = scrimOpacity },
+ { "Doze aod dimming scrim opacity set, opacity=$long1" }
+ )
}
fun logCarModeEnded() {
- buffer.log(TAG, INFO, {}, {
- "Doze car mode ended"
- })
+ buffer.log(TAG, INFO, {}, { "Doze car mode ended" })
}
fun logCarModeStarted() {
- buffer.log(TAG, INFO, {}, {
- "Doze car mode started"
- })
+ buffer.log(TAG, INFO, {}, { "Doze car mode started" })
}
fun logSensorRegisterAttempt(sensorInfo: String, successfulRegistration: Boolean) {
- buffer.log(TAG, INFO, {
- str1 = sensorInfo
- bool1 = successfulRegistration
- }, {
- "Register sensor. Success=$bool1 sensor=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = sensorInfo
+ bool1 = successfulRegistration
+ },
+ { "Register sensor. Success=$bool1 sensor=$str1" }
+ )
}
fun logSensorUnregisterAttempt(sensorInfo: String, successfulUnregister: Boolean) {
- buffer.log(TAG, INFO, {
- str1 = sensorInfo
- bool1 = successfulUnregister
- }, {
- "Unregister sensor. Success=$bool1 sensor=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = sensorInfo
+ bool1 = successfulUnregister
+ },
+ { "Unregister sensor. Success=$bool1 sensor=$str1" }
+ )
}
fun logSensorUnregisterAttempt(
- sensorInfo: String,
- successfulUnregister: Boolean,
- reason: String
+ sensorInfo: String,
+ successfulUnregister: Boolean,
+ reason: String
) {
- buffer.log(TAG, INFO, {
- str1 = sensorInfo
- bool1 = successfulUnregister
- str2 = reason
- }, {
- "Unregister sensor. reason=$str2. Success=$bool1 sensor=$str1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = sensorInfo
+ bool1 = successfulUnregister
+ str2 = reason
+ },
+ { "Unregister sensor. reason=$str2. Success=$bool1 sensor=$str1" }
+ )
}
fun logSkipSensorRegistration(sensor: String) {
- buffer.log(TAG, DEBUG, {
- str1 = sensor
- }, {
- "Skipping sensor registration because its already registered. sensor=$str1"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ { str1 = sensor },
+ { "Skipping sensor registration because its already registered. sensor=$str1" }
+ )
}
fun logSetIgnoreTouchWhilePulsing(ignoreTouchWhilePulsing: Boolean) {
- buffer.log(TAG, DEBUG, {
- bool1 = ignoreTouchWhilePulsing
- }, {
- "Prox changed while pulsing. setIgnoreTouchWhilePulsing=$bool1"
- })
+ buffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = ignoreTouchWhilePulsing },
+ { "Prox changed while pulsing. setIgnoreTouchWhilePulsing=$bool1" }
+ )
}
fun log(@CompileTimeConstant msg: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 8198ef4..e02e3fb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -39,7 +39,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -232,7 +231,6 @@
}
void onScreenState(int state) {
- mDozeLog.traceDisplayState(state);
for (Part part : mParts) {
part.onScreenState(state);
}
@@ -516,11 +514,9 @@
class Delegate implements Service {
private final Service mDelegate;
- private final Executor mBgExecutor;
- public Delegate(Service delegate, Executor bgExecutor) {
+ public Delegate(Service delegate) {
mDelegate = delegate;
- mBgExecutor = bgExecutor;
}
@Override
@@ -540,16 +536,12 @@
@Override
public void setDozeScreenBrightness(int brightness) {
- mBgExecutor.execute(() -> {
- mDelegate.setDozeScreenBrightness(brightness);
- });
+ mDelegate.setDozeScreenBrightness(brightness);
}
@Override
public void setDozeScreenBrightnessFloat(float brightness) {
- mBgExecutor.execute(() -> {
- mDelegate.setDozeScreenBrightnessFloat(brightness);
- });
+ mDelegate.setDozeScreenBrightnessFloat(brightness);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
index 8d44472..25c2c39 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
@@ -22,16 +22,14 @@
import com.android.systemui.statusbar.phone.DozeParameters;
-import java.util.concurrent.Executor;
-
/**
* Prevents usage of doze screen states on devices that don't support them.
*/
public class DozeScreenStatePreventingAdapter extends DozeMachine.Service.Delegate {
@VisibleForTesting
- DozeScreenStatePreventingAdapter(DozeMachine.Service inner, Executor bgExecutor) {
- super(inner, bgExecutor);
+ DozeScreenStatePreventingAdapter(DozeMachine.Service inner) {
+ super(inner);
}
@Override
@@ -49,8 +47,8 @@
* return a new instance of {@link DozeScreenStatePreventingAdapter} wrapping {@code inner}.
*/
public static DozeMachine.Service wrapIfNeeded(DozeMachine.Service inner,
- DozeParameters params, Executor bgExecutor) {
- return isNeeded(params) ? new DozeScreenStatePreventingAdapter(inner, bgExecutor) : inner;
+ DozeParameters params) {
+ return isNeeded(params) ? new DozeScreenStatePreventingAdapter(inner) : inner;
}
private static boolean isNeeded(DozeParameters params) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index ba38ab0..2e372ff 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -23,6 +23,7 @@
import android.service.dreams.DreamService;
import android.util.Log;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.plugins.DozeServicePlugin;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
@@ -31,6 +32,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -43,9 +45,14 @@
private DozeMachine mDozeMachine;
private DozeServicePlugin mDozePlugin;
private PluginManager mPluginManager;
+ private DozeLog mDozeLog;
+ private Executor mBgExecutor;
@Inject
- public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager) {
+ public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager,
+ DozeLog dozeLog, @UiBackground Executor bgExecutor) {
+ mDozeLog = dozeLog;
+ mBgExecutor = bgExecutor;
mDozeComponentBuilder = dozeComponentBuilder;
setDebug(DEBUG);
mPluginManager = pluginManager;
@@ -143,9 +150,29 @@
@Override
public void setDozeScreenState(int state) {
+ mDozeLog.traceDisplayState(state, /* afterRequest */ false);
super.setDozeScreenState(state);
+ mDozeLog.traceDisplayState(state, /* afterRequest */ true);
if (mDozeMachine != null) {
mDozeMachine.onScreenState(state);
}
}
+
+ @Override
+ public void setDozeScreenBrightness(int brightness) {
+ mBgExecutor.execute(() -> {
+ mDozeLog.traceDozeScreenBrightness(brightness, /* afterRequest */ false);
+ super.setDozeScreenBrightness(brightness);
+ mDozeLog.traceDozeScreenBrightness(brightness, /* afterRequest */ true);
+ });
+ }
+
+ @Override
+ public void setDozeScreenBrightnessFloat(float brightness) {
+ mBgExecutor.execute(() -> {
+ mDozeLog.traceDozeScreenBrightnessFloat(brightness, /* afterRequest */ false);
+ super.setDozeScreenBrightnessFloat(brightness);
+ mDozeLog.traceDozeScreenBrightnessFloat(brightness, /* afterRequest */ true);
+ });
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
index f7773f1..cfc952d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
@@ -22,16 +22,14 @@
import com.android.systemui.statusbar.phone.DozeParameters;
-import java.util.concurrent.Executor;
-
/**
* Prevents usage of doze screen states on devices that don't support them.
*/
public class DozeSuspendScreenStatePreventingAdapter extends DozeMachine.Service.Delegate {
@VisibleForTesting
- DozeSuspendScreenStatePreventingAdapter(DozeMachine.Service inner, Executor bgExecutor) {
- super(inner, bgExecutor);
+ DozeSuspendScreenStatePreventingAdapter(DozeMachine.Service inner) {
+ super(inner);
}
@Override
@@ -47,8 +45,8 @@
* return a new instance of {@link DozeSuspendScreenStatePreventingAdapter} wrapping {@code inner}.
*/
public static DozeMachine.Service wrapIfNeeded(DozeMachine.Service inner,
- DozeParameters params, Executor bgExecutor) {
- return isNeeded(params) ? new DozeSuspendScreenStatePreventingAdapter(inner, bgExecutor)
+ DozeParameters params) {
+ return isNeeded(params) ? new DozeSuspendScreenStatePreventingAdapter(inner)
: inner;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 8c3de4b..f383a04 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.hardware.Sensor;
-import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.doze.DozeAuthRemover;
import com.android.systemui.doze.DozeBrightnessHostForwarder;
import com.android.systemui.doze.DozeDockHandler;
@@ -51,7 +50,6 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
-import java.util.concurrent.Executor;
/** Dagger module for use with {@link com.android.systemui.doze.dagger.DozeComponent}. */
@Module
@@ -60,13 +58,13 @@
@DozeScope
@WrappedService
static DozeMachine.Service providesWrappedService(DozeMachine.Service dozeMachineService,
- DozeHost dozeHost, DozeParameters dozeParameters, @UiBackground Executor bgExecutor) {
+ DozeHost dozeHost, DozeParameters dozeParameters) {
DozeMachine.Service wrappedService = dozeMachineService;
- wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost, bgExecutor);
+ wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost);
wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, dozeParameters, bgExecutor);
+ wrappedService, dozeParameters);
wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, dozeParameters, bgExecutor);
+ wrappedService, dozeParameters);
return wrappedService;
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 096556f..7e2c9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -26,6 +26,7 @@
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractorImpl
+import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
import dagger.Binds
import dagger.Lazy
import dagger.Module
@@ -74,7 +75,7 @@
implLazy.get()
} else {
// No-op implementation when the flag is disabled.
- return NoOpContextualEducationInteractor
+ return NoOpCoreStartable
}
}
@@ -91,6 +92,8 @@
}
@Provides
+ @IntoMap
+ @ClassKey(KeyboardTouchpadEduInteractor::class)
fun provideKeyboardTouchpadEduInteractor(
implLazy: Lazy<KeyboardTouchpadEduInteractor>
): CoreStartable {
@@ -98,22 +101,32 @@
implLazy.get()
} else {
// No-op implementation when the flag is disabled.
- return NoOpKeyboardTouchpadEduInteractor
+ return NoOpCoreStartable
+ }
+ }
+
+ @Provides
+ @IntoMap
+ @ClassKey(ContextualEduUiCoordinator::class)
+ fun provideContextualEduUiCoordinator(
+ implLazy: Lazy<ContextualEduUiCoordinator>
+ ): CoreStartable {
+ return if (Flags.keyboardTouchpadContextualEducation()) {
+ implLazy.get()
+ } else {
+ // No-op implementation when the flag is disabled.
+ return NoOpCoreStartable
}
}
}
+}
- private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
- override fun incrementSignalCount(gestureType: GestureType) {}
+private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
+ override fun incrementSignalCount(gestureType: GestureType) {}
- override fun updateShortcutTriggerTime(gestureType: GestureType) {}
- }
+ override fun updateShortcutTriggerTime(gestureType: GestureType) {}
+}
- private object NoOpContextualEducationInteractor : CoreStartable {
- override fun start() {}
- }
-
- private object NoOpKeyboardTouchpadEduInteractor : CoreStartable {
- override fun start() {}
- }
+private object NoOpCoreStartable : CoreStartable {
+ override fun start() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
new file mode 100644
index 0000000..b446ea2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.education.ui.view
+
+import android.content.Context
+import android.widget.Toast
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.education.shared.model.EducationUiType
+import com.android.systemui.education.ui.viewmodel.ContextualEduContentViewModel
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A class to show contextual education on UI based on the edu produced from
+ * [ContextualEduViewModel]
+ */
+@SysUISingleton
+class ContextualEduUiCoordinator
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val viewModel: ContextualEduViewModel,
+ private val createToast: (String) -> Toast
+) : CoreStartable {
+
+ @Inject
+ constructor(
+ @Application applicationScope: CoroutineScope,
+ context: Context,
+ viewModel: ContextualEduViewModel,
+ ) : this(
+ applicationScope,
+ viewModel,
+ createToast = { message -> Toast.makeText(context, message, Toast.LENGTH_LONG) }
+ )
+
+ override fun start() {
+ applicationScope.launch {
+ viewModel.eduContent.collect { contentModel ->
+ if (contentModel.type == EducationUiType.Toast) {
+ showToast(contentModel)
+ }
+ }
+ }
+ }
+
+ private fun showToast(model: ContextualEduContentViewModel) {
+ val toast = createToast(model.message)
+ toast.show()
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
similarity index 65%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
index 460de8c..3cba4c8 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
@@ -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,18 +14,8 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.systemui.education.ui.viewmodel
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import com.android.systemui.education.shared.model.EducationUiType
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
-}
+data class ContextualEduContentViewModel(val message: String, val type: EducationUiType)
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
new file mode 100644
index 0000000..58276e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.education.ui.viewmodel
+
+import android.content.res.Resources
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.shared.model.EducationInfo
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class ContextualEduViewModel
+@Inject
+constructor(@Main private val resources: Resources, interactor: KeyboardTouchpadEduInteractor) {
+ val eduContent: Flow<ContextualEduContentViewModel> =
+ interactor.educationTriggered.filterNotNull().map {
+ ContextualEduContentViewModel(getEduContent(it), it.educationUiType)
+ }
+
+ private fun getEduContent(educationInfo: EducationInfo): String {
+ // Todo: also check UiType in educationInfo to determine the string
+ val resourceId =
+ when (educationInfo.gestureType) {
+ GestureType.BACK -> R.string.back_edu_toast_content
+ GestureType.HOME -> R.string.home_edu_toast_content
+ GestureType.OVERVIEW -> R.string.overview_edu_toast_content
+ GestureType.ALL_APPS -> R.string.all_apps_edu_toast_content
+ }
+ return resources.getString(resourceId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 8990505..e5f3a57 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -152,9 +152,6 @@
// TODO(b/286563884): Tracking bug
@JvmField val KEYGUARD_TALKBACK_FIX = unreleasedFlag("keyguard_talkback_fix")
- // TODO(b/287268101): Tracking bug.
- @JvmField val TRANSIT_CLOCK = releasedFlag("lockscreen_custom_transit_clock")
-
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadTutorialModule.kt
new file mode 100644
index 0000000..8e6cb07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadTutorialModule.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial
+
+import android.app.Activity
+import com.android.systemui.CoreStartable
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import dagger.Binds
+import dagger.BindsOptionalOf
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface KeyboardTouchpadTutorialModule {
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyboardTouchpadTutorialCoreStartable::class)
+ fun bindKeyboardTouchpadTutorialCoreStartable(
+ listener: KeyboardTouchpadTutorialCoreStartable
+ ): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyboardTouchpadTutorialActivity::class)
+ fun activity(impl: KeyboardTouchpadTutorialActivity): Activity
+
+ // TouchpadModule dependencies below
+ // all should be optional to not introduce touchpad dependency in all sysui variants
+
+ @BindsOptionalOf fun touchpadScreensProvider(): TouchpadTutorialScreensProvider
+
+ @BindsOptionalOf fun touchpadGesturesInteractor(): TouchpadGesturesInteractor
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/TouchpadTutorialScreensProvider.kt
similarity index 66%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SystemUI/src/com/android/systemui/inputdevice/oobe/TouchpadTutorialScreensProvider.kt
index 460de8c..bd3e771 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/TouchpadTutorialScreensProvider.kt
@@ -14,18 +14,13 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.systemui.inputdevice.tutorial
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import androidx.compose.runtime.Composable
- /** carrier id */
- int mCarrierId;
+interface TouchpadTutorialScreensProvider {
- /** apn */
- String mNiddApn;
+ @Composable fun BackGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit)
+
+ @Composable fun HomeGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit)
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionKeyTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionKeyTutorialScreen.kt
new file mode 100644
index 0000000..c5b0ca7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionKeyTutorialScreen.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.
+ */
+
+package com.android.systemui.inputdevice.tutorial.ui.composable
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
+import com.airbnb.lottie.compose.rememberLottieDynamicProperties
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.FINISHED
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NOT_STARTED
+import com.android.systemui.res.R
+
+@Composable
+fun ActionKeyTutorialScreen(
+ onDoneButtonClicked: () -> Unit,
+ onBack: () -> Unit,
+) {
+ BackHandler(onBack = onBack)
+ val screenConfig = buildScreenConfig()
+ var actionState by remember { mutableStateOf(NOT_STARTED) }
+ Box(
+ modifier =
+ Modifier.fillMaxSize().onKeyEvent { keyEvent: KeyEvent ->
+ // temporary before we can access Action/Meta key
+ if (keyEvent.key == Key.AltLeft && keyEvent.type == KeyEventType.KeyUp) {
+ actionState = FINISHED
+ }
+ true
+ }
+ ) {
+ ActionTutorialContent(actionState, onDoneButtonClicked, screenConfig)
+ }
+}
+
+@Composable
+private fun buildScreenConfig() =
+ TutorialScreenConfig(
+ colors = rememberScreenColors(),
+ strings =
+ TutorialScreenConfig.Strings(
+ titleResId = R.string.tutorial_action_key_title,
+ bodyResId = R.string.tutorial_action_key_guidance,
+ titleSuccessResId = R.string.tutorial_action_key_success_title,
+ bodySuccessResId = R.string.tutorial_action_key_success_body
+ ),
+ animations =
+ TutorialScreenConfig.Animations(
+ educationResId = R.raw.action_key_edu,
+ successResId = R.raw.action_key_success
+ )
+ )
+
+@Composable
+private fun rememberScreenColors(): TutorialScreenConfig.Colors {
+ val primaryFixedDim = LocalAndroidColorScheme.current.primaryFixedDim
+ val secondaryFixedDim = LocalAndroidColorScheme.current.secondaryFixedDim
+ val onSecondaryFixed = LocalAndroidColorScheme.current.onSecondaryFixed
+ val onSecondaryFixedVariant = LocalAndroidColorScheme.current.onSecondaryFixedVariant
+ val surfaceContainer = MaterialTheme.colorScheme.surfaceContainer
+ val dynamicProperties =
+ rememberLottieDynamicProperties(
+ rememberColorFilterProperty(".primaryFixedDim", primaryFixedDim),
+ rememberColorFilterProperty(".secondaryFixedDim", secondaryFixedDim),
+ rememberColorFilterProperty(".onSecondaryFixed", onSecondaryFixed),
+ rememberColorFilterProperty(".onSecondaryFixedVariant", onSecondaryFixedVariant)
+ )
+ val screenColors =
+ remember(surfaceContainer, dynamicProperties) {
+ TutorialScreenConfig.Colors(
+ background = onSecondaryFixed,
+ successBackground = surfaceContainer,
+ title = primaryFixedDim,
+ animationColors = dynamicProperties,
+ )
+ }
+ return screenColors
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionTutorialContent.kt
new file mode 100644
index 0000000..c50b7dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionTutorialContent.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.ui.composable
+
+import android.graphics.ColorFilter
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import androidx.annotation.RawRes
+import androidx.annotation.StringRes
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+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.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.LottieDynamicProperties
+import com.airbnb.lottie.compose.LottieDynamicProperty
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+import com.airbnb.lottie.compose.rememberLottieDynamicProperty
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.FINISHED
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.IN_PROGRESS
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NOT_STARTED
+
+enum class TutorialActionState {
+ NOT_STARTED,
+ IN_PROGRESS,
+ FINISHED
+}
+
+@Composable
+fun ActionTutorialContent(
+ actionState: TutorialActionState,
+ onDoneButtonClicked: () -> Unit,
+ config: TutorialScreenConfig
+) {
+ val animatedColor by
+ animateColorAsState(
+ targetValue =
+ if (actionState == FINISHED) config.colors.successBackground
+ else config.colors.background,
+ animationSpec = tween(durationMillis = 150, easing = LinearEasing),
+ label = "backgroundColor"
+ )
+ Column(
+ verticalArrangement = Arrangement.Center,
+ modifier =
+ Modifier.fillMaxSize()
+ .drawBehind { drawRect(animatedColor) }
+ .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
+ ) {
+ Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
+ TutorialDescription(
+ titleTextId =
+ if (actionState == FINISHED) config.strings.titleSuccessResId
+ else config.strings.titleResId,
+ titleColor = config.colors.title,
+ bodyTextId =
+ if (actionState == FINISHED) config.strings.bodySuccessResId
+ else config.strings.bodyResId,
+ modifier = Modifier.weight(1f)
+ )
+ Spacer(modifier = Modifier.width(76.dp))
+ TutorialAnimation(
+ actionState,
+ config,
+ modifier = Modifier.weight(1f).padding(top = 8.dp)
+ )
+ }
+ DoneButton(onDoneButtonClicked = onDoneButtonClicked)
+ }
+}
+
+@Composable
+fun TutorialDescription(
+ @StringRes titleTextId: Int,
+ titleColor: Color,
+ @StringRes bodyTextId: Int,
+ modifier: Modifier = Modifier
+) {
+ Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
+ Text(
+ text = stringResource(id = titleTextId),
+ style = MaterialTheme.typography.displayLarge,
+ color = titleColor
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = stringResource(id = bodyTextId),
+ style = MaterialTheme.typography.bodyLarge,
+ color = Color.White
+ )
+ }
+}
+
+@Composable
+fun TutorialAnimation(
+ actionState: TutorialActionState,
+ config: TutorialScreenConfig,
+ modifier: Modifier = Modifier
+) {
+ Box(modifier = modifier.fillMaxWidth()) {
+ AnimatedContent(
+ targetState = actionState,
+ transitionSpec = {
+ if (initialState == NOT_STARTED) {
+ val transitionDurationMillis = 150
+ fadeIn(animationSpec = tween(transitionDurationMillis, easing = LinearEasing))
+ .togetherWith(
+ fadeOut(animationSpec = snap(delayMillis = transitionDurationMillis))
+ )
+ // we explicitly don't want size transform because when targetState
+ // animation is loaded for the first time, AnimatedContent thinks target
+ // size is smaller and tries to shrink initial state animation
+ .using(sizeTransform = null)
+ } else {
+ // empty transition works because all remaining transitions are from IN_PROGRESS
+ // state which shares initial animation frame with both FINISHED and NOT_STARTED
+ EnterTransition.None togetherWith ExitTransition.None
+ }
+ }
+ ) { state ->
+ when (state) {
+ NOT_STARTED ->
+ EducationAnimation(
+ config.animations.educationResId,
+ config.colors.animationColors
+ )
+ IN_PROGRESS ->
+ FrozenSuccessAnimation(
+ config.animations.successResId,
+ config.colors.animationColors
+ )
+ FINISHED ->
+ SuccessAnimation(config.animations.successResId, config.colors.animationColors)
+ }
+ }
+ }
+}
+
+@Composable
+private fun FrozenSuccessAnimation(
+ @RawRes successAnimationId: Int,
+ animationProperties: LottieDynamicProperties
+) {
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
+ LottieAnimation(
+ composition = composition,
+ progress = { 0f }, // animation should freeze on 1st frame
+ dynamicProperties = animationProperties,
+ )
+}
+
+@Composable
+private fun EducationAnimation(
+ @RawRes educationAnimationId: Int,
+ animationProperties: LottieDynamicProperties
+) {
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
+ val progress by
+ animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
+ LottieAnimation(
+ composition = composition,
+ progress = { progress },
+ dynamicProperties = animationProperties,
+ )
+}
+
+@Composable
+private fun SuccessAnimation(
+ @RawRes successAnimationId: Int,
+ animationProperties: LottieDynamicProperties
+) {
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
+ val progress by animateLottieCompositionAsState(composition, iterations = 1)
+ LottieAnimation(
+ composition = composition,
+ progress = { progress },
+ dynamicProperties = animationProperties,
+ )
+}
+
+@Composable
+fun rememberColorFilterProperty(
+ layerName: String,
+ color: Color
+): LottieDynamicProperty<ColorFilter> {
+ return rememberLottieDynamicProperty(
+ LottieProperty.COLOR_FILTER,
+ value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
+ // "**" below means match zero or more layers, so ** layerName ** means find layer with that
+ // name at any depth
+ keyPath = arrayOf("**", layerName, "**")
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialComponents.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialComponents.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialComponents.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialComponents.kt
index f2276c8..01ad585 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialComponents.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialComponents.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.touchpad.tutorial.ui.composable
+package com.android.systemui.inputdevice.tutorial.ui.composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialScreenConfig.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialScreenConfig.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialScreenConfig.kt
index d76ceb9..0406bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialScreenConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialScreenConfig.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.touchpad.tutorial.ui.composable
+package com.android.systemui.inputdevice.tutorial.ui.composable
import androidx.annotation.RawRes
import androidx.annotation.StringRes
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/view/KeyboardTouchpadTutorialActivity.kt
new file mode 100644
index 0000000..3e382d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/view/KeyboardTouchpadTutorialActivity.kt
@@ -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.systemui.inputdevice.tutorial.ui.view
+
+import android.os.Bundle
+import android.view.WindowManager
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
+import androidx.compose.runtime.Composable
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel
+import java.util.Optional
+import javax.inject.Inject
+
+/**
+ * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that
+ * either of them are actually not connected when this is launched
+ */
+class KeyboardTouchpadTutorialActivity
+@Inject
+constructor(
+ private val viewModelFactory: KeyboardTouchpadTutorialViewModel.Factory,
+ private val touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
+) : ComponentActivity() {
+
+ private val vm by
+ viewModels<KeyboardTouchpadTutorialViewModel>(factoryProducer = { viewModelFactory })
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContent {
+ PlatformTheme {
+ KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) { finish() }
+ }
+ }
+ // required to handle 3+ fingers on touchpad
+ window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ vm.onOpened()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ vm.onClosed()
+ }
+}
+
+@Composable
+fun KeyboardTouchpadTutorialContainer(
+ vm: KeyboardTouchpadTutorialViewModel,
+ touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
+ closeTutorial: () -> Unit
+) {}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt
new file mode 100644
index 0000000..39b1ec0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/viewmodel/KeyboardTouchpadTutorialViewModel.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.inputdevice.tutorial.ui.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import java.util.Optional
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class KeyboardTouchpadTutorialViewModel(
+ private val gesturesInteractor: Optional<TouchpadGesturesInteractor>
+) : ViewModel() {
+
+ private val _screen = MutableStateFlow(Screen.BACK_GESTURE)
+ val screen: StateFlow<Screen> = _screen
+
+ fun goTo(screen: Screen) {
+ _screen.value = screen
+ }
+
+ fun onOpened() {
+ gesturesInteractor.ifPresent { it.disableGestures() }
+ }
+
+ fun onClosed() {
+ gesturesInteractor.ifPresent { it.enableGestures() }
+ }
+
+ class Factory
+ @Inject
+ constructor(private val gesturesInteractor: Optional<TouchpadGesturesInteractor>) :
+ ViewModelProvider.Factory {
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ return KeyboardTouchpadTutorialViewModel(gesturesInteractor) as T
+ }
+ }
+}
+
+enum class Screen {
+ BACK_GESTURE,
+ HOME_GESTURE,
+ ACTION_KEY
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
index cfe64e2..9f46846 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
@@ -16,11 +16,6 @@
package com.android.systemui.inputdevice.tutorial.data.model
-data class TutorialSchedulerInfo(
- val keyboard: DeviceSchedulerInfo = DeviceSchedulerInfo(),
- val touchpad: DeviceSchedulerInfo = DeviceSchedulerInfo()
-)
-
data class DeviceSchedulerInfo(var isLaunched: Boolean = false, var connectTime: Long? = null) {
val wasEverConnected: Boolean
get() = connectTime != null
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
index 31ff018..b9b3895 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -25,21 +25,32 @@
import androidx.datastore.preferences.preferencesDataStore
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
-import com.android.systemui.inputdevice.tutorial.data.model.TutorialSchedulerInfo
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
@SysUISingleton
class TutorialSchedulerRepository
@Inject
-constructor(@Application private val applicationContext: Context) {
+constructor(
+ @Application private val applicationContext: Context,
+ @Background private val backgroundScope: CoroutineScope
+) {
private val Context.dataStore: DataStore<Preferences> by
- preferencesDataStore(name = DATASTORE_NAME)
+ preferencesDataStore(name = DATASTORE_NAME, scope = backgroundScope)
- suspend fun loadData(): TutorialSchedulerInfo {
+ suspend fun isLaunched(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isLaunched
+
+ suspend fun wasEverConnected(deviceType: DeviceType): Boolean =
+ loadData()[deviceType]!!.wasEverConnected
+
+ suspend fun connectTime(deviceType: DeviceType): Long = loadData()[deviceType]!!.connectTime!!
+
+ private suspend fun loadData(): Map<DeviceType, DeviceSchedulerInfo> {
return applicationContext.dataStore.data.map { pref -> getSchedulerInfo(pref) }.first()
}
@@ -51,10 +62,10 @@
applicationContext.dataStore.edit { pref -> pref[getLaunchedKey(device)] = true }
}
- private fun getSchedulerInfo(pref: Preferences): TutorialSchedulerInfo {
- return TutorialSchedulerInfo(
- keyboard = getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
- touchpad = getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
+ private fun getSchedulerInfo(pref: Preferences): Map<DeviceType, DeviceSchedulerInfo> {
+ return mapOf(
+ DeviceType.KEYBOARD to getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
+ DeviceType.TOUCHPAD to getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index 05e1044..b3b8f21 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -16,23 +16,25 @@
package com.android.systemui.inputdevice.tutorial.domain.interactor
-import android.content.Context
-import android.content.Intent
+import android.os.SystemProperties
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
import com.android.systemui.keyboard.data.repository.KeyboardRepository
import com.android.systemui.touchpad.data.repository.TouchpadRepository
-import java.time.Duration
import java.time.Instant
import javax.inject.Inject
+import kotlin.time.Duration.Companion.hours
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
/**
@@ -43,62 +45,72 @@
class TutorialSchedulerInteractor
@Inject
constructor(
- @Application private val context: Context,
- @Application private val applicationScope: CoroutineScope,
- private val keyboardRepository: KeyboardRepository,
- private val touchpadRepository: TouchpadRepository,
- private val tutorialSchedulerRepository: TutorialSchedulerRepository
+ @Background private val backgroundScope: CoroutineScope,
+ keyboardRepository: KeyboardRepository,
+ touchpadRepository: TouchpadRepository,
+ private val repo: TutorialSchedulerRepository
) {
+ private val isAnyDeviceConnected =
+ mapOf(
+ KEYBOARD to keyboardRepository.isAnyKeyboardConnected,
+ TOUCHPAD to touchpadRepository.isAnyTouchpadConnected
+ )
+
fun start() {
- applicationScope.launch {
- val info = tutorialSchedulerRepository.loadData()
- if (!info.keyboard.isLaunched) {
- applicationScope.launch {
- schedule(
- keyboardRepository.isAnyKeyboardConnected,
- info.keyboard,
- DeviceType.KEYBOARD
- )
- }
- }
- if (!info.touchpad.isLaunched) {
- applicationScope.launch {
- schedule(
- touchpadRepository.isAnyTouchpadConnected,
- info.touchpad,
- DeviceType.TOUCHPAD
- )
- }
+ backgroundScope.launch {
+ // Merging two flows to ensure that launch tutorial is launched consecutively in order
+ // to avoid race condition
+ merge(touchpadScheduleFlow, keyboardScheduleFlow).collect {
+ val tutorialType = resolveTutorialType(it)
+ launchTutorial(tutorialType)
}
}
}
- private suspend fun schedule(
- isAnyDeviceConnected: Flow<Boolean>,
- info: DeviceSchedulerInfo,
- deviceType: DeviceType
- ) {
- if (!info.wasEverConnected) {
- waitForDeviceConnection(isAnyDeviceConnected)
- info.connectTime = Instant.now().toEpochMilli()
- tutorialSchedulerRepository.updateConnectTime(deviceType, info.connectTime!!)
+ private val touchpadScheduleFlow = flow {
+ if (!repo.isLaunched(TOUCHPAD)) {
+ schedule(TOUCHPAD)
+ emit(TOUCHPAD)
}
- delay(remainingTimeMillis(info.connectTime!!))
- waitForDeviceConnection(isAnyDeviceConnected)
- info.isLaunched = true
- tutorialSchedulerRepository.updateLaunch(deviceType)
- launchTutorial()
}
- private suspend fun waitForDeviceConnection(isAnyDeviceConnected: Flow<Boolean>): Boolean {
- return isAnyDeviceConnected.filter { it }.first()
+ private val keyboardScheduleFlow = flow {
+ if (!repo.isLaunched(KEYBOARD)) {
+ schedule(KEYBOARD)
+ emit(KEYBOARD)
+ }
}
- private fun launchTutorial() {
- val intent = Intent(TUTORIAL_ACTION)
- intent.addCategory(Intent.CATEGORY_DEFAULT)
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- context.startActivity(intent)
+ private suspend fun schedule(deviceType: DeviceType) {
+ if (!repo.wasEverConnected(deviceType)) {
+ waitForDeviceConnection(deviceType)
+ repo.updateConnectTime(deviceType, Instant.now().toEpochMilli())
+ }
+ delay(remainingTimeMillis(start = repo.connectTime(deviceType)))
+ waitForDeviceConnection(deviceType)
+ }
+
+ private suspend fun waitForDeviceConnection(deviceType: DeviceType) =
+ isAnyDeviceConnected[deviceType]!!.filter { it }.first()
+
+ private suspend fun launchTutorial(tutorialType: TutorialType) {
+ if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
+ repo.updateLaunch(KEYBOARD)
+ if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
+ repo.updateLaunch(TOUCHPAD)
+ // TODO: launch tutorial
+ Log.d(TAG, "Launch tutorial for $tutorialType")
+ }
+
+ private suspend fun resolveTutorialType(deviceType: DeviceType): TutorialType {
+ // Resolve the type of tutorial depending on which device are connected when the tutorial is
+ // launched. E.g. when the keyboard is connected for [LAUNCH_DELAY], both keyboard and
+ // touchpad are connected, we launch the tutorial for both.
+ if (repo.isLaunched(deviceType)) return TutorialType.NONE
+ val otherDevice = if (deviceType == KEYBOARD) TOUCHPAD else KEYBOARD
+ val isOtherDeviceConnected = isAnyDeviceConnected[otherDevice]!!.first()
+ if (!repo.isLaunched(otherDevice) && isOtherDeviceConnected) return TutorialType.BOTH
+ return if (deviceType == KEYBOARD) TutorialType.KEYBOARD else TutorialType.TOUCHPAD
}
private fun remainingTimeMillis(start: Long): Long {
@@ -107,7 +119,20 @@
}
companion object {
- const val TUTORIAL_ACTION = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
- private val LAUNCH_DELAY = Duration.ofHours(72).toMillis()
+ const val TAG = "TutorialSchedulerInteractor"
+ private val DEFAULT_LAUNCH_DELAY = 72.hours.inWholeMilliseconds
+ private val LAUNCH_DELAY: Long
+ get() =
+ SystemProperties.getLong(
+ "persist.peripheral_tutorial_delay_ms",
+ DEFAULT_LAUNCH_DELAY
+ )
+ }
+
+ enum class TutorialType {
+ KEYBOARD,
+ TOUCHPAD,
+ BOTH,
+ NONE
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3f9c98d..17c5977 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -42,6 +42,7 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
import static com.android.systemui.Flags.refactorGetCurrentUser;
+import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
import static com.android.systemui.Flags.translucentOccludingActivityFix;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
@@ -477,6 +478,7 @@
private boolean mUnlockingAndWakingFromDream = false;
private boolean mHideAnimationRun = false;
private boolean mHideAnimationRunning = false;
+ private boolean mIsKeyguardExitAnimationCanceled = false;
private SoundPool mLockSounds;
private int mLockSoundId;
@@ -1588,10 +1590,11 @@
setShowingLocked(!shouldWaitForProvisioning()
&& !mLockPatternUtils.isLockScreenDisabled(
mSelectedUserInteractor.getSelectedUserId()),
- true /* forceCallbacks */);
+ true /* forceCallbacks */, "setupLocked - keyguard service enabled");
} else {
// The system's keyguard is disabled or missing.
- setShowingLocked(false /* showing */, true /* forceCallbacks */);
+ setShowingLocked(false /* showing */, true /* forceCallbacks */,
+ "setupLocked - keyguard service disabled");
}
mKeyguardTransitions.register(
@@ -2334,6 +2337,12 @@
Log.e(TAG, "doKeyguard: we're still showing, but going away. Re-show the "
+ "keyguard rather than short-circuiting and resetting.");
} else {
+ // We're removing "reset" in the refactor - "resetting" the views will happen
+ // as a reaction to the root cause of the "reset" signal.
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ return;
+ }
+
// It's already showing, and we're not trying to show it while the screen is
// off. We can simply reset all of the views, but don't hide the bouncer in case
// the user is currently interacting with it.
@@ -2827,9 +2836,10 @@
playSound(mTrustedSoundId);
}
- private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
+ private void updateActivityLockScreenState(boolean showing, boolean aodShowing, String reason) {
mUiBgExecutor.execute(() -> {
- Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
+ Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ", "
+ + reason + ")");
if (KeyguardWmStateRefactor.isEnabled()) {
// Handled in WmLockscreenVisibilityManager if flag is enabled.
@@ -2889,7 +2899,7 @@
// Force if we're showing in the middle of unlocking, to ensure we end up in the
// correct state.
- setShowingLocked(true, hidingOrGoingAway /* force */);
+ setShowingLocked(true, hidingOrGoingAway /* force */, "handleShowInner");
mHiding = false;
if (!KeyguardWmStateRefactor.isEnabled()) {
@@ -3061,15 +3071,14 @@
mHiding = true;
mKeyguardGoingAwayRunnable.run();
} else {
- Log.d(TAG, "Hiding keyguard while occluded. Just hide the keyguard view and exit.");
-
if (!KeyguardWmStateRefactor.isEnabled()) {
mKeyguardViewControllerLazy.get().hide(
mSystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
mHideAnimation.getDuration());
}
- onKeyguardExitFinished();
+ onKeyguardExitFinished("Hiding keyguard while occluded. Just hide the keyguard "
+ + "view and exit.");
}
// It's possible that the device was unlocked (via BOUNCER or Fingerprint) while
@@ -3100,6 +3109,7 @@
Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ " fadeoutDuration=" + fadeoutDuration);
synchronized (KeyguardViewMediator.this) {
+ mIsKeyguardExitAnimationCanceled = false;
// Tell ActivityManager that we canceled the keyguard animation if
// handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard,
// unless we're animating the surface behind the keyguard and will be hiding the
@@ -3119,7 +3129,8 @@
Slog.w(TAG, "Failed to call onAnimationFinished", e);
}
}
- setShowingLocked(mShowing, true /* force */);
+ setShowingLocked(mShowing, true /* force */,
+ "handleStartKeyguardExitAnimation - canceled");
return;
}
mHiding = false;
@@ -3143,9 +3154,11 @@
Slog.w(TAG, "Failed to call onAnimationFinished", e);
}
}
- onKeyguardExitFinished();
- mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
- 0 /* fadeoutDuration */);
+ if (!mIsKeyguardExitAnimationCanceled) {
+ onKeyguardExitFinished("onRemoteAnimationFinished");
+ mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
+ 0 /* fadeoutDuration */);
+ }
mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@@ -3282,12 +3295,12 @@
anim.start();
});
- onKeyguardExitFinished();
+ onKeyguardExitFinished("remote animation disabled");
}
}
}
- private void onKeyguardExitFinished() {
+ private void onKeyguardExitFinished(String reason) {
if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()");
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
@@ -3295,7 +3308,7 @@
playSounds(false);
}
- setShowingLocked(false);
+ setShowingLocked(false, "onKeyguardExitFinished: " + reason);
mWakeAndUnlocking = false;
mDismissCallbackRegistry.notifyDismissSucceeded();
resetKeyguardDonePendingLocked();
@@ -3343,6 +3356,9 @@
// A lock is pending, meaning the keyguard exit animation was cancelled because we're
// re-locking. We should just end the surface-behind animation without exiting the
// keyguard. The pending lock will be handled by onFinishedGoingToSleep().
+ if (relockWithPowerButtonImmediately()) {
+ mIsKeyguardExitAnimationCanceled = true;
+ }
finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
maybeHandlePendingLock();
} else {
@@ -3391,12 +3407,13 @@
doKeyguardLocked(null);
finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
// Ensure WM is notified that we made a decision to show
- setShowingLocked(true /* showing */, true /* force */);
+ setShowingLocked(true /* showing */, true /* force */,
+ "exitKeyguardAndFinishSurfaceBehindRemoteAnimation - relocked");
return;
}
- onKeyguardExitFinished();
+ onKeyguardExitFinished("exitKeyguardAndFinishSurfaceBehindRemoteAnimation");
if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
Log.d(TAG, "onKeyguardExitRemoteAnimationFinished"
@@ -3453,7 +3470,7 @@
mSurfaceBehindRemoteAnimationRequested = false;
mKeyguardStateController.notifyKeyguardGoingAway(false);
if (mShowing) {
- setShowingLocked(true, true);
+ setShowingLocked(true, true, "hideSurfaceBehindKeyguard");
}
}
@@ -3799,7 +3816,7 @@
// update lock screen state in ATMS here, otherwise ATMS tries to resume activities when
// enabling doze state.
if (mShowing || !mPendingLock || !mDozeParameters.canControlUnlockedScreenOff()) {
- setShowingLocked(mShowing);
+ setShowingLocked(mShowing, "setDozing");
}
}
@@ -3809,7 +3826,7 @@
// is 1f), then show the activity lock screen.
if (mAnimatingScreenOff && mDozing && linear == 1f) {
mAnimatingScreenOff = false;
- setShowingLocked(mShowing, true);
+ setShowingLocked(mShowing, true, "onDozeAmountChanged");
}
}
@@ -3847,11 +3864,11 @@
}
}
- void setShowingLocked(boolean showing) {
- setShowingLocked(showing, false /* forceCallbacks */);
+ void setShowingLocked(boolean showing, String reason) {
+ setShowingLocked(showing, false /* forceCallbacks */, reason);
}
- private void setShowingLocked(boolean showing, boolean forceCallbacks) {
+ private void setShowingLocked(boolean showing, boolean forceCallbacks, String reason) {
final boolean aodShowing = mDozing && !mWakeAndUnlocking;
final boolean notifyDefaultDisplayCallbacks = showing != mShowing || forceCallbacks;
final boolean updateActivityLockScreenState = showing != mShowing
@@ -3862,9 +3879,8 @@
notifyDefaultDisplayCallbacks(showing);
}
if (updateActivityLockScreenState) {
- updateActivityLockScreenState(showing, aodShowing);
+ updateActivityLockScreenState(showing, aodShowing, reason);
}
-
}
private void notifyDefaultDisplayCallbacks(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
new file mode 100644
index 0000000..443e9876
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
@@ -0,0 +1,11 @@
+set noparent
+
+# Bug component: 78010
+
+amiko@google.com
+beverlyt@google.com
+bhinegardner@google.com
+chandruis@google.com
+jglazier@google.com
+mpietal@google.com
+tsuji@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index de60c11..797a4ec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -285,7 +285,7 @@
state: TransitionState
) {
if (updateTransitionId != transitionId) {
- Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
+ Log.e(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
return
}
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 51d92f0..5dc020f 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
@@ -300,7 +300,9 @@
swipeToDismissInteractor.dismissFling
.filterNotNull()
.filterRelevantKeyguardState()
- .collect { _ -> startTransitionTo(KeyguardState.GONE) }
+ .collect { _ ->
+ startTransitionTo(KeyguardState.GONE, ownerReason = "dismissFling != null")
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index aea57ce..905ca8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -135,7 +135,7 @@
}
}
- private suspend fun FromOccludedTransitionInteractor.startTransitionToLockscreenOrHub(
+ private suspend fun startTransitionToLockscreenOrHub(
isIdleOnCommunal: Boolean,
showCommunalFromOccluded: Boolean,
dreamFromOccluded: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 31236a4..0682d87 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -433,10 +433,6 @@
value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_UI_FOR_AIWP)
),
KeyguardPickerFlag(
- name = Contract.FlagsTable.FLAG_NAME_TRANSIT_CLOCK,
- value = featureFlags.isEnabled(Flags.TRANSIT_CLOCK)
- ),
- KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_PAGE_TRANSITIONS,
value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PAGE_TRANSITIONS)
),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index efdae62..6ff369e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -162,6 +162,25 @@
}
}
}
+
+ // Safety: When any transition is FINISHED, ensure all other transitionValue flows other
+ // than the FINISHED state are reset to a value of 0f. There have been rare but severe
+ // bugs that get the device stuck in a bad state when these are not properly reset.
+ scope.launch {
+ repository.transitions
+ .filter { it.transitionState == TransitionState.FINISHED }
+ .collect {
+ for (state in KeyguardState.entries) {
+ if (state != it.to) {
+ val flow = getTransitionValueFlow(state)
+ val replayCache = flow.replayCache
+ if (!replayCache.isEmpty() && replayCache.last() != 0f) {
+ flow.emit(0f)
+ }
+ }
+ }
+ }
+ }
}
fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
index 86e4115..906d586 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
@@ -19,14 +19,15 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
/**
* Handles logic around the swipe to dismiss gesture, where the user swipes up on the dismissable
@@ -53,12 +54,14 @@
shadeRepository.currentFling
.sample(
transitionInteractor.startedKeyguardState,
- keyguardInteractor.isKeyguardDismissible
+ keyguardInteractor.isKeyguardDismissible,
+ keyguardInteractor.statusBarState,
)
- .filter { (flingInfo, startedState, keyguardDismissable) ->
+ .filter { (flingInfo, startedState, keyguardDismissable, statusBarState) ->
flingInfo != null &&
- !flingInfo.expand &&
- startedState == KeyguardState.LOCKSCREEN &&
+ !flingInfo.expand &&
+ statusBarState != StatusBarState.SHADE_LOCKED &&
+ startedState == KeyguardState.LOCKSCREEN &&
keyguardDismissable
}
.map { (flingInfo, _) -> flingInfo }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index 9dc77d3..fb97191 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ExperimentalCoroutinesApi
@@ -52,7 +53,11 @@
}
}
- launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } }
+ if (SceneContainerFlag.isEnabled) {
+ view.alpha = 1f
+ } else {
+ launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } }
+ }
}
}
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 a250b22..91a7f7f 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
@@ -37,13 +37,13 @@
import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
-import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scrim.ScrimView
import dagger.Lazy
import javax.inject.Inject
@@ -67,7 +67,6 @@
private val alternateBouncerDependencies: Lazy<AlternateBouncerDependencies>,
private val windowManager: Lazy<WindowManager>,
private val layoutInflater: Lazy<LayoutInflater>,
- private val dismissCallbackRegistry: DismissCallbackRegistry,
) : CoreStartable {
private val layoutParams: WindowManager.LayoutParams
get() =
@@ -95,9 +94,10 @@
private var alternateBouncerView: ConstraintLayout? = null
override fun start() {
- if (!DeviceEntryUdfpsRefactor.isEnabled) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) {
return
}
+
applicationScope.launch("$TAG#alternateBouncerWindowViewModel") {
alternateBouncerWindowViewModel.get().alternateBouncerWindowRequired.collect {
addAlternateBouncerWindowView ->
@@ -110,7 +110,7 @@
bind(alternateBouncerView!!, alternateBouncerDependencies.get())
} else {
removeViewFromWindowManager()
- alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
+ alternateBouncerDependencies.get().viewModel.onRemovedFromWindow()
}
}
}
@@ -144,7 +144,7 @@
private val onAttachAddBackGestureHandler =
object : View.OnAttachStateChangeListener {
private val onBackInvokedCallback: OnBackInvokedCallback = OnBackInvokedCallback {
- onBackRequested()
+ alternateBouncerDependencies.get().viewModel.onBackRequested()
}
override fun onViewAttachedToWindow(view: View) {
@@ -161,14 +161,12 @@
.findOnBackInvokedDispatcher()
?.unregisterOnBackInvokedCallback(onBackInvokedCallback)
}
-
- fun onBackRequested() {
- alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
- dismissCallbackRegistry.notifyDismissCancelled()
- }
}
private fun addViewToWindowManager() {
+ if (SceneContainerFlag.isEnabled) {
+ return
+ }
if (alternateBouncerView != null) {
return
}
@@ -190,6 +188,7 @@
if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
return
}
+
optionallyAddUdfpsViews(
view = view,
udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel,
@@ -202,12 +201,13 @@
viewModel = alternateBouncerDependencies.messageAreaViewModel,
)
- val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
+ val scrim: ScrimView = view.requireViewById(R.id.alternate_bouncer_scrim)
val viewModel = alternateBouncerDependencies.viewModel
val swipeUpAnywhereGestureHandler =
alternateBouncerDependencies.swipeUpAnywhereGestureHandler
val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector
- view.repeatWhenAttached { alternateBouncerViewContainer ->
+
+ view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch("$TAG#viewModel.registerForDismissGestures") {
viewModel.registerForDismissGestures.collect { registerForDismissGestures ->
@@ -216,11 +216,11 @@
swipeTag
) { _ ->
alternateBouncerDependencies.powerInteractor.onUserTouch()
- viewModel.showPrimaryBouncer()
+ viewModel.onTapped()
}
tapGestureDetector.addOnGestureDetectedCallback(tapTag) { _ ->
alternateBouncerDependencies.powerInteractor.onUserTouch()
- viewModel.showPrimaryBouncer()
+ viewModel.onTapped()
}
} else {
swipeUpAnywhereGestureHandler.removeOnGestureDetectedCallback(
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 830ef3b..76d3389 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
@@ -26,8 +26,8 @@
import com.android.systemui.common.ui.view.LongPressHandlingView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
object KeyguardLongPressViewBinder {
/**
@@ -46,7 +46,6 @@
onSingleTap: () -> Unit,
falsingManager: FalsingManager,
) {
- view.contentDescription = view.resources.getString(R.string.accessibility_desc_lock_screen)
view.accessibilityHintLongPressAction =
AccessibilityNodeInfo.AccessibilityAction(
AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
@@ -54,8 +53,15 @@
)
view.listener =
object : LongPressHandlingView.Listener {
- override fun onLongPressDetected(view: View, x: Int, y: Int, isA11yAction: Boolean) {
- if (!isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+ override fun onLongPressDetected(
+ view: View,
+ x: Int,
+ y: Int,
+ isA11yAction: Boolean
+ ) {
+ if (
+ !isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
+ ) {
return
}
@@ -76,6 +82,12 @@
launch("$TAG#viewModel.isLongPressHandlingEnabled") {
viewModel.isLongPressHandlingEnabled.collect { isEnabled ->
view.setLongPressHandlingEnabled(isEnabled)
+ view.contentDescription =
+ if (isEnabled) {
+ view.resources.getString(R.string.accessibility_desc_lock_screen)
+ } else {
+ null
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index df0b3dc..4908dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shared.recents.utilities.Utilities.clamp
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,6 +51,7 @@
private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
val alpha: Flow<Float> =
alternateBouncerViewModel.transitionToAlternateBouncerProgress.map {
+ SceneContainerFlag.assertInLegacyMode()
clamp(it * 2f, 0f, 1f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 470f17b..7b0b23f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -18,15 +18,20 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Color
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
@ExperimentalCoroutinesApi
class AlternateBouncerViewModel
@@ -34,12 +39,18 @@
constructor(
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
+ alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>,
) {
// When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
private val alternateBouncerScrimAlpha = .66f
+ /** Reports the alternate bouncer visible state if the scene container flag is enabled. */
+ val isVisible: Flow<Boolean> =
+ alternateBouncerInteractor.get().isVisible.onEach { SceneContainerFlag.assertInNewMode() }
+
/** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
- val transitionToAlternateBouncerProgress =
+ val transitionToAlternateBouncerProgress: Flow<Float> =
keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER)
/** An observable for the scrim alpha. */
@@ -51,11 +62,16 @@
val registerForDismissGestures: Flow<Boolean> =
transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged()
- fun showPrimaryBouncer() {
+ fun onTapped() {
statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
}
- fun hideAlternateBouncer() {
+ fun onRemovedFromWindow() {
statusBarKeyguardViewManager.hideAlternateBouncer(false)
}
+
+ fun onBackRequested() {
+ statusBarKeyguardViewManager.hideAlternateBouncer(false)
+ dismissCallbackRegistry.notifyDismissCancelled()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index a460d51..9d8a7a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -51,7 +51,8 @@
onCancel = { 0f },
)
- val lockscreenAlpha: Flow<Float> = shortcutsAlpha
+ // Show immediately to avoid what can appear to be a flicker on device wakeup
+ val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index 661da6d..c2b5d98 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -227,13 +227,33 @@
}
/**
+ * Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is
+ * at least at [state] (or immediately after calling this function if the window is already at least
+ * at [state]), automatically canceling the work when the window is no longer at least at that
+ * state.
+ *
+ * [block] may be run multiple times, running once per every time this` [View]'s Window's
+ * [WindowLifecycleState] becomes at least at [state].
+ */
+suspend fun View.repeatOnWindowLifecycle(
+ state: WindowLifecycleState,
+ block: suspend CoroutineScope.() -> Unit,
+): Nothing {
+ when (state) {
+ WindowLifecycleState.ATTACHED -> repeatWhenAttachedToWindow(block)
+ WindowLifecycleState.VISIBLE -> repeatWhenWindowIsVisible(block)
+ WindowLifecycleState.FOCUSED -> repeatWhenWindowHasFocus(block)
+ }
+}
+
+/**
* Runs the given [block] every time the [View] becomes attached (or immediately after calling this
* function, if the view was already attached), automatically canceling the work when the view
* becomes detached.
*
* Only use from the main thread.
*
- * The [block] may be run multiple times, running once per every time the view is attached.
+ * [block] may be run multiple times, running once per every time the view is attached.
*/
@MainThread
suspend fun View.repeatWhenAttachedToWindow(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -249,7 +269,7 @@
*
* Only use from the main thread.
*
- * The [block] may be run multiple times, running once per every time the window becomes visible.
+ * [block] may be run multiple times, running once per every time the window becomes visible.
*/
@MainThread
suspend fun View.repeatWhenWindowIsVisible(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -265,7 +285,7 @@
*
* Only use from the main thread.
*
- * The [block] may be run multiple times, running once per every time the window is focused.
+ * [block] may be run multiple times, running once per every time the window is focused.
*/
@MainThread
suspend fun View.repeatWhenWindowHasFocus(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -274,6 +294,21 @@
awaitCancellation() // satisfies return type of Nothing
}
+/** Lifecycle states for a [View]'s interaction with a [android.view.Window]. */
+enum class WindowLifecycleState {
+ /** Indicates that the [View] is attached to a [android.view.Window]. */
+ ATTACHED,
+ /**
+ * Indicates that the [View] is attached to a [android.view.Window], and the window is visible.
+ */
+ VISIBLE,
+ /**
+ * Indicates that the [View] is attached to a [android.view.Window], and the window is visible
+ * and focused.
+ */
+ FOCUSED
+}
+
private val View.isAttached
get() = conflatedCallbackFlow {
val onAttachListener =
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
index 0af5fea..7731481 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
@@ -16,9 +16,10 @@
package com.android.systemui.lifecycle
+import android.view.View
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.remember
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** Base class for all System UI view-models. */
abstract class SysUiViewModel : SafeActivatable() {
@@ -37,8 +38,20 @@
fun <T : SysUiViewModel> rememberViewModel(
key: Any = Unit,
factory: () -> T,
-): T {
- val instance = remember(key) { factory() }
- LaunchedEffect(instance) { instance.activate() }
- return instance
-}
+): T = rememberActivated(key, factory)
+
+/**
+ * Invokes [block] in a new coroutine with a new [SysUiViewModel] that is automatically activated
+ * whenever `this` [View]'s Window's [WindowLifecycleState] is at least at
+ * [minWindowLifecycleState], and is automatically canceled once that is no longer the case.
+ */
+suspend fun <T : SysUiViewModel> View.viewModel(
+ minWindowLifecycleState: WindowLifecycleState,
+ factory: () -> T,
+ block: suspend CoroutineScope.(T) -> Unit,
+): Nothing =
+ repeatOnWindowLifecycle(minWindowLifecycleState) {
+ val instance = factory()
+ launch { instance.activate() }
+ block(instance)
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index e17c0bb..9bea6e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.content.res.Configuration
+import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
@@ -125,6 +126,7 @@
/** single pane media container placed at the top of the notifications list */
var singlePaneContainer: MediaContainerView? = null
private set
+
private var splitShadeContainer: ViewGroup? = null
/**
@@ -185,6 +187,13 @@
}
}
+ fun isWithinMediaViewBounds(x: Int, y: Int): Boolean {
+ val bounds = Rect()
+ mediaHost.hostView.getBoundsOnScreen(bounds)
+
+ return bounds.contains(x, y)
+ }
+
fun refreshMediaPosition(reason: String) {
val currentState = statusBarStateController.state
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 c5d7b25..62a7218 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
@@ -888,8 +888,6 @@
heightInSceneContainerPx = height
mediaCarouselScrollHandler.playerWidthPlusPadding =
width + context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
- mediaContent.minimumWidth = widthInSceneContainerPx
- mediaContent.minimumHeight = heightInSceneContainerPx
updatePlayers(recreateMedia = true)
}
@@ -1637,6 +1635,7 @@
"only active ${desiredHostState?.showsOnlyActiveMedia}"
)
println("isSwipedAway: ${MediaPlayerData.isSwipedAway}")
+ println("allowMediaPlayerOnLockScreen: $allowMediaPlayerOnLockScreen")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 88a28bf..091b886 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -1210,7 +1210,6 @@
(onCommunalNotDreaming && qsExpansion == 0.0f) || onCommunalDreamingAndShadeExpanding
val location =
when {
- mediaFlags.isSceneContainerEnabled() -> desiredLocation
dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
onCommunal -> LOCATION_COMMUNAL_HUB
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
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 b48b409..875e505 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -94,6 +94,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
@@ -173,6 +174,7 @@
private float mActiveRadius;
private FeatureFlags mFeatureFlags;
private UserTracker mUserTracker;
+ private VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor;
public enum BroadcastNotifyDialog {
ACTION_FIRST_LAUNCH,
@@ -195,6 +197,7 @@
PowerExemptionManager powerExemptionManager,
KeyguardManager keyGuardManager,
FeatureFlags featureFlags,
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor,
UserTracker userTracker) {
mContext = context;
mPackageName = packageName;
@@ -209,6 +212,7 @@
mFeatureFlags = featureFlags;
mUserTracker = userTracker;
mToken = token;
+ mVolumePanelGlobalStateInteractor = volumePanelGlobalStateInteractor;
InfoMediaManager imm =
InfoMediaManager.createInstance(mContext, packageName, userHandle, lbm, token);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
@@ -436,7 +440,7 @@
launchIntent.putExtra(EXTRA_ROUTE_ID, routeId);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mCallback.dismissDialog();
- mActivityStarter.startActivity(launchIntent, true, controller);
+ startActivity(launchIntent, controller);
}
}
@@ -447,7 +451,7 @@
if (launchIntent != null) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mCallback.dismissDialog();
- mActivityStarter.startActivity(launchIntent, true, controller);
+ startActivity(launchIntent, controller);
}
}
@@ -951,10 +955,10 @@
deepLinkIntent.putExtra(
Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
PAGE_CONNECTED_DEVICES_KEY);
- mActivityStarter.startActivity(deepLinkIntent, true, controller);
+ startActivity(deepLinkIntent, controller);
return;
}
- mActivityStarter.startActivity(launchIntent, true, controller);
+ startActivity(launchIntent, controller);
}
void launchLeBroadcastNotifyDialog(View mediaOutputDialog, BroadcastSender broadcastSender,
@@ -998,6 +1002,7 @@
mPowerExemptionManager,
mKeyGuardManager,
mFeatureFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
broadcastSender, controller);
@@ -1244,6 +1249,13 @@
return !device.isVolumeFixed();
}
+ private void startActivity(Intent intent, ActivityTransitionAnimator.Controller controller) {
+ // Media Output dialog can be shown from the volume panel. This makes sure the panel is
+ // closed when navigating to another activity, so it doesn't stays on top of it
+ mVolumePanelGlobalStateInteractor.setVisible(false);
+ mActivityStarter.startActivity(intent, true, controller);
+ }
+
@Override
public void onDevicesUpdated(List<NearbyDevice> nearbyDevices) throws RemoteException {
mNearbyDeviceInfoMap.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 4f062af..92db804 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -17,8 +17,8 @@
package com.android.systemui.media.taptotransfer.receiver
import android.animation.TimeInterpolator
-import android.annotation.SuppressLint
import android.animation.ValueAnimator
+import android.annotation.SuppressLint
import android.app.StatusBarManager
import android.content.Context
import android.graphics.Rect
@@ -29,15 +29,15 @@
import android.os.PowerManager
import android.view.Gravity
import android.view.View
+import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
-import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
-import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
-import com.android.internal.widget.CachingIconView
-import com.android.systemui.res.R
import com.android.app.animation.Interpolators
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
+import com.android.internal.widget.CachingIconView
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.ui.binder.TintedIconViewBinder
import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +46,7 @@
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.media.taptotransfer.common.MediaTttIcon
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
+import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -71,7 +72,7 @@
private val commandQueue: CommandQueue,
context: Context,
logger: MediaTttReceiverLogger,
- windowManager: WindowManager,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -88,7 +89,7 @@
) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
context,
logger,
- windowManager,
+ viewCaptureAwareWindowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index d6affd2..228b576 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -32,6 +32,10 @@
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
+import android.widget.ImageView
+import androidx.annotation.ColorRes
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
@@ -52,6 +56,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.AsyncActivityLauncher
+import java.lang.IllegalArgumentException
import javax.inject.Inject
class MediaProjectionAppSelectorActivity(
@@ -116,6 +121,7 @@
super.onCreate(savedInstanceState)
controller.init()
+ setIcon()
// we override AppList's AccessibilityDelegate set in ResolverActivity.onCreate because in
// our case this delegate must extend RecyclerViewAccessibilityDelegate, otherwise
// RecyclerView scrolling is broken
@@ -298,6 +304,29 @@
override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
recentsViewController.createView(parent)
+ /** Set up intent for the [ChooserActivity] */
+ private fun Intent.configureChooserIntent(
+ resources: Resources,
+ hostUserHandle: UserHandle,
+ personalProfileUserHandle: UserHandle,
+ ) {
+ // Specify the query intent to show icons for all apps on the chooser screen
+ val queryIntent = Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
+ putExtra(Intent.EXTRA_INTENT, queryIntent)
+
+ // Update the title of the chooser
+ putExtra(Intent.EXTRA_TITLE, resources.getString(titleResId))
+
+ // Select host app's profile tab by default
+ val selectedProfile =
+ if (hostUserHandle == personalProfileUserHandle) {
+ PROFILE_PERSONAL
+ } else {
+ PROFILE_WORK
+ }
+ putExtra(EXTRA_SELECTED_PROFILE, selectedProfile)
+ }
+
private val hostUserHandle: UserHandle
get() {
val extras =
@@ -321,6 +350,54 @@
return intent.getIntExtra(EXTRA_HOST_APP_UID, /* defaultValue= */ -1)
}
+ /**
+ * The type of screen sharing being performed. Used to show the right text and icon in the
+ * activity.
+ */
+ private val screenShareType: ScreenShareType?
+ get() {
+ if (!intent.hasExtra(EXTRA_SCREEN_SHARE_TYPE)) {
+ return null
+ } else {
+ val type = intent.getStringExtra(EXTRA_SCREEN_SHARE_TYPE) ?: return null
+ return try {
+ enumValueOf<ScreenShareType>(type)
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+ }
+ }
+
+ @get:StringRes
+ private val titleResId: Int
+ get() =
+ when (screenShareType) {
+ ScreenShareType.ShareToApp ->
+ R.string.media_projection_entry_share_app_selector_title
+ ScreenShareType.SystemCast ->
+ R.string.media_projection_entry_cast_app_selector_title
+ ScreenShareType.ScreenRecord -> R.string.screenrecord_app_selector_title
+ null -> R.string.screen_share_generic_app_selector_title
+ }
+
+ @get:DrawableRes
+ private val iconResId: Int
+ get() =
+ when (screenShareType) {
+ ScreenShareType.ShareToApp -> R.drawable.ic_present_to_all
+ ScreenShareType.SystemCast -> R.drawable.ic_cast_connected
+ ScreenShareType.ScreenRecord -> R.drawable.ic_screenrecord
+ null -> R.drawable.ic_present_to_all
+ }
+
+ @get:ColorRes
+ private val iconTintResId: Int?
+ get() =
+ when (screenShareType) {
+ ScreenShareType.ScreenRecord -> R.color.screenrecord_icon_color
+ else -> null
+ }
+
companion object {
const val TAG = "MediaProjectionAppSelectorActivity"
@@ -343,30 +420,18 @@
const val EXTRA_HOST_APP_UID = "launched_from_host_uid"
const val KEY_CAPTURE_TARGET = "capture_region"
- /** Set up intent for the [ChooserActivity] */
- private fun Intent.configureChooserIntent(
- resources: Resources,
- hostUserHandle: UserHandle,
- personalProfileUserHandle: UserHandle
- ) {
- // Specify the query intent to show icons for all apps on the chooser screen
- val queryIntent =
- Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
- putExtra(Intent.EXTRA_INTENT, queryIntent)
+ /**
+ * The type of screen sharing being performed.
+ *
+ * The value set for this extra should match the name of a [ScreenShareType].
+ */
+ const val EXTRA_SCREEN_SHARE_TYPE = "screen_share_type"
+ }
- // Update the title of the chooser
- val title = resources.getString(R.string.screen_share_permission_app_selector_title)
- putExtra(Intent.EXTRA_TITLE, title)
-
- // Select host app's profile tab by default
- val selectedProfile =
- if (hostUserHandle == personalProfileUserHandle) {
- PROFILE_PERSONAL
- } else {
- PROFILE_WORK
- }
- putExtra(EXTRA_SELECTED_PROFILE, selectedProfile)
- }
+ private fun setIcon() {
+ val iconView = findViewById<ImageView>(R.id.media_projection_app_selector_icon) ?: return
+ iconView.setImageResource(iconResId)
+ iconTintResId?.let { iconView.setColorFilter(this.resources.getColor(it, this.theme)) }
}
private fun setAppListAccessibilityDelegate() {
@@ -406,4 +471,14 @@
return delegate.onRequestSendAccessibilityEvent(host, child, event)
}
}
+
+ /** Enum describing what type of app screen sharing is being performed. */
+ enum class ScreenShareType {
+ /** The selected app will be cast to another device. */
+ SystemCast,
+ /** The selected app will be shared to another app on the device. */
+ ShareToApp,
+ /** The selected app will be recorded. */
+ ScreenRecord,
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 46aa064..2ce7044 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -27,7 +27,6 @@
import android.window.RemoteTransition
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.Flags.pssAppSelectorAbruptExitFix
import com.android.systemui.Flags.pssAppSelectorRecentsSplitScreen
import com.android.systemui.display.naturalBounds
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -160,7 +159,7 @@
private fun createAnimation(task: RecentTask, view: View): ActivityOptions =
- if (pssAppSelectorAbruptExitFix() && task.isForegroundTask) {
+ if (task.isForegroundTask) {
// When the selected task is in the foreground, the scale up animation doesn't work.
// We fallback to the default close animation.
ActivityOptions.makeCustomTaskAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 3c83db3..18c6f53 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -73,8 +73,7 @@
import dagger.Lazy;
-public class MediaProjectionPermissionActivity extends Activity
- implements DialogInterface.OnClickListener {
+public class MediaProjectionPermissionActivity extends Activity {
private static final String TAG = "MediaProjectionPermissionActivity";
private static final float MAX_APP_NAME_SIZE_PX = 500f;
private static final String ELLIPSIS = "\u2026";
@@ -269,7 +268,8 @@
Consumer<BaseMediaProjectionPermissionDialogDelegate<AlertDialog>> onStartRecordingClicked =
dialog -> {
ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
- grantMediaProjectionPermission(selectedOption.getMode());
+ grantMediaProjectionPermission(
+ selectedOption.getMode(), hasCastingCapabilities);
};
Runnable onCancelClicked = () -> finish(RECORD_CANCEL, /* projection= */ null);
if (hasCastingCapabilities) {
@@ -305,19 +305,6 @@
}
}
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (which == AlertDialog.BUTTON_POSITIVE) {
- grantMediaProjectionPermission(ENTIRE_SCREEN);
- } else {
- if (mDialog != null) {
- mDialog.dismiss();
- }
- setResult(RESULT_CANCELED);
- finish(RECORD_CANCEL, /* projection= */ null);
- }
- }
-
private void setUpDialog(AlertDialog dialog) {
SystemUIDialog.registerDismissListener(dialog);
SystemUIDialog.applyFlags(dialog);
@@ -345,25 +332,21 @@
return false;
}
- private void grantMediaProjectionPermission(int screenShareMode) {
+ private void grantMediaProjectionPermission(
+ int screenShareMode, boolean hasCastingCapabilities) {
try {
+ IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(
+ mUid, mPackageName, mReviewGrantedConsentRequired);
if (screenShareMode == ENTIRE_SCREEN) {
- final IMediaProjection projection =
- MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
- mReviewGrantedConsentRequired);
final Intent intent = new Intent();
- intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
- projection.asBinder());
+ setCommonIntentExtras(intent, hasCastingCapabilities, projection);
setResult(RESULT_OK, intent);
finish(RECORD_CONTENT_DISPLAY, projection);
}
if (screenShareMode == SINGLE_APP) {
- IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(
- mUid, mPackageName, mReviewGrantedConsentRequired);
final Intent intent = new Intent(this,
MediaProjectionAppSelectorActivity.class);
- intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
- projection.asBinder());
+ setCommonIntentExtras(intent, hasCastingCapabilities, projection);
intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
getHostUserHandle());
intent.putExtra(
@@ -391,6 +374,19 @@
}
}
+ private void setCommonIntentExtras(
+ Intent intent,
+ boolean hasCastingCapabilities,
+ IMediaProjection projection) throws RemoteException {
+ intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
+ projection.asBinder());
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE,
+ hasCastingCapabilities
+ ? MediaProjectionAppSelectorActivity.ScreenShareType.SystemCast.name()
+ : MediaProjectionAppSelectorActivity.ScreenShareType.ShareToApp.name());
+ }
+
private UserHandle getHostUserHandle() {
return UserHandle.getUserHandleForUid(getLaunchedFromUid());
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 13a786a..ac878c2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -38,6 +38,7 @@
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -516,7 +517,7 @@
* @return Whether the IME is shown on top of the screen given the {@code vis} flag of
* {@link InputMethodService} and the keyguard states.
*/
- public boolean isImeShown(int vis) {
+ public boolean isImeShown(@ImeWindowVisibility int vis) {
View shadeWindowView = mNotificationShadeWindowController.getWindowRootView();
boolean isKeyguardShowing = mKeyguardStateController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index e2ba761..a8b979e0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -16,11 +16,16 @@
package com.android.systemui.navigationbar;
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
+
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
@@ -28,6 +33,7 @@
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.res.R;
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -73,4 +79,15 @@
static WindowManager provideWindowManager(@DisplayId Context context) {
return context.getSystemService(WindowManager.class);
}
+
+ /** A ViewCaptureAwareWindowManager specific to the display's context. */
+ @Provides
+ @NavigationBarScope
+ @DisplayId
+ static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
+ @DisplayId WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) {
+ return new ViewCaptureAwareWindowManager(windowManager,
+ /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
+ /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 15b1e4d..cb0bb4a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -42,6 +42,8 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
@@ -424,8 +426,8 @@
}
@Override
- public void setImeWindowStatus(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
boolean imeShown = mNavBarHelper.isImeShown(vis);
if (!imeShown) {
// Count imperceptible changes as visible so we transition taskbar out quickly.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 0fe4d36..388272f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -1058,8 +1058,6 @@
mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
mEdgeBackPlugin.onMotionEvent(ev);
dispatchToBackAnimation(ev);
- mOverviewProxyService.updateContextualEduStats(mIsTrackpadThreeFingerSwipe,
- GestureType.BACK);
}
if (mLogGesture || mIsTrackpadThreeFingerSwipe) {
mDownPoint.set(ev.getX(), ev.getY());
@@ -1136,6 +1134,8 @@
if (mAllowGesture) {
if (mBackAnimation != null) {
mBackAnimation.onThresholdCrossed();
+ mOverviewProxyService.updateContextualEduStats(
+ mIsTrackpadThreeFingerSwipe, GestureType.BACK);
} else {
pilferPointers();
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index 7b248eb..c706c3e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -68,6 +68,8 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -102,6 +104,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
@@ -196,6 +199,7 @@
private final Context mContext;
private final Bundle mSavedState;
private final WindowManager mWindowManager;
+ private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final AccessibilityManager mAccessibilityManager;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
@@ -556,6 +560,7 @@
@Nullable Bundle savedState,
@DisplayId Context context,
@DisplayId WindowManager windowManager,
+ @DisplayId ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
Lazy<AssistManager> assistManagerLazy,
AccessibilityManager accessibilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -601,6 +606,7 @@
mContext = context;
mSavedState = savedState;
mWindowManager = windowManager;
+ mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mAccessibilityManager = accessibilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
@@ -721,7 +727,7 @@
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
- mWindowManager.addView(mFrame,
+ mViewCaptureAwareWindowManager.addView(mFrame,
getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
.getRotation()));
mDisplayId = mContext.getDisplayId();
@@ -764,7 +770,7 @@
mCommandQueue.removeCallback(this);
Trace.beginSection("NavigationBar#removeViewImmediate");
try {
- mWindowManager.removeViewImmediate(mView.getRootView());
+ mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView());
} finally {
Trace.endSection();
}
@@ -866,7 +872,7 @@
if (mOrientationHandle != null) {
resetSecondaryHandle();
getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
- mWindowManager.removeView(mOrientationHandle);
+ mViewCaptureAwareWindowManager.removeView(mOrientationHandle);
mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
}
@@ -937,7 +943,7 @@
mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
| WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
- mWindowManager.addView(mOrientationHandle, mOrientationParams);
+ mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams);
mOrientationHandle.setVisibility(View.GONE);
logNavbarOrientation("initSecondaryHomeHandleForRotation");
@@ -1094,8 +1100,8 @@
// ----- CommandQueue Callbacks -----
@Override
- public void setImeWindowStatus(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
index 2d2b869..9fb09c0 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
@@ -29,9 +29,6 @@
/**
* Models the UI state for the user actions that the user can perform to navigate to other scenes.
- *
- * Different from the [NotificationsShadeSceneContentViewModel] which models the _content_ of the
- * scene.
*/
class NotificationsShadeSceneActionsViewModel
@AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneContentViewModel.kt
deleted file mode 100644
index c1c7320..0000000
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneContentViewModel.kt
+++ /dev/null
@@ -1,49 +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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.notifications.ui.viewmodel
-
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.shade.ui.viewmodel.BaseShadeSceneViewModel
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-/**
- * Models UI state used to render the content of the notifications shade scene.
- *
- * Different from [NotificationsShadeSceneActionsViewModel], which only models user actions that can
- * be performed to navigate to other scenes.
- */
-class NotificationsShadeSceneContentViewModel
-@AssistedInject
-constructor(
- deviceEntryInteractor: DeviceEntryInteractor,
- sceneInteractor: SceneInteractor,
-) :
- BaseShadeSceneViewModel(
- deviceEntryInteractor,
- sceneInteractor,
- ) {
-
- @AssistedFactory
- interface Factory {
- fun create(): NotificationsShadeSceneContentViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 9939075..1511f31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -259,6 +259,13 @@
}
/**
+ * @return height with the squishiness fraction applied.
+ */
+ int getSquishedQqsHeight() {
+ return mHeader.getSquishedHeight();
+ }
+
+ /**
* Returns the size of QS (or the QSCustomizer), regardless of the measured size of this view
* @return size in pixels of QS (or QSCustomizer)
*/
@@ -267,6 +274,13 @@
: mQSPanel.getMeasuredHeight();
}
+ /**
+ * @return height with the squishiness fraction applied.
+ */
+ int getSquishedQsHeight() {
+ return mQSPanel.getSquishedHeight();
+ }
+
public void setExpansion(float expansion) {
mQsExpansion = expansion;
if (mQSPanelContainer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index a6fd35a..0b37b5b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -992,11 +992,25 @@
return mContainer.getQqsHeight();
}
+ /**
+ * @return height with the squishiness fraction applied.
+ */
+ public int getSquishedQqsHeight() {
+ return mContainer.getSquishedQqsHeight();
+ }
+
public int getQSHeight() {
return mContainer.getQsHeight();
}
/**
+ * @return height with the squishiness fraction applied.
+ */
+ public int getSquishedQsHeight() {
+ return mContainer.getSquishedQsHeight();
+ }
+
+ /**
* Pass the size of the navbar when it's at the bottom of the device so it can be used as
* padding
* @param padding size of the bottom nav bar in px
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 032891f..d3bed27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -733,6 +733,30 @@
mCanCollapse = canCollapse;
}
+ /**
+ * @return height with the {@link QSPanel#setSquishinessFraction(float)} applied.
+ */
+ public int getSquishedHeight() {
+ if (mFooter != null) {
+ final ViewGroup.LayoutParams footerLayoutParams = mFooter.getLayoutParams();
+ final int footerBottomMargin;
+ if (footerLayoutParams instanceof MarginLayoutParams) {
+ footerBottomMargin = ((MarginLayoutParams) footerLayoutParams).bottomMargin;
+ } else {
+ footerBottomMargin = 0;
+ }
+ // This is the distance between the top of the QSPanel and the last view in the
+ // layout (which is the effective the bottom)
+ return mFooter.getBottom() + footerBottomMargin - getTop();
+ }
+ if (mTileLayout != null) {
+ // Footer absence means that the panel is in the QQS. In this case it's just height
+ // of the tiles + paddings.
+ return mTileLayout.getTilesHeight() + getPaddingBottom() + getPaddingTop();
+ }
+ return getHeight();
+ }
+
@Nullable
@VisibleForTesting
View getMediaPlaceholder() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 5a3f1c0..8fde52c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -123,4 +123,11 @@
lp.setMarginEnd(marginEnd);
view.setLayoutParams(lp);
}
+
+ /**
+ * @return height with the squishiness fraction applied.
+ */
+ public int getSquishedHeight() {
+ return mHeaderQsPanel.getSquishedHeight();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index 9b8dba1..9fb1d46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -37,19 +37,22 @@
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- val icon =
+ iconRes =
+ if (data.isEnabled) {
+ R.drawable.qs_airplane_icon_on
+ } else {
+ R.drawable.qs_airplane_icon_off
+ }
+
+ icon = {
Icon.Loaded(
resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_airplane_icon_on
- } else {
- R.drawable.qs_airplane_icon_off
- },
+ iconRes!!,
theme,
),
contentDescription = null
)
- this.icon = { icon }
+ }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
index 875079c..984228d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
@@ -41,16 +41,25 @@
) : QSTileDataToStateMapper<CustomTileDataModel> {
override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
- val userContext = context.createContextAsUser(UserHandle(data.user.identifier), 0)
+ val userContext =
+ try {
+ context.createContextAsUser(UserHandle(data.user.identifier), 0)
+ } catch (exception: IllegalStateException) {
+ null
+ }
val iconResult =
- getIconProvider(
- userContext = userContext,
- icon = data.tile.icon,
- callingAppUid = data.callingAppUid,
- packageName = data.componentName.packageName,
- defaultIcon = data.defaultTileIcon,
- )
+ if (userContext != null) {
+ getIconProvider(
+ userContext = userContext,
+ icon = data.tile.icon,
+ callingAppUid = data.callingAppUid,
+ packageName = data.componentName.packageName,
+ defaultIcon = data.defaultTileIcon,
+ )
+ } else {
+ IconResult({ null }, true)
+ }
return QSTileState.build(iconResult.iconProvider, data.tile.label) {
var tileState: Int = data.tile.state
@@ -61,7 +70,7 @@
icon = iconResult.iconProvider
activationState =
if (iconResult.failedToLoad) {
- QSTileState.ActivationState.INACTIVE
+ QSTileState.ActivationState.UNAVAILABLE
} else {
QSTileState.ActivationState.valueOf(tileState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
index eec5d3d..204ead3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
@@ -79,6 +79,7 @@
flowOf(
InternetTileModel.Active(
secondaryTitle = secondary,
+ iconId = wifiIcon.icon.res,
icon = Icon.Loaded(context.getDrawable(wifiIcon.icon.res)!!, null),
stateDescription = wifiIcon.contentDescription,
contentDescription = ContentDescription.Loaded("$internetLabel,$secondary"),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index ae2f32a..dfcf216 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -34,7 +34,6 @@
import com.android.systemui.qs.QSContainerImpl
import com.android.systemui.qs.QSImpl
import com.android.systemui.qs.dagger.QSSceneComponent
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel.state
import com.android.systemui.res.R
import com.android.systemui.settings.brightness.MirrorController
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -126,12 +125,18 @@
/** The current height of QQS in the current [qsView], or 0 if there's no view. */
val qqsHeight: Int
+ /** @return height with the squishiness fraction applied. */
+ val squishedQqsHeight: Int
+
/**
* The current height of QS in the current [qsView], or 0 if there's no view. If customizing, it
* will return the height allocated to the customizer.
*/
val qsHeight: Int
+ /** @return height with the squishiness fraction applied. */
+ val squishedQsHeight: Int
+
/** Compatibility for use by LockscreenShadeTransitionController. Matches default from [QS] */
val isQsFullyCollapsed: Boolean
get() = true
@@ -273,9 +278,15 @@
override val qqsHeight: Int
get() = qsImpl.value?.qqsHeight ?: 0
+ override val squishedQqsHeight: Int
+ get() = qsImpl.value?.squishedQqsHeight ?: 0
+
override val qsHeight: Int
get() = qsImpl.value?.qsHeight ?: 0
+ override val squishedQsHeight: Int
+ get() = qsImpl.value?.squishedQsHeight ?: 0
+
// If value is null, there's no QS and therefore it's fully collapsed.
override val isQsFullyCollapsed: Boolean
get() = qsImpl.value?.isFullyCollapsed ?: true
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 15366d5..ecf816b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -333,6 +333,13 @@
}
@Override
+ public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) {
+ verifyCallerAndClearCallingIdentityPostMain("updateContextualEduStats",
+ () -> mHandler.post(() -> OverviewProxyService.this.updateContextualEduStats(
+ isTrackpadGesture, GestureType.valueOf(gestureType))));
+ }
+
+ @Override
public void setHomeRotationEnabled(boolean enabled) {
verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 98a61df..863a899 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -24,6 +24,7 @@
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
+import android.util.Log
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.qualifiers.LongRunning
@@ -71,6 +72,7 @@
override fun provideRecordingServiceStrings(): RecordingServiceStrings = IrsStrings(resources)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.d(getTag(), "handling action: ${intent?.action}")
when (intent?.action) {
ACTION_START -> {
bgExecutor.execute {
@@ -95,7 +97,7 @@
bgExecutor.execute {
mNotificationManager.cancelAsUser(
null,
- mNotificationId,
+ intent.getIntExtra(EXTRA_NOTIFICATION_ID, mNotificationId),
UserHandle(mUserContextTracker.userContext.userId)
)
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 5b50133..e73664d 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
@@ -25,6 +25,7 @@
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.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
@@ -132,6 +133,7 @@
private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
private val dismissCallbackRegistry: DismissCallbackRegistry,
private val statusBarStateController: SysuiStatusBarStateController,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
@@ -228,13 +230,16 @@
},
headsUpInteractor.isHeadsUpOrAnimatingAway,
occlusionInteractor.invisibleDueToOcclusion,
+ alternateBouncerInteractor.isVisible,
) {
visibilityForTransitionState,
isHeadsUpOrAnimatingAway,
invisibleDueToOcclusion,
+ isAlternateBouncerVisible,
->
when {
isHeadsUpOrAnimatingAway -> true to "showing a HUN"
+ isAlternateBouncerVisible -> true to "showing alternate bouncer"
invisibleDueToOcclusion -> false to "invisible due to occlusion"
else -> visibilityForTransitionState
}
@@ -351,9 +356,10 @@
)
}
val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen)
- val isOnBouncer = renderedScenes.contains(Scenes.Bouncer)
+ val isAlternateBouncerVisible = alternateBouncerInteractor.isVisibleState()
+ val isOnPrimaryBouncer = renderedScenes.contains(Scenes.Bouncer)
if (!deviceUnlockStatus.isUnlocked) {
- return@mapNotNull if (isOnLockscreen || isOnBouncer) {
+ return@mapNotNull if (isOnLockscreen || isOnPrimaryBouncer) {
// Already on lockscreen or bouncer, no need to change scenes.
null
} else {
@@ -365,26 +371,44 @@
}
if (
- isOnBouncer &&
+ isOnPrimaryBouncer &&
deviceUnlockStatus.deviceUnlockSource == DeviceUnlockSource.TrustAgent
) {
uiEventLogger.log(BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS)
}
when {
- isOnBouncer ->
- // When the device becomes unlocked in Bouncer, go to previous scene,
- // or Gone.
+ isAlternateBouncerVisible -> {
+ // When the device becomes unlocked when the alternate bouncer is
+ // showing, always hide the alternate bouncer...
+ alternateBouncerInteractor.hide()
+
+ // ... and go to Gone or stay on the current scene
+ if (
+ isOnLockscreen ||
+ !statusBarStateController.leaveOpenOnKeyguardHide()
+ ) {
+ Scenes.Gone to
+ "device was unlocked with alternate bouncer showing" +
+ " and shade didn't need to be left open"
+ } else {
+ null
+ }
+ }
+ isOnPrimaryBouncer ->
+ // When the device becomes unlocked in primary Bouncer,
+ // go to previous scene or Gone.
if (
previousScene.value == Scenes.Lockscreen ||
!statusBarStateController.leaveOpenOnKeyguardHide()
) {
Scenes.Gone to
- "device was unlocked in Bouncer scene and shade" +
+ "device was unlocked with bouncer showing and shade" +
" didn't need to be left open"
} else {
val prevScene = previousScene.value
(prevScene ?: Scenes.Gone) to
- "device was unlocked in Bouncer scene, from sceneKey=$prevScene"
+ "device was unlocked with primary bouncer showing," +
+ " from sceneKey=$prevScene"
}
isOnLockscreen ->
// The lockscreen should be dismissed automatically in 2 scenarios:
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index bccbb11..f6924f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -5,15 +5,18 @@
import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.shade.TouchLogger
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
/** A root view of the main SysUI window that supports scenes. */
+@ExperimentalCoroutinesApi
class SceneWindowRootView(
context: Context,
attrs: AttributeSet?,
@@ -35,6 +38,7 @@
scenes: Set<Scene>,
layoutInsetController: LayoutInsetsController,
sceneDataSourceDelegator: SceneDataSourceDelegator,
+ alternateBouncerDependencies: AlternateBouncerDependencies,
) {
this.viewModel = viewModel
setLayoutInsetsController(layoutInsetController)
@@ -49,6 +53,7 @@
super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE)
},
dataSourceDelegator = sceneDataSourceDelegator,
+ alternateBouncerDependencies = alternateBouncerDependencies,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index d31d6f4..73a8e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -37,6 +37,8 @@
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
+import com.android.systemui.keyguard.ui.composable.AlternateBouncer
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -48,12 +50,14 @@
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+@ExperimentalCoroutinesApi
object SceneWindowRootViewBinder {
/** Binds between the view and view-model pertaining to a specific scene container. */
@@ -66,6 +70,7 @@
scenes: Set<Scene>,
onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
dataSourceDelegator: SceneDataSourceDelegator,
+ alternateBouncerDependencies: AlternateBouncerDependencies,
) {
val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
val sortedSceneByKey: Map<SceneKey, Scene> = buildMap {
@@ -120,6 +125,14 @@
sharedNotificationContainer
)
view.addView(sharedNotificationContainer)
+
+ // TODO (b/358354906): use an overlay for the alternate bouncer
+ view.addView(
+ createAlternateBouncerView(
+ context = view.context,
+ alternateBouncerDependencies = alternateBouncerDependencies,
+ )
+ )
}
launch {
@@ -164,6 +177,19 @@
}
}
+ private fun createAlternateBouncerView(
+ context: Context,
+ alternateBouncerDependencies: AlternateBouncerDependencies,
+ ): ComposeView {
+ return ComposeView(context).apply {
+ setContent {
+ AlternateBouncer(
+ alternateBouncerDependencies = alternateBouncerDependencies,
+ )
+ }
+ }
+ }
+
// TODO(b/298525212): remove once Compose exposes window inset bounds.
private fun displayCutoutFromWindowInsets(
scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 108564c..700253ba 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -80,6 +80,7 @@
"com.android.systemui.screenrecord.STOP_FROM_NOTIF";
protected static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+ protected static final String EXTRA_NOTIFICATION_ID = "notification_id";
private final RecordingController mController;
protected final KeyguardDismissUtil mKeyguardDismissUtil;
@@ -542,7 +543,8 @@
private Intent getShareIntent(Context context, Uri path) {
return new Intent(context, this.getClass()).setAction(ACTION_SHARE)
- .putExtra(EXTRA_PATH, path);
+ .putExtra(EXTRA_PATH, path)
+ .putExtra(EXTRA_NOTIFICATION_ID, mNotificationId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index b54bf6c..f3357ee 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -146,6 +146,10 @@
hostUserHandle
)
intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid)
+ intent.putExtra(
+ MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE,
+ MediaProjectionAppSelectorActivity.ScreenShareType.ScreenRecord.name,
+ )
activityStarter.startActivity(intent, /* dismissShade= */ true)
}
dialog.dismiss()
@@ -270,15 +274,18 @@
return listOf(
ScreenShareOption(
SINGLE_APP,
- R.string.screen_share_permission_dialog_option_single_app,
+ R.string.screenrecord_permission_dialog_option_text_single_app,
R.string.screenrecord_permission_dialog_warning_single_app,
- startButtonText = R.string.screenrecord_permission_dialog_continue,
+ startButtonText =
+ R.string
+ .media_projection_entry_generic_permission_dialog_continue_single_app,
),
ScreenShareOption(
ENTIRE_SCREEN,
- R.string.screen_share_permission_dialog_option_entire_screen,
+ R.string.screenrecord_permission_dialog_option_text_entire_screen,
R.string.screenrecord_permission_dialog_warning_entire_screen,
- startButtonText = R.string.screenrecord_permission_dialog_continue,
+ startButtonText =
+ R.string.screenrecord_permission_dialog_continue_entire_screen,
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
index 6ff1535..74513f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
@@ -91,7 +91,7 @@
try {
boolean success = mActivityTaskManager.requestAssistDataForTask(
new AssistDataReceiver(callback, this), taskId, mPackageName,
- mAttributionTag);
+ mAttributionTag, false /* fetchStructure */);
if (!success) {
callback.onAssistContentAvailable(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
index 644e12c..c4fe7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
@@ -31,6 +31,7 @@
import android.view.WindowInsets
import android.view.WindowManager
import android.window.WindowContext
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.policy.PhoneWindow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -41,6 +42,7 @@
@AssistedInject
constructor(
private val windowManager: WindowManager,
+ private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
private val context: Context,
@Assisted private val display: Display,
) {
@@ -95,7 +97,7 @@
Log.d(TAG, "attachWindow")
}
attachRequested = true
- windowManager.addView(decorView, params)
+ viewCaptureAwareWindowManager.addView(decorView, params)
decorView.requestApplyInsets()
decorView.requireViewById<ViewGroup>(R.id.content).apply {
@@ -133,7 +135,7 @@
if (LogConfig.DEBUG_WINDOW) {
Log.d(TAG, "Removing screenshot window")
}
- windowManager.removeViewImmediate(decorView)
+ viewCaptureAwareWindowManager.removeViewImmediate(decorView)
detachRequested = false
}
if (attachRequested && !detachRequested) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 15bbef0..22f62fc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -55,10 +55,12 @@
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.collectFlow
@@ -88,6 +90,8 @@
private val communalContent: CommunalContent,
@Communal private val dataSourceDelegator: SceneDataSourceDelegator,
private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
+ private val keyguardMediaController: KeyguardMediaController,
+ private val lockscreenSmartspaceController: LockscreenSmartspaceController
) : LifecycleOwner {
private class CommunalWrapper(context: Context) : FrameLayout(context) {
@@ -445,7 +449,12 @@
// the touch.
if (
!hubShowing &&
- !notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y)
+ (!notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y) ||
+ keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt()) ||
+ lockscreenSmartspaceController.isWithinSmartspaceBounds(
+ ev.x.toInt(),
+ ev.y.toInt()
+ ))
) {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 104d4b5..65a59f5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1339,6 +1339,10 @@
"NotificationPanelViewController.updateResources");
if (splitShadeChanged) {
+ if (isPanelVisibleBecauseOfHeadsUp()) {
+ // workaround for b/324642496, because HUNs set state to OPENING
+ onPanelStateChanged(STATE_CLOSED);
+ }
onSplitShadeEnabledChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 0a092a0..16aef65 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -2252,8 +2252,11 @@
// panel, mQs will not need to be null cause it will be tied to the same lifecycle.
if (fragment == mQs) {
// Clear it to remove bindings to mQs from the provider.
- mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(null);
- mNotificationStackScrollLayoutController.setQsHeader(null);
+ if (QSComposeFragment.isEnabled()) {
+ mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(null);
+ } else {
+ mNotificationStackScrollLayoutController.setQsHeader(null);
+ }
mQs = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index bc23778..21bbaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -31,6 +31,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -83,6 +84,7 @@
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
layoutInsetController: NotificationInsetsController,
sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
+ alternateBouncerDependencies: Provider<AlternateBouncerDependencies>,
): WindowRootView {
return if (SceneContainerFlag.isEnabled) {
checkNoSceneDuplicates(scenesProvider.get())
@@ -96,6 +98,7 @@
scenes = scenesProvider.get(),
layoutInsetController = layoutInsetController,
sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
+ alternateBouncerDependencies = alternateBouncerDependencies.get(),
)
sceneWindowRootView
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/BaseShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/BaseShadeSceneViewModel.kt
deleted file mode 100644
index 068d6a7..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/BaseShadeSceneViewModel.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.shade.ui.viewmodel
-
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.lifecycle.SysUiViewModel
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
-
-/** Base class for classes that model UI state of the content of shade scenes. */
-abstract class BaseShadeSceneViewModel(
- private val deviceEntryInteractor: DeviceEntryInteractor,
- private val sceneInteractor: SceneInteractor,
-) : SysUiViewModel() {
-
- private val _isEmptySpaceClickable =
- MutableStateFlow(!deviceEntryInteractor.isDeviceEntered.value)
- /** Whether clicking on the empty area of the shade does something */
- val isEmptySpaceClickable: StateFlow<Boolean> = _isEmptySpaceClickable.asStateFlow()
-
- override suspend fun onActivated() {
- deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered ->
- _isEmptySpaceClickable.value = !isDeviceEntered
- }
- }
-
- /** Notifies that the empty space in the shade has been clicked. */
- fun onEmptySpaceClicked() {
- if (!isEmptySpaceClickable.value) {
- return
- }
-
- sceneInteractor.changeScene(Scenes.Lockscreen, "Shade empty space clicked.")
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
index 3cdff96..a4d3416 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
@@ -20,11 +20,13 @@
import androidx.lifecycle.LifecycleOwner
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
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
@@ -34,7 +36,10 @@
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
/**
* Models UI state used to render the content of the shade scene.
@@ -53,20 +58,27 @@
private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
private val footerActionsController: FooterActionsController,
private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
- deviceEntryInteractor: DeviceEntryInteractor,
- sceneInteractor: SceneInteractor,
-) :
- BaseShadeSceneViewModel(
- deviceEntryInteractor,
- sceneInteractor,
- ) {
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val sceneInteractor: SceneInteractor,
+) : SysUiViewModel() {
val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
+ private val _isEmptySpaceClickable =
+ MutableStateFlow(!deviceEntryInteractor.isDeviceEntered.value)
+ /** Whether clicking on the empty area of the shade does something */
+ val isEmptySpaceClickable: StateFlow<Boolean> = _isEmptySpaceClickable.asStateFlow()
+
val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasActiveMediaOrRecommendation
private val footerActionsControllerInitialized = AtomicBoolean(false)
+ override suspend fun onActivated() {
+ deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered ->
+ _isEmptySpaceClickable.value = !isDeviceEntered
+ }
+ }
+
/**
* Amount of X-axis translation to apply to various elements as the unfolded foldable is folded
* slightly, in pixels.
@@ -82,6 +94,15 @@
return footerActionsViewModelFactory.create(lifecycleOwner)
}
+ /** Notifies that the empty space in the shade has been clicked. */
+ fun onEmptySpaceClicked() {
+ if (!isEmptySpaceClickable.value) {
+ return
+ }
+
+ sceneInteractor.changeScene(Scenes.Lockscreen, "Shade empty space clicked.")
+ }
+
@AssistedFactory
interface Factory {
fun create(): ShadeSceneContentViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 50be6dc..a1477b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -37,6 +37,7 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
import android.os.Binder;
@@ -257,10 +258,10 @@
*
* @param displayId The id of the display to notify.
* @param vis IME visibility.
- * @param backDisposition Disposition mode of back button. It should be one of below flags:
+ * @param backDisposition Disposition mode of back button.
* @param showImeSwitcher {@code true} to show IME switch button.
*/
- default void setImeWindowStatus(int displayId, int vis,
+ default void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
default void showRecentApps(boolean triggeredFromAltTab) { }
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
@@ -743,8 +744,8 @@
}
@Override
- public void setImeWindowStatus(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
SomeArgs args = SomeArgs.obtain();
@@ -1205,8 +1206,8 @@
}
}
- private void handleShowImeButton(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ private void handleShowImeButton(int displayId, @ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
if (displayId == INVALID_DISPLAY) return;
boolean isConcurrentMultiUserModeEnabled = UserManager.isVisibleBackgroundUsersEnabled()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 69ebb76..c4f539a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -5,3 +5,12 @@
caitlinshk@google.com
evanlaird@google.com
pixel@google.com
+
+per-file *Biometrics* = set noparent
+per-file *Biometrics* = file:../keyguard/OWNERS
+per-file *Doze* = set noparent
+per-file *Doze* = file:../keyguard/OWNERS
+per-file *Keyboard* = set noparent
+per-file *Keyboard* = file:../keyguard/OWNERS
+per-file *Keyguard* = set noparent
+per-file *Keyguard* = file:../keyguard/OWNERS
\ No newline at end of file
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 ef4dffad..97add30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -26,6 +26,7 @@
import android.content.Context
import android.content.Intent
import android.database.ContentObserver
+import android.graphics.Rect
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
@@ -570,6 +571,20 @@
plugin?.unregisterListener(listener)
}
+ fun isWithinSmartspaceBounds(x: Int, y: Int): Boolean {
+ smartspaceViews.forEach {
+ val bounds = Rect()
+ with(it as View) {
+ this.getBoundsOnScreen(bounds)
+ if (bounds.contains(x, y)) {
+ return true
+ }
+ }
+ }
+
+ return false
+ }
+
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
if (isDateWeatherDecoupled && t.featureType == SmartspaceTarget.FEATURE_WEATHER) {
return false
@@ -587,7 +602,7 @@
// Only the primary user can have an associated managed profile, so only show
// content for the managed profile if the primary user is active
userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
- (!t.isSensitive || showSensitiveContentForManagedUser)
+ (!t.isSensitive || showSensitiveContentForManagedUser)
}
else -> {
false
@@ -705,4 +720,3 @@
}
}
}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/OWNERS
new file mode 100644
index 0000000..4c349c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+file:../../keyguard/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 1027bc9..9b382e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -89,6 +89,7 @@
.filter { it.callType == CallType.Ongoing }
.minByOrNull { it.whenTime }
}
+ .distinctUntilChanged()
.flowOn(backgroundDispatcher)
/** Are any notifications being actively presented in the notification stack? */
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 9d13a17..cb3e26b 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
@@ -1804,6 +1804,20 @@
NotificationEntry childEntry,
NotificationEntry containerEntry
);
+
+ /**
+ * Called when resetting the alpha value for content views
+ */
+ void logResetAllContentAlphas(
+ NotificationEntry entry
+ );
+
+ /**
+ * Called when resetting the alpha value for content views is skipped
+ */
+ void logSkipResetAllContentAlphas(
+ NotificationEntry entry
+ );
}
/**
@@ -3001,6 +3015,8 @@
mChildrenContainer.animate().cancel();
}
resetAllContentAlphas();
+ } else {
+ mLogger.logSkipResetAllContentAlphas(getEntry());
}
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
@@ -3186,6 +3202,7 @@
@Override
protected void resetAllContentAlphas() {
+ mLogger.logResetAllContentAlphas(getEntry());
mPrivateLayout.setAlpha(1f);
mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
mPublicLayout.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 4c76e328..c31a2cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -195,6 +195,20 @@
) {
mLogBufferLogger.logRemoveTransientRow(childEntry, containerEntry);
}
+
+ @Override
+ public void logResetAllContentAlphas(
+ NotificationEntry entry
+ ) {
+ mLogBufferLogger.logResetAllContentAlphas(entry);
+ }
+
+ @Override
+ public void logSkipResetAllContentAlphas(
+ NotificationEntry entry
+ ) {
+ mLogBufferLogger.logSkipResetAllContentAlphas(entry);
+ }
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index 4f5a04f..b1e9032 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -128,6 +128,24 @@
{ "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }
)
}
+
+ fun logResetAllContentAlphas(entry: NotificationEntry) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = entry.logKey },
+ { "resetAllContentAlphas: $str1" }
+ )
+ }
+
+ fun logSkipResetAllContentAlphas(entry: NotificationEntry) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = entry.logKey },
+ { "Skip resetAllContentAlphas: $str1" }
+ )
+ }
}
private const val TAG = "NotifRow"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
index fe86375..bf5b3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.row
import android.app.Notification
-import android.app.Notification.RichOngoingStyle
import android.app.PendingIntent
import android.content.Context
import android.util.Log
@@ -69,14 +68,12 @@
builder: Notification.Builder,
systemUIContext: Context,
packageContext: Context
- ): RichOngoingContentModel? {
- if (builder.style !is RichOngoingStyle) return null
-
+ ): RichOngoingContentModel? =
try {
val sbn = entry.sbn
val notification = sbn.notification
val icon = IconModel(notification.smallIcon)
- return if (sbn.packageName == "com.google.android.deskclock") {
+ if (sbn.packageName == "com.google.android.deskclock") {
when (notification.channelId) {
"Timers v2" -> {
parseTimerNotification(notification, icon)
@@ -93,9 +90,8 @@
} else null
} catch (e: Exception) {
Log.e("RONs", "Error parsing RON", e)
- return null
+ null
}
- }
/**
* FOR PROTOTYPING ONLY: create a RON TimerContentModel using the time information available
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index fd08e89..a30b877 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import android.util.Log
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
@@ -33,7 +33,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
@@ -46,7 +45,7 @@
dumpManager: DumpManager,
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
private val view: NotificationScrollView,
- private val viewModel: NotificationScrollViewModel,
+ private val viewModelFactory: NotificationScrollViewModel.Factory,
private val configuration: ConfigurationState,
) : FlowDumperImpl(dumpManager) {
@@ -61,38 +60,42 @@
}
fun bindWhileAttached(): DisposableHandle {
- return view.asView().repeatWhenAttached(mainImmediateDispatcher) {
- repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
- }
+ return view.asView().repeatWhenAttached(mainImmediateDispatcher) { bind() }
}
- suspend fun bind() = coroutineScope {
- launchAndDispose {
- updateViewPosition()
- view.asView().onLayoutChanged { updateViewPosition() }
- }
+ suspend fun bind(): Nothing =
+ view.asView().viewModel(
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = viewModelFactory::create,
+ ) { viewModel ->
+ launchAndDispose {
+ updateViewPosition()
+ view.asView().onLayoutChanged { updateViewPosition() }
+ }
- launch {
- viewModel
- .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
- .collect { view.setScrimClippingShape(it) }
- }
+ launch {
+ viewModel
+ .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
+ .collect { view.setScrimClippingShape(it) }
+ }
- launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
- launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
- launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
- launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
- launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
+ launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
+ launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+ launch {
+ viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) }
+ }
+ launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
+ launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
- launchAndDispose {
- view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
- view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
- DisposableHandle {
- view.setSyntheticScrollConsumer(null)
- view.setCurrentGestureOverscrollConsumer(null)
+ launchAndDispose {
+ view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
+ view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
+ DisposableHandle {
+ view.setSyntheticScrollConsumer(null)
+ view.setCurrentGestureOverscrollConsumer(null)
+ }
}
}
- }
/** flow of the scrim clipping radius */
private val scrimRadius: Flow<Int>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 2ba79a8..bfb624a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -19,9 +19,9 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.SceneFamilies
@@ -33,9 +33,11 @@
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
-import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.ActivatableFlowDumper
+import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
import dagger.Lazy
-import javax.inject.Inject
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -43,9 +45,8 @@
import kotlinx.coroutines.flow.map
/** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
-@SysUISingleton
class NotificationScrollViewModel
-@Inject
+@AssistedInject
constructor(
dumpManager: DumpManager,
stackAppearanceInteractor: NotificationStackAppearanceInteractor,
@@ -54,7 +55,14 @@
// TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
// while the flag is off, creating this object too early results in a crash
keyguardInteractor: Lazy<KeyguardInteractor>,
-) : FlowDumperImpl(dumpManager) {
+) :
+ ActivatableFlowDumper by ActivatableFlowDumperImpl(dumpManager, "NotificationScrollViewModel"),
+ SysUiViewModel() {
+
+ override suspend fun onActivated() {
+ activateFlowDumper()
+ }
+
/**
* The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
* from Gone to Shade scenes, and remain at 1 when in Lockscreen or Shade scenes and while
@@ -186,4 +194,9 @@
keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
}
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): NotificationScrollViewModel
+ }
}
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 d179888..53fab62 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
@@ -16,34 +16,32 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
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 dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
/**
* ViewModel used by the Notification placeholders inside the scene container to update the
* [NotificationStackAppearanceInteractor], and by extension control the NSSL.
*/
-@SysUISingleton
class NotificationsPlaceholderViewModel
-@Inject
+@AssistedInject
constructor(
- dumpManager: DumpManager,
private val interactor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
private val headsUpNotificationInteractor: HeadsUpNotificationInteractor,
featureFlags: FeatureFlagsClassic,
-) : FlowDumperImpl(dumpManager) {
+) : SysUiViewModel() {
+
/** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
@@ -70,35 +68,37 @@
headsUpNotificationInteractor.isHeadsUpOrAnimatingAway
/** Corner rounding of the stack */
- val shadeScrimRounding: Flow<ShadeScrimRounding> =
- interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
+ // TODO(b/359244921): add .dumpWhileCollecting("shadeScrimRounding")
+ val shadeScrimRounding: Flow<ShadeScrimRounding> = interactor.shadeScrimRounding
/**
* The amount [0-1] that the shade or quick settings has been opened. At 0, the shade is closed;
* at 1, either the shade or quick settings is open.
*/
- val expandFraction: Flow<Float> = shadeInteractor.anyExpansion.dumpValue("expandFraction")
+ // TODO(b/359244921): add .dumpValue("expandFraction")
+ val expandFraction: Flow<Float> = shadeInteractor.anyExpansion
/**
* The amount [0-1] that quick settings has been opened. At 0, the shade may be open or closed;
* at 1, the quick settings are open.
*/
- val shadeToQsFraction: Flow<Float> = shadeInteractor.qsExpansion.dumpValue("shadeToQsFraction")
+ // TODO(b/359244921): add .dumpValue("shadeToQsFraction")
+ val shadeToQsFraction: Flow<Float> = shadeInteractor.qsExpansion
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
* should only happen when a notification expansion hits the bottom of the screen, so it is
* necessary to scroll up to keep expanding the notification.
*/
- val syntheticScroll: Flow<Float> =
- interactor.syntheticScroll.dumpWhileCollecting("syntheticScroll")
+ // TODO(b/359244921): add .dumpWhileCollecting("syntheticScroll")
+ val syntheticScroll: Flow<Float> = interactor.syntheticScroll
/**
* Whether the current touch gesture is overscroll. If true, it means the NSSL has already
* consumed part of the gesture.
*/
- val isCurrentGestureOverscroll: Flow<Boolean> =
- interactor.isCurrentGestureOverscroll.dumpWhileCollecting("isCurrentGestureOverScroll")
+ // TODO(b/359244921): add .dumpWhileCollecting("isCurrentGestureOverScroll")
+ val isCurrentGestureOverscroll: Flow<Boolean> = interactor.isCurrentGestureOverscroll
/** Sets whether the notification stack is scrolled to the top. */
fun setScrolledToTop(scrolledToTop: Boolean) {
@@ -114,6 +114,11 @@
fun snoozeHun() {
headsUpNotificationInteractor.snooze()
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): NotificationsPlaceholderViewModel
+ }
}
// Expansion fraction thresholds (between 0-1f) at which the corresponding value should be
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 b6d58d6..c4fbc37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -30,6 +30,7 @@
import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
import static com.android.systemui.Flags.lightRevealMigration;
import static com.android.systemui.Flags.newAodTransition;
+import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -2352,8 +2353,14 @@
} else if (mState == StatusBarState.KEYGUARD
&& !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
&& mStatusBarKeyguardViewManager.isSecure()) {
- Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer");
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+ if (!relockWithPowerButtonImmediately()) {
+ Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer");
+ if (SceneContainerFlag.isEnabled()) {
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ } else {
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+ }
+ }
}
}
}
@@ -2985,7 +2992,7 @@
@Override
public void onFalse() {
// Hides quick settings, bouncer, and quick-quick settings.
- mStatusBarKeyguardViewManager.reset(true);
+ mStatusBarKeyguardViewManager.reset(true, /* isFalsingReset= */true);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index f13a593..ac10155 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -424,14 +424,12 @@
@Override
public void setDozeScreenBrightness(int brightness) {
- mDozeLog.traceDozeScreenBrightness(brightness);
mNotificationShadeWindowController.setDozeScreenBrightness(brightness);
}
@Override
public void setDozeScreenBrightnessFloat(float brightness) {
- mDozeLog.traceDozeScreenBrightnessFloat(brightness);
mNotificationShadeWindowController.setDozeScreenBrightnessFloat(brightness);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 8115c36..f178708 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -36,6 +36,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dependency;
+import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
@@ -217,8 +218,12 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- mTouchEventHandler.onInterceptTouchEvent(event);
- return super.onInterceptTouchEvent(event);
+ if (Flags.statusBarSwipeOverChip()) {
+ return mTouchEventHandler.onInterceptTouchEvent(event);
+ } else {
+ mTouchEventHandler.onInterceptTouchEvent(event);
+ return super.onInterceptTouchEvent(event);
+ }
}
public void updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 5206e46..a818c05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -23,9 +23,10 @@
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
+import com.android.systemui.Flags
import com.android.systemui.Gefingerpoken
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.ui.view.WindowRootView
@@ -83,22 +84,25 @@
statusContainer.setOnHoverListener(
statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)
)
- statusContainer.setOnTouchListener(object : View.OnTouchListener {
- override fun onTouch(v: View, event: MotionEvent): Boolean {
- // We want to handle only mouse events here to avoid stealing finger touches from
- // status bar which expands shade when swiped down on. We're using onTouchListener
- // instead of onClickListener as the later will lead to isClickable being set to
- // true and hence ALL touches always being intercepted. See [View.OnTouchEvent]
- if (event.source == InputDevice.SOURCE_MOUSE) {
- if (event.action == MotionEvent.ACTION_UP) {
- v.performClick()
- shadeController.animateExpandShade()
+ statusContainer.setOnTouchListener(
+ object : View.OnTouchListener {
+ override fun onTouch(v: View, event: MotionEvent): Boolean {
+ // We want to handle only mouse events here to avoid stealing finger touches
+ // from status bar which expands shade when swiped down on. See b/326097469.
+ // We're using onTouchListener instead of onClickListener as the later will lead
+ // to isClickable being set to true and hence ALL touches always being
+ // intercepted. See [View.OnTouchEvent]
+ if (event.source == InputDevice.SOURCE_MOUSE) {
+ if (event.action == MotionEvent.ACTION_UP) {
+ v.performClick()
+ shadeController.animateExpandShade()
+ }
+ return true
}
- return true
+ return false
}
- return false
}
- })
+ )
progressProvider?.setReadyToHandleTransition(true)
configurationController.addCallback(configurationListener)
@@ -180,8 +184,12 @@
inner class PhoneStatusBarViewTouchHandler : Gefingerpoken {
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
- onTouch(event)
- return false
+ return if (Flags.statusBarSwipeOverChip()) {
+ shadeViewController.handleExternalInterceptTouch(event)
+ } else {
+ onTouch(event)
+ false
+ }
}
override fun onTouchEvent(event: MotionEvent): Boolean {
@@ -280,7 +288,7 @@
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
val statusBarMoveFromCenterAnimationController =
- if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) {
+ if (featureFlags.isEnabled(ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) {
unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController()
} else {
null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 2d775b7..5486abb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -708,7 +708,7 @@
* Shows the notification keyguard or the bouncer depending on
* {@link #needsFullscreenBouncer()}.
*/
- protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
+ protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
boolean isDozing = mDozing;
if (Flags.simPinRaceConditionOnRestart()) {
KeyguardState toState = mKeyguardTransitionInteractor.getTransitionState().getValue()
@@ -734,8 +734,11 @@
mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
}
}
- } else {
- Log.e(TAG, "Attempted to show the sim bouncer when it is already showing.");
+ } else if (!isFalsingReset) {
+ // Falsing resets can cause this to flicker, so don't reset in this case
+ Log.i(TAG, "Sim bouncer is already showing, issuing a refresh");
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+
}
} else {
mCentralSurfaces.showKeyguard();
@@ -957,6 +960,10 @@
@Override
public void reset(boolean hideBouncerWhenShowing) {
+ reset(hideBouncerWhenShowing, /* isFalsingReset= */false);
+ }
+
+ public void reset(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
if (mKeyguardStateController.isShowing() && !bouncerIsAnimatingAway()) {
final boolean isOccluded = mKeyguardStateController.isOccluded();
// Hide quick settings.
@@ -965,10 +972,14 @@
if (isOccluded && !mDozing) {
mCentralSurfaces.hideKeyguard();
if (hideBouncerWhenShowing || needsFullscreenBouncer()) {
- hideBouncer(false /* destroyView */);
+ // We're removing "reset" in the refactor - bouncer will be hidden by the root
+ // cause of the "reset" calls.
+ if (!KeyguardWmStateRefactor.isEnabled()) {
+ hideBouncer(false /* destroyView */);
+ }
}
} else {
- showBouncerOrKeyguard(hideBouncerWhenShowing);
+ showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset);
}
if (hideBouncerWhenShowing) {
hideAlternateBouncer(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 4368239..bd6a1c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -227,6 +227,15 @@
callNotificationInfo
// This shouldn't happen, but protect against it in case
?: return OngoingCallModel.NoCall
+ logger.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ bool1 = Flags.statusBarCallChipNotificationIcon()
+ bool2 = currentInfo.notificationIconView != null
+ },
+ { "Creating OngoingCallModel.InCall. notifIconFlag=$bool1 hasIcon=$bool2" }
+ )
val icon =
if (Flags.statusBarCallChipNotificationIcon()) {
currentInfo.notificationIconView
@@ -257,6 +266,7 @@
private fun updateInfoFromNotifModel(notifModel: ActiveNotificationModel?) {
if (notifModel == null) {
+ logger.log(TAG, LogLevel.DEBUG, {}, { "NotifInteractorCallModel: null" })
removeChip()
} else if (notifModel.callType != CallType.Ongoing) {
logger.log(
@@ -267,6 +277,18 @@
)
removeChip()
} else {
+ logger.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = notifModel.key
+ long1 = notifModel.whenTime
+ str1 = notifModel.callType.name
+ bool1 = notifModel.statusBarChipIconView != null
+ },
+ { "NotifInteractorCallModel: key=$str1 when=$long1 callType=$str2 hasIcon=$bool1" }
+ )
+
val newOngoingCallInfo =
CallNotificationInfo(
notifModel.key,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 2e54972..9cbfc44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -198,7 +198,8 @@
fun logServiceProvidersUpdatedBroadcast(intent: Intent) {
val showSpn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)
- val spn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
+ val spn = intent.getStringExtra(TelephonyManager.EXTRA_SPN)
+ val dataSpn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
val showPlmn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)
val plmn = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
@@ -208,12 +209,13 @@
{
bool1 = showSpn
str1 = spn
+ str2 = dataSpn
bool2 = showPlmn
- str2 = plmn
+ str3 = plmn
},
{
"Intent: ACTION_SERVICE_PROVIDERS_UPDATED." +
- " showSpn=$bool1 spn=$str1 showPlmn=$bool2 plmn=$str2"
+ " showSpn=$bool1 spn=$str1 dataSpn=$str2 showPlmn=$bool2 plmn=$str3"
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index 99ed2d9..85bbe7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -21,6 +21,8 @@
import android.telephony.TelephonyManager.EXTRA_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
+import android.telephony.TelephonyManager.EXTRA_SPN
+import com.android.systemui.Flags.statusBarSwitchToSpnFromDataSpn
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
@@ -96,7 +98,13 @@
fun Intent.toNetworkNameModel(separator: String): NetworkNameModel? {
val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false)
- val spn = getStringExtra(EXTRA_DATA_SPN)
+ val spn =
+ if (statusBarSwitchToSpnFromDataSpn()) {
+ getStringExtra(EXTRA_SPN)
+ } else {
+ getStringExtra(EXTRA_DATA_SPN)
+ }
+
val showPlmn = getBooleanExtra(EXTRA_SHOW_PLMN, false)
val plmn = getStringExtra(EXTRA_PLMN)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index 21f1a3d..c30a6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -46,6 +46,7 @@
import android.view.WindowInsets;
import android.view.WindowManager;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DelegateTransitionAnimatorController;
@@ -71,7 +72,7 @@
private static final boolean DEBUG = false;
private final Context mContext;
- private final WindowManager mWindowManager;
+ private final ViewCaptureAwareWindowManager mWindowManager;
private final IWindowManager mIWindowManager;
private final StatusBarContentInsetsProvider mContentInsetsProvider;
private int mBarHeight = -1;
@@ -91,14 +92,14 @@
public StatusBarWindowController(
Context context,
@StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
- WindowManager windowManager,
+ ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
IWindowManager iWindowManager,
StatusBarContentInsetsProvider contentInsetsProvider,
FragmentService fragmentService,
@Main Resources resources,
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
mContext = context;
- mWindowManager = windowManager;
+ mWindowManager = viewCaptureAwareWindowManager;
mIWindowManager = iWindowManager;
mContentInsetsProvider = contentInsetsProvider;
mStatusBarWindowView = statusBarWindowView;
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 8f048963..3c53d2d 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -32,6 +32,7 @@
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import androidx.annotation.CallSuper
import androidx.annotation.VisibleForTesting
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Main
@@ -70,7 +71,7 @@
abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : TemporaryViewLogger<T>>(
internal val context: Context,
internal val logger: U,
- internal val windowManager: WindowManager,
+ internal val windowManager: ViewCaptureAwareWindowManager,
@Main private val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index b6f5433..9b9cba9 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -29,7 +29,6 @@
import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import android.view.ViewGroup
-import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.ImageView
@@ -38,6 +37,7 @@
import androidx.annotation.IdRes
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
import com.android.systemui.classifier.FalsingCollector
@@ -81,7 +81,7 @@
constructor(
context: Context,
logger: ChipbarLogger,
- windowManager: WindowManager,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -100,7 +100,7 @@
TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
logger,
- windowManager,
+ viewCaptureAwareWindowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt
deleted file mode 100644
index 8ba8db4..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt
+++ /dev/null
@@ -1,33 +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.touchpad.tutorial
-
-import android.app.Activity
-import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-
-@Module
-interface TouchpadKeyboardTutorialModule {
-
- @Binds
- @IntoMap
- @ClassKey(TouchpadTutorialActivity::class)
- fun activity(impl: TouchpadTutorialActivity): Activity
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
new file mode 100644
index 0000000..238e8a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
@@ -0,0 +1,73 @@
+/*
+ * 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
+
+import android.app.Activity
+import androidx.compose.runtime.Composable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
+import com.android.systemui.model.SysUiState
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen
+import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen
+import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineScope
+
+@Module
+interface TouchpadTutorialModule {
+
+ @Binds
+ @IntoMap
+ @ClassKey(TouchpadTutorialActivity::class)
+ fun activity(impl: TouchpadTutorialActivity): Activity
+
+ companion object {
+ @Provides
+ fun touchpadScreensProvider(): TouchpadTutorialScreensProvider {
+ return ScreensProvider
+ }
+
+ @SysUISingleton
+ @Provides
+ fun touchpadGesturesInteractor(
+ sysUiState: SysUiState,
+ displayTracker: DisplayTracker,
+ @Background backgroundScope: CoroutineScope
+ ): TouchpadGesturesInteractor {
+ return TouchpadGesturesInteractor(sysUiState, displayTracker, backgroundScope)
+ }
+ }
+}
+
+private object ScreensProvider : TouchpadTutorialScreensProvider {
+ @Composable
+ override fun BackGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
+ BackGestureTutorialScreen(onDoneButtonClicked, onBack)
+ }
+
+ @Composable
+ override fun HomeGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
+ HomeGestureTutorialScreen(onDoneButtonClicked, onBack)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
index b6c2ae7..df95232 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
@@ -16,22 +16,16 @@
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.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.launch
-@SysUISingleton
-class TouchpadGesturesInteractor
-@Inject
-constructor(
+class TouchpadGesturesInteractor(
private val sysUiState: SysUiState,
private val displayTracker: DisplayTracker,
- @Background private val backgroundScope: CoroutineScope
+ private val backgroundScope: CoroutineScope
) {
fun disableGestures() {
setGesturesState(disabled = true)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
index 5980e1d..1c8041f 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
@@ -21,6 +21,8 @@
import androidx.compose.runtime.remember
import com.airbnb.lottie.compose.rememberLottieDynamicProperties
import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
+import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty
import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureMonitor
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
index 9ac2cba..57d7c84 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
@@ -16,57 +16,21 @@
package com.android.systemui.touchpad.tutorial.ui.composable
-import android.graphics.ColorFilter
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
import androidx.activity.compose.BackHandler
-import androidx.annotation.RawRes
-import androidx.annotation.StringRes
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.snap
-import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.togetherWith
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
-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.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import com.airbnb.lottie.LottieProperty
-import com.airbnb.lottie.compose.LottieAnimation
-import com.airbnb.lottie.compose.LottieCompositionSpec
-import com.airbnb.lottie.compose.LottieConstants
-import com.airbnb.lottie.compose.LottieDynamicProperties
-import com.airbnb.lottie.compose.LottieDynamicProperty
-import com.airbnb.lottie.compose.animateLottieCompositionAsState
-import com.airbnb.lottie.compose.rememberLottieComposition
-import com.airbnb.lottie.compose.rememberLottieDynamicProperty
+import com.android.systemui.inputdevice.tutorial.ui.composable.ActionTutorialContent
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
@@ -81,6 +45,14 @@
): TouchpadGestureMonitor
}
+fun GestureState.toTutorialActionState(): TutorialActionState {
+ return when (this) {
+ NOT_STARTED -> TutorialActionState.NOT_STARTED
+ IN_PROGRESS -> TutorialActionState.IN_PROGRESS
+ FINISHED -> TutorialActionState.FINISHED
+ }
+}
+
@Composable
fun GestureTutorialScreen(
screenConfig: TutorialScreenConfig,
@@ -104,7 +76,11 @@
)
}
TouchpadGesturesHandlingBox(gestureHandler, gestureState) {
- GestureTutorialContent(gestureState, onDoneButtonClicked, screenConfig)
+ ActionTutorialContent(
+ gestureState.toTutorialActionState(),
+ onDoneButtonClicked,
+ screenConfig
+ )
}
}
@@ -135,169 +111,3 @@
content()
}
}
-
-@Composable
-private fun GestureTutorialContent(
- gestureState: GestureState,
- onDoneButtonClicked: () -> Unit,
- config: TutorialScreenConfig
-) {
- val animatedColor by
- animateColorAsState(
- targetValue =
- if (gestureState == FINISHED) config.colors.successBackground
- else config.colors.background,
- animationSpec = tween(durationMillis = 150, easing = LinearEasing),
- label = "backgroundColor"
- )
- Column(
- verticalArrangement = Arrangement.Center,
- modifier =
- Modifier.fillMaxSize()
- .drawBehind { drawRect(animatedColor) }
- .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
- ) {
- Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
- TutorialDescription(
- titleTextId =
- if (gestureState == FINISHED) config.strings.titleSuccessResId
- else config.strings.titleResId,
- titleColor = config.colors.title,
- bodyTextId =
- if (gestureState == FINISHED) config.strings.bodySuccessResId
- else config.strings.bodyResId,
- modifier = Modifier.weight(1f)
- )
- Spacer(modifier = Modifier.width(76.dp))
- TutorialAnimation(
- gestureState,
- config,
- modifier = Modifier.weight(1f).padding(top = 8.dp)
- )
- }
- DoneButton(onDoneButtonClicked = onDoneButtonClicked)
- }
-}
-
-@Composable
-fun TutorialDescription(
- @StringRes titleTextId: Int,
- titleColor: Color,
- @StringRes bodyTextId: Int,
- modifier: Modifier = Modifier
-) {
- Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
- Text(
- text = stringResource(id = titleTextId),
- style = MaterialTheme.typography.displayLarge,
- color = titleColor
- )
- Spacer(modifier = Modifier.height(16.dp))
- Text(
- text = stringResource(id = bodyTextId),
- style = MaterialTheme.typography.bodyLarge,
- color = Color.White
- )
- }
-}
-
-@Composable
-fun TutorialAnimation(
- gestureState: GestureState,
- config: TutorialScreenConfig,
- modifier: Modifier = Modifier
-) {
- Box(modifier = modifier.fillMaxWidth()) {
- AnimatedContent(
- targetState = gestureState,
- transitionSpec = {
- if (initialState == NOT_STARTED && targetState == IN_PROGRESS) {
- val transitionDurationMillis = 150
- fadeIn(animationSpec = tween(transitionDurationMillis, easing = LinearEasing))
- .togetherWith(
- fadeOut(animationSpec = snap(delayMillis = transitionDurationMillis))
- )
- // we explicitly don't want size transform because when targetState
- // animation is loaded for the first time, AnimatedContent thinks target
- // size is smaller and tries to shrink initial state animation
- .using(sizeTransform = null)
- } else {
- // empty transition works because all remaining transitions are from IN_PROGRESS
- // state which shares initial animation frame with both FINISHED and NOT_STARTED
- EnterTransition.None togetherWith ExitTransition.None
- }
- }
- ) { gestureState ->
- when (gestureState) {
- NOT_STARTED ->
- EducationAnimation(
- config.animations.educationResId,
- config.colors.animationColors
- )
- IN_PROGRESS ->
- FrozenSuccessAnimation(
- config.animations.successResId,
- config.colors.animationColors
- )
- FINISHED ->
- SuccessAnimation(config.animations.successResId, config.colors.animationColors)
- }
- }
- }
-}
-
-@Composable
-private fun FrozenSuccessAnimation(
- @RawRes successAnimationId: Int,
- animationProperties: LottieDynamicProperties
-) {
- val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
- LottieAnimation(
- composition = composition,
- progress = { 0f }, // animation should freeze on 1st frame
- dynamicProperties = animationProperties,
- )
-}
-
-@Composable
-private fun EducationAnimation(
- @RawRes educationAnimationId: Int,
- animationProperties: LottieDynamicProperties
-) {
- val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
- val progress by
- animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
- LottieAnimation(
- composition = composition,
- progress = { progress },
- dynamicProperties = animationProperties,
- )
-}
-
-@Composable
-private fun SuccessAnimation(
- @RawRes successAnimationId: Int,
- animationProperties: LottieDynamicProperties
-) {
- val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
- val progress by animateLottieCompositionAsState(composition, iterations = 1)
- LottieAnimation(
- composition = composition,
- progress = { progress },
- dynamicProperties = animationProperties,
- )
-}
-
-@Composable
-fun rememberColorFilterProperty(
- layerName: String,
- color: Color
-): LottieDynamicProperty<ColorFilter> {
- return rememberLottieDynamicProperty(
- LottieProperty.COLOR_FILTER,
- value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
- // "**" below means match zero or more layers, so ** layerName ** means find layer with that
- // name at any depth
- keyPath = arrayOf("**", layerName, "**")
- )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index ed3110c..0a6283a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
@@ -21,6 +21,8 @@
import androidx.compose.runtime.remember
import com.airbnb.lottie.compose.rememberLottieDynamicProperties
import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
+import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty
import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureMonitor
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 14355fa..65b452a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -34,6 +34,7 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton
import com.android.systemui.res.R
@Composable
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 ad8ab30..256c5b5 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
@@ -27,9 +27,11 @@
import androidx.lifecycle.Lifecycle.State.STARTED
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
+import com.android.systemui.inputdevice.tutorial.ui.composable.ActionKeyTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen
import com.android.systemui.touchpad.tutorial.ui.composable.TutorialSelectionScreen
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.ACTION_KEY
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.BACK_GESTURE
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.HOME_GESTURE
import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.TUTORIAL_SELECTION
@@ -71,7 +73,7 @@
TutorialSelectionScreen(
onBackTutorialClicked = { vm.goTo(BACK_GESTURE) },
onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) },
- onActionKeyTutorialClicked = {},
+ onActionKeyTutorialClicked = { vm.goTo(ACTION_KEY) },
onDoneButtonClicked = closeTutorial
)
BACK_GESTURE ->
@@ -84,5 +86,10 @@
onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
onBack = { vm.goTo(TUTORIAL_SELECTION) },
)
+ ACTION_KEY -> // TODO(b/358105049) move action key tutorial to OOBE flow
+ ActionKeyTutorialScreen(
+ onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
+ onBack = { vm.goTo(TUTORIAL_SELECTION) },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
index 11984af..d3aeaa7 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
@@ -55,4 +55,5 @@
TUTORIAL_SELECTION,
BACK_GESTURE,
HOME_GESTURE,
+ ACTION_KEY,
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index 56b46624..32f2ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -116,7 +116,7 @@
return mCreateUserDialogController.createDialog(
this,
this::startActivity,
- (mUserCreator.isMultipleAdminEnabled() && mUserCreator.isUserAdmin()
+ (mUserCreator.canCreateAdminUser() && mUserCreator.isUserAdmin()
&& !isKeyguardShowing),
this::addUserNow,
this::finish
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
index 9304a46..a426da9 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.pm.UserInfo
import android.graphics.drawable.Drawable
+import android.multiuser.Flags
import android.os.UserManager
import com.android.internal.util.UserIcons
import com.android.settingslib.users.UserCreatingDialog
@@ -91,7 +92,17 @@
return userManager.isAdminUser
}
- fun isMultipleAdminEnabled(): Boolean {
- return UserManager.isMultipleAdminEnabled()
+ /**
+ * Checks if the creation of a new admin user is allowed.
+ *
+ * @return `true` if creating a new admin is allowed, `false` otherwise.
+ */
+ fun canCreateAdminUser(): Boolean {
+ return if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ UserManager.isMultipleAdminEnabled() &&
+ !userManager.hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN)
+ } else {
+ UserManager.isMultipleAdminEnabled()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
index e17274c..ade6c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
@@ -19,11 +19,14 @@
import android.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.SafeActivatable
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printCollection
import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
@@ -64,19 +67,20 @@
}
/**
- * An implementation of [FlowDumper]. This be extended directly, or can be used to implement
- * [FlowDumper] by delegation.
- *
- * @param dumpManager if provided, this will be used by the [FlowDumperImpl] to register and
- * unregister itself when there is something to dump.
- * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
- * class's name will be used. If you're implementing by delegation, you probably want to provide
- * this tag to get a meaningful dumpable name.
+ * The minimal implementation of FlowDumper. The owner must either register this with the
+ * DumpManager, or else call [dumpFlows] from its own [Dumpable.dump] method.
*/
-open class FlowDumperImpl(private val dumpManager: DumpManager?, tag: String? = null) : FlowDumper {
+open class SimpleFlowDumper : FlowDumper {
+
private val stateFlowMap = ConcurrentHashMap<String, StateFlow<*>>()
private val sharedFlowMap = ConcurrentHashMap<String, SharedFlow<*>>()
private val flowCollectionMap = ConcurrentHashMap<Pair<String, String>, Any>()
+
+ protected fun isNotEmpty(): Boolean =
+ stateFlowMap.isNotEmpty() || sharedFlowMap.isNotEmpty() || flowCollectionMap.isNotEmpty()
+
+ protected open fun onMapKeysChanged(added: Boolean) {}
+
override fun dumpFlows(pw: IndentingPrintWriter) {
pw.printCollection("StateFlow (value)", stateFlowMap.toSortedMap().entries) { (key, flow) ->
append(key).append('=').println(flow.value)
@@ -92,43 +96,62 @@
}
}
- private val Any.idString: String
- get() = Integer.toHexString(System.identityHashCode(this))
-
override fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T> = flow {
val mapKey = dumpName to idString
try {
collect {
flowCollectionMap[mapKey] = it ?: "null"
- updateRegistration(required = true)
+ onMapKeysChanged(added = true)
emit(it)
}
} finally {
flowCollectionMap.remove(mapKey)
- updateRegistration(required = false)
+ onMapKeysChanged(added = false)
}
}
override fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F {
stateFlowMap[dumpName] = this
+ onMapKeysChanged(added = true)
return this
}
override fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F {
sharedFlowMap[dumpName] = this
+ onMapKeysChanged(added = true)
return this
}
- private val dumpManagerName = tag ?: "[$idString] ${javaClass.simpleName}"
+ protected val Any.idString: String
+ get() = Integer.toHexString(System.identityHashCode(this))
+}
+
+/**
+ * An implementation of [FlowDumper] that registers itself whenever there is something to dump. This
+ * class is meant to be extended.
+ *
+ * @param dumpManager this will be used by the [FlowDumperImpl] to register and unregister itself
+ * when there is something to dump.
+ * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
+ * class's name will be used.
+ */
+abstract class FlowDumperImpl(
+ private val dumpManager: DumpManager,
+ private val tag: String? = null,
+) : SimpleFlowDumper() {
+
+ override fun onMapKeysChanged(added: Boolean) {
+ updateRegistration(required = added)
+ }
+
+ private val dumpManagerName = "[$idString] ${tag ?: javaClass.simpleName}"
+
private var registered = AtomicBoolean(false)
+
private fun updateRegistration(required: Boolean) {
- if (dumpManager == null) return
if (required && registered.get()) return
synchronized(registered) {
- val shouldRegister =
- stateFlowMap.isNotEmpty() ||
- sharedFlowMap.isNotEmpty() ||
- flowCollectionMap.isNotEmpty()
+ val shouldRegister = isNotEmpty()
val wasRegistered = registered.getAndSet(shouldRegister)
if (wasRegistered != shouldRegister) {
if (shouldRegister) {
@@ -140,3 +163,49 @@
}
}
}
+
+/**
+ * A [FlowDumper] that also has an [activateFlowDumper] suspend function that allows the dumper to
+ * be registered with the [DumpManager] only when activated, just like
+ * [Activatable.activate()][com.android.systemui.lifecycle.Activatable.activate].
+ */
+interface ActivatableFlowDumper : FlowDumper {
+ suspend fun activateFlowDumper()
+}
+
+/**
+ * Implementation of [ActivatableFlowDumper] that only registers when activated.
+ *
+ * This is generally used to implement [ActivatableFlowDumper] by delegation, especially for
+ * [SysUiViewModel] implementations.
+ *
+ * @param dumpManager used to automatically register and unregister this instance when activated and
+ * there is something to dump.
+ * @param tag the name with which this is dumper registered.
+ */
+class ActivatableFlowDumperImpl(
+ private val dumpManager: DumpManager,
+ tag: String,
+) : SimpleFlowDumper(), ActivatableFlowDumper {
+
+ private val registration =
+ object : SafeActivatable() {
+ override suspend fun onActivated() {
+ try {
+ dumpManager.registerCriticalDumpable(
+ dumpManagerName,
+ this@ActivatableFlowDumperImpl
+ )
+ awaitCancellation()
+ } finally {
+ dumpManager.unregisterDumpable(dumpManagerName)
+ }
+ }
+ }
+
+ private val dumpManagerName = "[$idString] $tag"
+
+ override suspend fun activateFlowDumper() {
+ registration.activate()
+ }
+}
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 5d8b6f1..d39daaf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -79,13 +79,15 @@
localBluetoothManager: LocalBluetoothManager?,
@Application coroutineScope: CoroutineScope,
@Background coroutineContext: CoroutineContext,
+ volumeLogger: VolumeLogger
): AudioSharingRepository =
if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
AudioSharingRepositoryImpl(
contentResolver,
localBluetoothManager,
coroutineScope,
- coroutineContext
+ coroutineContext,
+ volumeLogger
)
} else {
AudioSharingRepositoryEmptyImpl()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
index e46ce26..24fb001 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
import java.io.PrintWriter
import javax.inject.Inject
@@ -27,10 +28,15 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
-private const val TAG = "VolumePanelGlobalState"
+private const val TAG = "VolumePanelGlobalStateRepository"
@SysUISingleton
-class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpManager) : Dumpable {
+class VolumePanelGlobalStateRepository
+@Inject
+constructor(
+ dumpManager: DumpManager,
+ private val logger: VolumePanelLogger,
+) : Dumpable {
private val mutableGlobalState =
MutableStateFlow(
@@ -48,6 +54,7 @@
update: (currentState: VolumePanelGlobalState) -> VolumePanelGlobalState
) {
mutableGlobalState.update(update)
+ logger.onVolumePanelGlobalStateChanged(mutableGlobalState.value)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
index 5301b00..9de862a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
@@ -19,6 +19,7 @@
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.domain.model.ComponentModel
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import javax.inject.Inject
import javax.inject.Provider
@@ -26,8 +27,12 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
interface ComponentsInteractor {
@@ -45,6 +50,7 @@
enabledComponents: Collection<VolumePanelComponentKey>,
defaultCriteria: Provider<ComponentAvailabilityCriteria>,
@VolumePanelScope coroutineScope: CoroutineScope,
+ private val logger: VolumePanelLogger,
private val criteriaByKey:
Map<
VolumePanelComponentKey,
@@ -57,12 +63,18 @@
combine(
enabledComponents.map { componentKey ->
val componentCriteria = (criteriaByKey[componentKey] ?: defaultCriteria).get()
- componentCriteria.isAvailable().map { isAvailable ->
- ComponentModel(componentKey, isAvailable = isAvailable)
- }
+ componentCriteria
+ .isAvailable()
+ .distinctUntilChanged()
+ .conflate()
+ .onEach { logger.onComponentAvailabilityChanged(componentKey, it) }
+ .map { isAvailable ->
+ ComponentModel(componentKey, isAvailable = isAvailable)
+ }
}
) {
it.asList()
}
- .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1)
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
}
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
index cc513b5..276326c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
@@ -20,15 +20,41 @@
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 com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
+import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
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 onVolumePanelStateChanged(state: VolumePanelState) {
+ logBuffer.log(TAG, LogLevel.DEBUG, { str1 = state.toString() }, { "State changed: $str1" })
+ }
+
+ fun onComponentAvailabilityChanged(key: VolumePanelComponentKey, isAvailable: Boolean) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = key
+ bool1 = isAvailable
+ },
+ { "$str1 isAvailable=$bool1" }
+ )
+ }
+
+ fun onVolumePanelGlobalStateChanged(globalState: VolumePanelGlobalState) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { bool1 = globalState.isVisible },
+ { "Global state changed: isVisible=$bool1" }
+ )
+ }
+
fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
logBuffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
index 1c51236..a06d3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.ui.layout
import com.android.systemui.volume.panel.ui.viewmodel.ComponentState
+import com.android.systemui.volume.panel.ui.viewmodel.toLogString
/** Represents components grouping into the layout. */
data class ComponentsLayout(
@@ -29,3 +30,12 @@
/** This is a separated entity that is always visible on the bottom of the Volume Panel. */
val bottomBarComponent: ComponentState,
)
+
+fun ComponentsLayout.toLogString(): String {
+ return "(" +
+ " headerComponents=${headerComponents.joinToString { it.toLogString() }}" +
+ " contentComponents=${contentComponents.joinToString { it.toLogString() }}" +
+ " footerComponents=${footerComponents.joinToString { it.toLogString() }}" +
+ " bottomBarComponent=${bottomBarComponent.toLogString()}" +
+ " )"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
index 5f4dbfb..41c80fa 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
@@ -32,3 +32,5 @@
val component: VolumePanelUiComponent,
val isVisible: Boolean,
)
+
+fun ComponentState.toLogString(): String = "$key:visible=$isVisible"
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index f495a02f..2f60c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -19,23 +19,30 @@
import android.content.Context
import android.content.IntentFilter
import android.content.res.Resources
+import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.util.kotlin.launchAndDispose
import com.android.systemui.volume.VolumePanelDialogReceiver
import com.android.systemui.volume.panel.dagger.VolumePanelComponent
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager
+import com.android.systemui.volume.panel.ui.layout.toLogString
+import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -43,19 +50,23 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
+private const val TAG = "VolumePanelViewModel"
+
// Can't inject a constructor here because VolumePanelComponent provides this view model for its
// components.
+@OptIn(ExperimentalCoroutinesApi::class)
class VolumePanelViewModel(
resources: Resources,
coroutineScope: CoroutineScope,
daggerComponentFactory: VolumePanelComponentFactory,
configurationController: ConfigurationController,
broadcastDispatcher: BroadcastDispatcher,
+ private val dumpManager: DumpManager,
+ private val logger: VolumePanelLogger,
private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
-) {
+) : Dumpable {
private val volumePanelComponent: VolumePanelComponent =
daggerComponentFactory.create(this, coroutineScope)
@@ -77,9 +88,10 @@
.onStart { emit(resources.configuration) }
.map { configuration ->
VolumePanelState(
- orientation = configuration.orientation,
- isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
- )
+ orientation = configuration.orientation,
+ isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
+ )
+ .also { logger.onVolumePanelStateChanged(it) }
}
.stateIn(
scope,
@@ -89,7 +101,7 @@
isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen)
),
)
- val componentsLayout: Flow<ComponentsLayout> =
+ val componentsLayout: StateFlow<ComponentsLayout?> =
combine(
componentsInteractor.components,
volumePanelState,
@@ -104,13 +116,18 @@
}
componentsLayoutManager.layout(scope, componentStates)
}
- .shareIn(
+ .stateIn(
scope,
SharingStarted.Eagerly,
- replay = 1,
+ null,
)
init {
+ scope.launchAndDispose {
+ dumpManager.registerNormalDumpable(TAG, this)
+ DisposableHandle { dumpManager.unregisterDumpable(TAG) }
+ }
+
volumePanelComponent.volumePanelStartables().onEach(VolumePanelStartable::start)
broadcastDispatcher
.broadcastFlow(IntentFilter(VolumePanelDialogReceiver.DISMISS_ACTION))
@@ -122,6 +139,13 @@
volumePanelGlobalStateInteractor.setVisible(false)
}
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ with(pw) {
+ println("volumePanelState=${volumePanelState.value}")
+ println("componentsLayout=${componentsLayout.value?.toLogString()}")
+ }
+ }
+
class Factory
@Inject
constructor(
@@ -129,6 +153,8 @@
private val daggerComponentFactory: VolumePanelComponentFactory,
private val configurationController: ConfigurationController,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val dumpManager: DumpManager,
+ private val logger: VolumePanelLogger,
private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
) {
@@ -139,6 +165,8 @@
daggerComponentFactory,
configurationController,
broadcastDispatcher,
+ dumpManager,
+ logger,
volumePanelGlobalStateInteractor,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
index 869a82a..d6b159e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
@@ -16,7 +16,8 @@
package com.android.systemui.volume.shared
-import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
+import com.android.settingslib.volume.shared.AudioLogger
+import com.android.settingslib.volume.shared.AudioSharingLogger
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.systemui.dagger.SysUISingleton
@@ -30,7 +31,7 @@
/** Logs general System UI volume events. */
@SysUISingleton
class VolumeLogger @Inject constructor(@VolumeLog private val logBuffer: LogBuffer) :
- AudioRepositoryImpl.Logger {
+ AudioLogger, AudioSharingLogger {
override fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
logBuffer.log(
@@ -55,4 +56,35 @@
{ "Volume update received: stream=$str1 volume=$int1" }
)
}
+
+ override fun onAudioSharingStateChanged(state: Boolean) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { bool1 = state },
+ { "Audio sharing state update: state=$bool1" }
+ )
+ }
+
+ override fun onSecondaryGroupIdChanged(groupId: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { int1 = groupId },
+ { "Secondary group id in audio sharing update: groupId=$int1" }
+ )
+ }
+
+ override fun onVolumeMapChanged(map: Map<Int, Int>) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = map.toString() },
+ { "Volume map update: map=$str1" }
+ )
+ }
+
+ override fun onSetDeviceVolumeRequested(volume: Int) {
+ logBuffer.log(TAG, LogLevel.DEBUG, { int1 = volume }, { "Set device volume: volume=$int1" })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 9ca0591..50efc21 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -34,6 +34,8 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
@@ -378,8 +380,8 @@
}
@Override
- public void setImeWindowStatus(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
if (displayId == mDisplayTracker.getDefaultDisplayId()
&& (vis & InputMethodService.IME_VISIBLE) != 0) {
oneHanded.stopOneHanded(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 997f8a8..344d065 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -80,6 +80,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository;
@@ -109,6 +111,8 @@
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import kotlin.Lazy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -127,6 +131,7 @@
private ScreenDecorations mScreenDecorations;
private WindowManager mWindowManager;
+ private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private DisplayManager mDisplayManager;
private SecureSettings mSecureSettings;
private FakeExecutor mExecutor;
@@ -173,6 +178,8 @@
private CutoutDecorProviderFactory mCutoutFactory;
@Mock
private JavaAdapter mJavaAdapter;
+ @Mock
+ private Lazy<ViewCapture> mLazyViewCapture;
private FakeFacePropertyRepository mFakeFacePropertyRepository =
new FakeFacePropertyRepository();
@@ -245,12 +252,15 @@
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository));
+ mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
+ mLazyViewCapture, false);
mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
mThreadFactory,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
- mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader) {
+ mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
+ mViewCaptureAwareWindowManager) {
@Override
public void start() {
super.start();
@@ -1264,7 +1274,8 @@
mDotViewController,
mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
- mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader);
+ mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
+ mViewCaptureAwareWindowManager);
screenDecorations.start();
when(mContext.getDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index b71739a..5ff3915 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -37,6 +37,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
@@ -46,6 +48,8 @@
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -71,6 +75,7 @@
private Context mContextWrapper;
private WindowManager mWindowManager;
+ private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private AccessibilityManager mAccessibilityManager;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private AccessibilityFloatingMenuController mController;
@@ -83,6 +88,8 @@
private KeyguardUpdateMonitorCallback mKeyguardCallback;
@Mock
private SecureSettings mSecureSettings;
+ @Mock
+ private Lazy<ViewCapture> mLazyViewCapture;
@Before
public void setUp() throws Exception {
@@ -95,6 +102,8 @@
};
mWindowManager = mContext.getSystemService(WindowManager.class);
+ mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
+ mLazyViewCapture, /* isViewCaptureEnabled= */ false);
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
@@ -154,7 +163,7 @@
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mAccessibilityManager, mSecureSettings);
+ mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
@@ -181,7 +190,7 @@
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mAccessibilityManager, mSecureSettings);
+ mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -195,7 +204,7 @@
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mAccessibilityManager, mSecureSettings);
+ mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -321,13 +330,17 @@
private AccessibilityFloatingMenuController setUpController() {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ final ViewCaptureAwareWindowManager viewCaptureAwareWindowManager =
+ new ViewCaptureAwareWindowManager(windowManager, mLazyViewCapture,
+ /* isViewCaptureEnabled= */ false);
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
final FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
- displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver,
- mKeyguardUpdateMonitor, mSecureSettings, displayTracker);
+ viewCaptureAwareWindowManager, displayManager, mAccessibilityManager,
+ mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings,
+ displayTracker);
controller.init();
return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index bd1a7f0..07ce7b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -38,9 +38,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Lazy;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -69,11 +73,16 @@
@Mock
private WindowMetrics mWindowMetrics;
+ @Mock
+ private Lazy<ViewCapture> mLazyViewCapture;
+
private MenuViewLayerController mMenuViewLayerController;
@Before
public void setUp() throws Exception {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ final ViewCaptureAwareWindowManager viewCaptureAwareWm = new ViewCaptureAwareWindowManager(
+ mWindowManager, mLazyViewCapture, /* isViewCaptureEnabled= */ false);
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
@@ -81,7 +90,7 @@
when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1080, 2340));
when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets());
mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager,
- mAccessibilityManager, mSecureSettings);
+ viewCaptureAwareWm, mAccessibilityManager, mSecureSettings);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
new file mode 100644
index 0000000..2ac5d10
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.accessibility.hearingaid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptyList;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHapClient;
+import android.bluetooth.BluetoothHapPresetInfo;
+import android.testing.TestableLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HapClientProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.SysuiTestCase;
+
+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;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** Tests for {@link HearingDevicesPresetsController}. */
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
+
+ private static final int TEST_PRESET_INDEX = 1;
+ private static final String TEST_PRESET_NAME = "test_preset";
+ private static final int TEST_HAP_GROUP_ID = 1;
+ private static final int TEST_REASON = 1024;
+
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private HapClientProfile mHapClientProfile;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private CachedBluetoothDevice mSubCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ @Mock
+ private BluetoothDevice mSubBluetoothDevice;
+
+ @Mock
+ private HearingDevicesPresetsController.PresetCallback mCallback;
+
+ private HearingDevicesPresetsController mController;
+
+ @Before
+ public void setUp() {
+ when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+ when(mHapClientProfile.isProfileReady()).thenReturn(true);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
+ when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
+
+ mController = new HearingDevicesPresetsController(mProfileManager, mCallback);
+ }
+
+ @Test
+ public void onServiceConnected_callExpectedCallback() {
+ mController.onServiceConnected();
+
+ verify(mHapClientProfile).registerCallback(any(Executor.class),
+ any(BluetoothHapClient.Callback.class));
+ verify(mCallback).onPresetInfoUpdated(anyList(), anyInt());
+ }
+
+ @Test
+ public void getAllPresetInfo_setInvalidHearingDevice_getEmpty() {
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(emptyList());
+ mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+
+ assertThat(mController.getAllPresetInfo()).isEmpty();
+ }
+
+ @Test
+ public void getAllPresetInfo_containsNotAvailablePresetInfo_getEmpty() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(false);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+
+ assertThat(mController.getAllPresetInfo()).isEmpty();
+ }
+
+ @Test
+ public void getAllPresetInfo_containsOnePresetInfo_getOnePresetInfo() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+
+ assertThat(mController.getAllPresetInfo()).contains(hapPresetInfo);
+ }
+
+ @Test
+ public void getActivePresetIndex_getExpectedIndex() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+ TEST_PRESET_INDEX);
+
+ assertThat(mController.getActivePresetIndex()).isEqualTo(TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void onPresetSelected_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+ when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+ TEST_PRESET_INDEX);
+
+ mController.onPresetSelected(mBluetoothDevice, TEST_PRESET_INDEX, TEST_REASON);
+
+ verify(mCallback).onPresetInfoUpdated(eq(List.of(hapPresetInfo)), eq(TEST_PRESET_INDEX));
+ }
+
+ @Test
+ public void onPresetInfoChanged_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+ setValidHearingDeviceSupportHap();
+ BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+ when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+ List.of(hapPresetInfo));
+ when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+ TEST_PRESET_INDEX);
+
+ mController.onPresetInfoChanged(mBluetoothDevice, List.of(hapPresetInfo), TEST_REASON);
+
+ verify(mCallback).onPresetInfoUpdated(List.of(hapPresetInfo), TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void onPresetSelectionFailed_callOnPresetCommandFailed() {
+ setValidHearingDeviceSupportHap();
+
+ mController.onPresetSelectionFailed(mBluetoothDevice, TEST_REASON);
+
+ verify(mCallback).onPresetCommandFailed(TEST_REASON);
+ }
+
+ @Test
+ public void onSetPresetNameFailed_callOnPresetCommandFailed() {
+ setValidHearingDeviceSupportHap();
+
+ mController.onSetPresetNameFailed(mBluetoothDevice, TEST_REASON);
+
+ verify(mCallback).onPresetCommandFailed(TEST_REASON);
+ }
+
+ @Test
+ public void onPresetSelectionForGroupFailed_callSelectPresetIndividual() {
+ setValidHearingDeviceSupportHap();
+ mController.selectPreset(TEST_PRESET_INDEX);
+ Mockito.reset(mHapClientProfile);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+ mController.onPresetSelectionForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
+
+
+ verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void onSetPresetNameForGroupFailed_callOnPresetCommandFailed() {
+ setValidHearingDeviceSupportHap();
+
+ mController.onSetPresetNameForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
+
+ verify(mCallback).onPresetCommandFailed(TEST_REASON);
+ }
+
+ @Test
+ public void registerHapCallback_callHapRegisterCallback() {
+ mController.registerHapCallback();
+
+ verify(mHapClientProfile).registerCallback(any(Executor.class),
+ any(BluetoothHapClient.Callback.class));
+ }
+
+ @Test
+ public void unregisterHapCallback_callHapUnregisterCallback() {
+ mController.unregisterHapCallback();
+
+ verify(mHapClientProfile).unregisterCallback(any(BluetoothHapClient.Callback.class));
+ }
+
+ @Test
+ public void selectPreset_supportSynchronized_validGroupId_callSelectPresetForGroup() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+ mController.selectPreset(TEST_PRESET_INDEX);
+
+ verify(mHapClientProfile).selectPresetForGroup(TEST_HAP_GROUP_ID, TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndividual() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+
+ mController.selectPreset(TEST_PRESET_INDEX);
+
+ verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ }
+
+ @Test
+ public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndividual() {
+ setValidHearingDeviceSupportHap();
+ when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(false);
+ when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+ mController.selectPreset(TEST_PRESET_INDEX);
+
+ verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+ verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+ }
+
+ private BluetoothHapPresetInfo getHapPresetInfo(boolean available) {
+ BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class);
+ when(info.getName()).thenReturn(TEST_PRESET_NAME);
+ when(info.getIndex()).thenReturn(TEST_PRESET_INDEX);
+ when(info.isAvailable()).thenReturn(available);
+ return info;
+ }
+
+ private void setValidHearingDeviceSupportHap() {
+ LocalBluetoothProfile hapClientProfile = mock(HapClientProfile.class);
+ List<LocalBluetoothProfile> profiles = List.of(hapClientProfile);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(profiles);
+
+ mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index dc69cda..f94a6f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -29,7 +29,6 @@
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.os.Handler
import android.os.IBinder
import android.os.UserManager
import android.testing.TestableLooper
@@ -45,7 +44,6 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
import com.android.launcher3.icons.IconProvider
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -105,7 +103,6 @@
@Mock lateinit var fingerprintManager: FingerprintManager
@Mock lateinit var lockPatternUtils: LockPatternUtils
@Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector
@Mock lateinit var windowToken: IBinder
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@Mock lateinit var vibrator: VibratorHelper
@@ -265,14 +262,6 @@
}
@Test
- fun testActionCancel_panelInteractionDetectorDisable() {
- val container = initializeFingerprintContainer()
- container.mBiometricCallback.onUserCanceled()
- waitForIdleSync()
- verify(panelInteractionDetector).disable()
- }
-
- @Test
fun testActionAuthenticated_sendsDismissedAuthenticated() {
val container = initializeFingerprintContainer()
container.mBiometricCallback.onAuthenticated()
@@ -416,19 +405,7 @@
}
@Test
- fun testShowBiometricUI() {
- mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
- val container = initializeFingerprintContainer()
-
- waitForIdleSync()
-
- assertThat(container.hasCredentialView()).isFalse()
- assertThat(container.hasBiometricPrompt()).isTrue()
- }
-
- @Test
fun testShowBiometricUI_ContentViewWithMoreOptionsButton() {
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
var isButtonClicked = false
val contentView =
@@ -466,7 +443,6 @@
@Test
fun testShowCredentialUI_withVerticalListContentView() {
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val container =
initializeFingerprintContainer(
@@ -488,7 +464,6 @@
@Test
fun testShowCredentialUI_withContentViewWithMoreOptionsButton() {
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val contentView =
PromptContentViewWithMoreOptionsButton.Builder()
@@ -674,7 +649,6 @@
fingerprintProps,
faceProps,
wakefulnessLifecycle,
- panelInteractionDetector,
userManager,
lockPatternUtils,
interactionJankMonitor,
@@ -690,7 +664,6 @@
activityTaskManager
),
{ credentialViewModel },
- Handler(TestableLooper.get(this).looper),
fakeExecutor,
vibrator
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
deleted file mode 100644
index 0231486..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ /dev/null
@@ -1,156 +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.biometrics
-
-import android.platform.test.flag.junit.FlagsParameterization
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.andSceneContainer
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.testKosmos
-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.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.kotlin.verifyZeroInteractions
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-
-@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
-class AuthDialogPanelInteractionDetectorTest(flags: FlagsParameterization?) : SysuiTestCase() {
-
- companion object {
- @JvmStatic
- @Parameters(name = "{0}")
- fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
- }
- }
-
- init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
- }
-
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
-
- @Mock private lateinit var action: Runnable
-
- lateinit var detector: AuthDialogPanelInteractionDetector
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- detector =
- AuthDialogPanelInteractionDetector(
- kosmos.applicationCoroutineScope,
- { kosmos.shadeInteractor },
- )
- }
-
- @Test
- fun enableDetector_expand_shouldRunAction() =
- testScope.runTest {
- // GIVEN shade is closed and detector is enabled
- shadeTestUtil.setShadeExpansion(0f)
- detector.enable(action)
- runCurrent()
-
- // WHEN shade expands
- shadeTestUtil.setTracking(true)
- shadeTestUtil.setShadeExpansion(.5f)
- runCurrent()
-
- // THEN action was run
- verify(action).run()
- }
-
- @Test
- fun enableDetector_isUserInteractingTrue_shouldNotPostRunnable() =
- testScope.runTest {
- // GIVEN isInteracting starts true
- shadeTestUtil.setTracking(true)
- runCurrent()
- detector.enable(action)
-
- // THEN action was not run
- verifyZeroInteractions(action)
- }
-
- @Test
- fun enableDetector_shadeExpandImmediate_shouldNotPostRunnable() =
- testScope.runTest {
- // GIVEN shade is closed and detector is enabled
- shadeTestUtil.setShadeExpansion(0f)
- detector.enable(action)
- runCurrent()
-
- // WHEN shade expands fully instantly
- shadeTestUtil.setShadeExpansion(1f)
- runCurrent()
-
- // THEN action not run
- verifyZeroInteractions(action)
- detector.disable()
- }
-
- @Test
- fun disableDetector_shouldNotPostRunnable() =
- testScope.runTest {
- // GIVEN shade is closed and detector is enabled
- shadeTestUtil.setShadeExpansion(0f)
- detector.enable(action)
- runCurrent()
-
- // WHEN detector is disabled and shade opens
- detector.disable()
- runCurrent()
- shadeTestUtil.setTracking(true)
- shadeTestUtil.setShadeExpansion(.5f)
- runCurrent()
-
- // THEN action not run
- verifyZeroInteractions(action)
- }
-
- @Test
- fun enableDetector_beginCollapse_shouldNotPostRunnable() =
- testScope.runTest {
- // GIVEN shade is open and detector is enabled
- shadeTestUtil.setShadeExpansion(1f)
- detector.enable(action)
- runCurrent()
-
- // WHEN shade begins to collapse
- shadeTestUtil.programmaticCollapseShade()
- runCurrent()
-
- // THEN action not run
- verifyZeroInteractions(action)
- detector.disable()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 720f207..dc499cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -155,7 +155,7 @@
Authenticators.BIOMETRIC_STRONG
}
isDeviceCredentialAllowed = allowCredentialFallback
- componentNameForConfirmDeviceCredentialActivity =
+ realCallerForConfirmDeviceCredentialActivity =
if (setComponentNameForConfirmDeviceCredentialActivity)
componentNameOverriddenForConfirmDeviceCredentialActivity
else null
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 534f25c..6047e7d 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
@@ -43,7 +43,6 @@
import android.view.Surface
import androidx.test.filters.SmallTest
import com.android.app.activityTaskManager
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.Utils.toBitmap
@@ -1385,7 +1384,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun descriptionOverriddenByVerticalListContentView() =
runGenericTest(description = "test description", contentView = promptContentView) {
val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1396,7 +1395,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
runGenericTest(
description = "test description",
@@ -1410,7 +1409,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun descriptionWithoutContentView() =
runGenericTest(description = "test description") {
val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1421,7 +1420,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_nullIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1430,7 +1429,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_defaultFromActivityInfo() =
runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1445,7 +1444,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_defaultIsNull() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1454,7 +1453,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_default() = runGenericTest {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat(logoInfo).isNotNull()
@@ -1462,7 +1461,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_resSetByApp() =
runGenericTest(logoRes = logoResFromApp) {
val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
@@ -1472,7 +1471,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logo_bitmapSetByApp() =
runGenericTest(logoBitmap = logoBitmapFromApp) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1480,7 +1479,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_emptyIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1488,7 +1487,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_defaultFromActivityInfo() =
runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1500,7 +1499,7 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_defaultIsEmpty() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1508,14 +1507,14 @@
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_default() = runGenericTest {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
}
@Test
- @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
fun logoDescription_setByApp() =
runGenericTest(logoDescription = logoDescriptionFromApp) {
val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1523,7 +1522,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_rotation0() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1531,7 +1529,6 @@
} // TODO(b/335278136): Add test for no sensor landscape
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_forceLarge() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
kosmos.promptViewModel.onSwitchToCredential()
@@ -1540,7 +1537,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_largeScreen() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
kosmos.displayStateRepository.setIsLargeScreen(true)
@@ -1549,7 +1545,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun position_right_rotation90() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1557,7 +1552,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun position_left_rotation270() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1565,7 +1559,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun position_top_rotation180() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1577,7 +1570,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_bottom() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
@@ -1585,7 +1577,6 @@
} // TODO(b/335278136): Add test for no sensor landscape
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_right() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
@@ -1602,7 +1593,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_right_onlyShortTitle() =
runGenericTest(subtitle = "") {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
@@ -1617,7 +1607,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_left() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
@@ -1634,7 +1623,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_left_onlyShortTitle() =
runGenericTest(subtitle = "") {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
@@ -1649,7 +1637,6 @@
}
@Test
- @EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_top() = runGenericTest {
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 3b2cf61..0db7b62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -32,7 +32,6 @@
import com.airbnb.lottie.model.KeyPath
import com.android.keyguard.keyguardUpdateMonitor
import com.android.settingslib.Utils
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
import com.android.systemui.biometrics.data.repository.biometricStatusRepository
@@ -199,7 +198,6 @@
@Test
fun updatesOverlayViewParams_onDisplayRotationChange_xAlignedSensor() {
kosmos.testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
setupTestConfiguration(
DeviceConfig.X_ALIGNED,
rotation = DisplayRotation.ROTATION_0,
@@ -230,11 +228,11 @@
.isEqualTo(
displayWidth - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
)
- assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
+ assertThat(overlayViewParams!!.y).isEqualTo(displayHeight)
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
assertThat(overlayViewParams).isNotNull()
- assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
+ assertThat(overlayViewParams!!.x).isEqualTo(displayWidth)
assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationX)
}
}
@@ -242,7 +240,6 @@
@Test
fun updatesOverlayViewParams_onDisplayRotationChange_yAlignedSensor() {
kosmos.testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
setupTestConfiguration(
DeviceConfig.Y_ALIGNED,
rotation = DisplayRotation.ROTATION_0,
@@ -256,7 +253,7 @@
runCurrent()
assertThat(overlayViewParams).isNotNull()
- assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
+ assertThat(overlayViewParams!!.x).isEqualTo(displayWidth)
assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationY)
kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
@@ -278,7 +275,7 @@
.isEqualTo(
displayWidth - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
)
- assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
+ assertThat(overlayViewParams!!.y).isEqualTo(displayHeight)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
index 4d7c499..2c53fd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
@@ -113,7 +113,8 @@
assertThat(actual)
.isEqualTo(
AudioSharingButtonState.Visible(
- R.string.quick_settings_bluetooth_audio_sharing_button_sharing
+ R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
+ isActive = true
)
)
}
@@ -163,7 +164,8 @@
assertThat(actual)
.isEqualTo(
AudioSharingButtonState.Visible(
- R.string.quick_settings_bluetooth_audio_sharing_button
+ R.string.quick_settings_bluetooth_audio_sharing_button,
+ isActive = false
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index d01fac3..fb0fd23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -328,4 +328,70 @@
dialog.dismiss()
}
}
+
+ @Test
+ fun testOnAudioSharingButtonUpdated_visibleActive_activateButton() {
+ testScope.runTest {
+ val dialog = mBluetoothTileDialogDelegate.createDialog()
+ dialog.show()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothTileDialogDelegate.onAudioSharingButtonUpdated(
+ dialog,
+ visibility = VISIBLE,
+ label = null,
+ isActive = true
+ )
+
+ val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
+
+ assertThat(audioSharingButton).isNotNull()
+ assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
+ assertThat(audioSharingButton.isActivated).isTrue()
+ dialog.dismiss()
+ }
+ }
+
+ @Test
+ fun testOnAudioSharingButtonUpdated_visibleNotActive_inactivateButton() {
+ testScope.runTest {
+ val dialog = mBluetoothTileDialogDelegate.createDialog()
+ dialog.show()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothTileDialogDelegate.onAudioSharingButtonUpdated(
+ dialog,
+ visibility = VISIBLE,
+ label = null,
+ isActive = false
+ )
+
+ val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
+
+ assertThat(audioSharingButton).isNotNull()
+ assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
+ assertThat(audioSharingButton.isActivated).isFalse()
+ dialog.dismiss()
+ }
+ }
+
+ @Test
+ fun testOnAudioSharingButtonUpdated_gone_inactivateButton() {
+ testScope.runTest {
+ val dialog = mBluetoothTileDialogDelegate.createDialog()
+ dialog.show()
+ fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+ mBluetoothTileDialogDelegate.onAudioSharingButtonUpdated(
+ dialog,
+ visibility = GONE,
+ label = null,
+ isActive = false
+ )
+
+ val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
+
+ assertThat(audioSharingButton).isNotNull()
+ assertThat(audioSharingButton.visibility).isEqualTo(GONE)
+ assertThat(audioSharingButton.isActivated).isFalse()
+ dialog.dismiss()
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 034bab8..57b397c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -23,11 +23,13 @@
import android.view.WindowMetrics
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -40,12 +42,12 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -60,6 +62,7 @@
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var windowMetrics: WindowMetrics
+ @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private val systemClock = FakeSystemClock()
@Before
@@ -68,7 +71,9 @@
`when`(featureFlags.isEnabled(Flags.CHARGING_RIPPLE)).thenReturn(true)
controller = WiredChargingRippleController(
commandRegistry, batteryController, configurationController,
- featureFlags, context, windowManager, systemClock, uiEventLogger)
+ featureFlags, context, windowManager,
+ ViewCaptureAwareWindowManager(windowManager,
+ lazyViewCapture, isViewCaptureEnabled = false), systemClock, uiEventLogger)
rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks()
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 419f7ed..299c384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
@@ -29,31 +29,22 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
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;
private DozeMachine.Service mInner;
private DozeScreenStatePreventingAdapter mWrapper;
@Before
public void setup() throws Exception {
- mExecutor = new FakeExecutor(new FakeSystemClock());
mInner = mock(DozeMachine.Service.class);
- mWrapper = new DozeScreenStatePreventingAdapter(
- mInner,
- mExecutor
- );
+ mWrapper = new DozeScreenStatePreventingAdapter(mInner);
}
@Test
@@ -98,7 +89,7 @@
when(params.getDisplayStateSupported()).thenReturn(false);
assertEquals(DozeScreenStatePreventingAdapter.class,
- DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params, mExecutor)
+ DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params)
.getClass());
}
@@ -107,7 +98,6 @@
DozeParameters params = mock(DozeParameters.class);
when(params.getDisplayStateSupported()).thenReturn(true);
- assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params,
- mExecutor));
+ assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params));
}
}
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 5a89710..0d6a9ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
@@ -29,28 +29,22 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
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;
private DozeMachine.Service mInner;
private DozeSuspendScreenStatePreventingAdapter mWrapper;
@Before
public void setup() throws Exception {
- mExecutor = new FakeExecutor(new FakeSystemClock());
mInner = mock(DozeMachine.Service.class);
- mWrapper = new DozeSuspendScreenStatePreventingAdapter(mInner, mExecutor);
+ mWrapper = new DozeSuspendScreenStatePreventingAdapter(mInner);
}
@Test
@@ -101,7 +95,7 @@
when(params.getDozeSuspendDisplayStateSupported()).thenReturn(false);
assertEquals(DozeSuspendScreenStatePreventingAdapter.class,
- DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params, mExecutor)
+ DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params)
.getClass());
}
@@ -110,7 +104,6 @@
DozeParameters params = mock(DozeParameters.class);
when(params.getDozeSuspendDisplayStateSupported()).thenReturn(true);
- assertSame(mInner, DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params,
- mExecutor));
+ assertSame(mInner, DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index e3a38a8..37f1a3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -443,7 +443,7 @@
mViewMediator.onSystemReady();
TestableLooper.get(this).processAllMessages();
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
TestableLooper.get(this).processAllMessages();
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -463,7 +463,7 @@
mViewMediator.onSystemReady();
TestableLooper.get(this).processAllMessages();
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
TestableLooper.get(this).processAllMessages();
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -570,7 +570,7 @@
// When showing and provisioned
mViewMediator.onSystemReady();
when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
- mViewMediator.setShowingLocked(true);
+ mViewMediator.setShowingLocked(true, "");
// and a SIM becomes locked and requires a PIN
mViewMediator.mUpdateCallback.onSimStateChanged(
@@ -579,7 +579,7 @@
TelephonyManager.SIM_STATE_PIN_REQUIRED);
// and the keyguard goes away
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
when(mKeyguardStateController.isShowing()).thenReturn(false);
mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
@@ -595,7 +595,7 @@
// When showing and provisioned
mViewMediator.onSystemReady();
when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
// and a SIM becomes locked and requires a PIN
mViewMediator.mUpdateCallback.onSimStateChanged(
@@ -604,7 +604,7 @@
TelephonyManager.SIM_STATE_PIN_REQUIRED);
// and the keyguard goes away
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
when(mKeyguardStateController.isShowing()).thenReturn(false);
mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
@@ -843,7 +843,7 @@
mViewMediator.onSystemReady();
TestableLooper.get(this).processAllMessages();
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
TestableLooper.get(this).processAllMessages();
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -863,7 +863,7 @@
mViewMediator.onSystemReady();
TestableLooper.get(this).processAllMessages();
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
TestableLooper.get(this).processAllMessages();
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -978,7 +978,7 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testDoKeyguardWhileInteractive_resets() {
- mViewMediator.setShowingLocked(true);
+ mViewMediator.setShowingLocked(true, "");
when(mKeyguardStateController.isShowing()).thenReturn(true);
TestableLooper.get(this).processAllMessages();
@@ -992,7 +992,7 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testDoKeyguardWhileNotInteractive_showsInsteadOfResetting() {
- mViewMediator.setShowingLocked(true);
+ mViewMediator.setShowingLocked(true, "");
when(mKeyguardStateController.isShowing()).thenReturn(true);
TestableLooper.get(this).processAllMessages();
@@ -1051,7 +1051,7 @@
mViewMediator.onSystemReady();
processAllMessagesAndBgExecutorMessages();
- mViewMediator.setShowingLocked(true);
+ mViewMediator.setShowingLocked(true, "");
RemoteAnimationTarget[] apps = new RemoteAnimationTarget[]{
mock(RemoteAnimationTarget.class)
@@ -1123,7 +1123,7 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testNotStartingKeyguardWhenFlagIsDisabled() {
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
when(mKeyguardStateController.isShowing()).thenReturn(false);
mViewMediator.onDreamingStarted();
@@ -1133,7 +1133,7 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testStartingKeyguardWhenFlagIsEnabled() {
- mViewMediator.setShowingLocked(true);
+ mViewMediator.setShowingLocked(true, "");
when(mKeyguardStateController.isShowing()).thenReturn(true);
mViewMediator.onDreamingStarted();
@@ -1174,7 +1174,7 @@
TestableLooper.get(this).processAllMessages();
// WHEN keyguard visibility becomes FALSE
- mViewMediator.setShowingLocked(false);
+ mViewMediator.setShowingLocked(false, "");
keyguardUpdateMonitorCallback.onKeyguardVisibilityChanged(false);
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 2af4d87..bfe89de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -17,9 +17,6 @@
package com.android.systemui.keyguard.data.repository
import android.animation.ValueAnimator
-import android.util.Log
-import android.util.Log.TerribleFailure
-import android.util.Log.TerribleFailureHandler
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
@@ -53,7 +50,6 @@
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -67,23 +63,14 @@
private val testScope = kosmos.testScope
private lateinit var underTest: KeyguardTransitionRepository
- private lateinit var oldWtfHandler: TerribleFailureHandler
- private lateinit var wtfHandler: WtfHandler
private lateinit var runner: KeyguardTransitionRunner
@Before
fun setUp() {
underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main)
- wtfHandler = WtfHandler()
- oldWtfHandler = Log.setWtfHandler(wtfHandler)
runner = KeyguardTransitionRunner(underTest)
}
- @After
- fun tearDown() {
- oldWtfHandler?.let { Log.setWtfHandler(it) }
- }
-
@Test
fun startTransitionRunsAnimatorToCompletion() =
testScope.runTest {
@@ -333,15 +320,17 @@
}
@Test
- fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() =
+ fun attemptTomanuallyUpdateTransitionWithInvalidUUIDEmitsNothing() =
testScope.runTest {
+ val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
- assertThat(wtfHandler.failed).isTrue()
+ assertThat(steps.size).isEqualTo(0)
}
@Test
- fun attemptToManuallyUpdateTransitionAfterFINISHEDstateThrowsException() =
+ fun attemptToManuallyUpdateTransitionAfterFINISHEDstateEmitsNothing() =
testScope.runTest {
+ val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
val uuid =
underTest.startTransition(
TransitionInfo(
@@ -356,12 +345,19 @@
underTest.updateTransition(it, 1f, TransitionState.FINISHED)
underTest.updateTransition(it, 0.5f, TransitionState.RUNNING)
}
- assertThat(wtfHandler.failed).isTrue()
+ assertThat(steps.size).isEqualTo(2)
+ assertThat(steps[0])
+ .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
+ assertThat(steps[1])
+ .isEqualTo(
+ TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME)
+ )
}
@Test
- fun attemptToManuallyUpdateTransitionAfterCANCELEDstateThrowsException() =
+ fun attemptToManuallyUpdateTransitionAfterCANCELEDstateEmitsNothing() =
testScope.runTest {
+ val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
val uuid =
underTest.startTransition(
TransitionInfo(
@@ -376,7 +372,13 @@
underTest.updateTransition(it, 0.2f, TransitionState.CANCELED)
underTest.updateTransition(it, 0.5f, TransitionState.RUNNING)
}
- assertThat(wtfHandler.failed).isTrue()
+ assertThat(steps.size).isEqualTo(2)
+ assertThat(steps[0])
+ .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
+ assertThat(steps[1])
+ .isEqualTo(
+ TransitionStep(AOD, LOCKSCREEN, 0.2f, TransitionState.CANCELED, OWNER_NAME)
+ )
}
@Test
@@ -530,8 +532,6 @@
}
assertThat(steps[steps.size - 1])
.isEqualTo(TransitionStep(from, to, lastValue, status, OWNER_NAME))
-
- assertThat(wtfHandler.failed).isFalse()
}
private fun getAnimator(): ValueAnimator {
@@ -541,14 +541,6 @@
}
}
- private class WtfHandler : TerribleFailureHandler {
- var failed = false
-
- override fun onTerribleFailure(tag: String, what: TerribleFailure, system: Boolean) {
- failed = true
- }
- }
-
companion object {
private const val OWNER_NAME = "KeyguardTransitionRunner"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index 313292a..664a0bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -19,10 +19,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -36,6 +39,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
@ExperimentalCoroutinesApi
@@ -49,20 +53,32 @@
private val underTest = kosmos.alternateBouncerViewModel
@Test
- fun showPrimaryBouncer() =
+ fun onTapped() =
testScope.runTest {
- underTest.showPrimaryBouncer()
+ underTest.onTapped()
verify(statusBarKeyguardViewManager).showPrimaryBouncer(any())
}
@Test
- fun hideAlternateBouncer() =
+ fun onRemovedFromWindow() =
testScope.runTest {
- underTest.hideAlternateBouncer()
+ underTest.onRemovedFromWindow()
verify(statusBarKeyguardViewManager).hideAlternateBouncer(any())
}
@Test
+ fun onBackRequested() =
+ testScope.runTest {
+ val dismissCallback = mock(IKeyguardDismissCallback::class.java)
+ kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+
+ underTest.onBackRequested()
+ kosmos.fakeExecutor.runAllReady()
+ verify(statusBarKeyguardViewManager).hideAlternateBouncer(any())
+ verify(dismissCallback).onDismissCancelled()
+ }
+
+ @Test
fun transitionToAlternateBouncer_scrimAlphaUpdate() =
testScope.runTest {
val scrimAlphas by collectValues(underTest.scrimAlpha)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index d5cd86e..c8cc6b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -50,14 +50,18 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +77,8 @@
private static final String TEST_PACKAGE = "test_package";
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
// Mock
private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
private MediaController mMediaController = mock(MediaController.class);
@@ -122,6 +128,9 @@
when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
mMediaControllers.add(mMediaController);
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
mMediaOutputController =
new MediaOutputController(
@@ -139,6 +148,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ volumePanelGlobalStateInteractor,
mUserTracker);
// Using a fake package will cause routing operations to fail, so we intercept
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index 87d2245..189a561 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -51,14 +51,18 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import com.google.common.base.Strings;
@@ -81,6 +85,8 @@
private static final String BROADCAST_CODE_TEST = "112233";
private static final String BROADCAST_CODE_UPDATE_TEST = "11223344";
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
// Mock
private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
@@ -123,6 +129,9 @@
when(mLocalBluetoothLeBroadcast.getProgramInfo()).thenReturn(BROADCAST_NAME_TEST);
when(mLocalBluetoothLeBroadcast.getBroadcastCode()).thenReturn(
BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8));
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
mMediaOutputController =
new MediaOutputController(
@@ -140,6 +149,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ volumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 856bc0b..714fad9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -72,15 +72,19 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import com.google.common.collect.ImmutableList;
@@ -158,11 +162,16 @@
@Mock
private UserTracker mUserTracker;
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
private FeatureFlags mFlags = mock(FeatureFlags.class);
private View mDialogLaunchView = mock(View.class);
private MediaOutputController.Callback mCallback = mock(MediaOutputController.Callback.class);
final Notification mNotification = mock(Notification.class);
+ private final VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
private Context mSpyContext;
private String mPackageName = null;
@@ -194,6 +203,7 @@
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
mCachedBluetoothDeviceManager);
+
mMediaOutputController =
new MediaOutputController(
mSpyContext,
@@ -210,6 +220,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
@@ -304,6 +315,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.start(mCb);
@@ -346,6 +358,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.start(mCb);
@@ -602,6 +615,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -636,6 +650,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -683,6 +698,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
@@ -710,6 +726,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
@@ -990,6 +1007,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
@@ -1193,6 +1211,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
testMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index f20b04a..90c2930 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -54,14 +54,18 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import org.junit.After;
import org.junit.Before;
@@ -84,6 +88,8 @@
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
// Mock
private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private MediaController mMediaController = mock(MediaController.class);
@@ -136,6 +142,9 @@
when(mMediaSessionManager.getActiveSessionsForUser(any(),
Mockito.eq(userHandle))).thenReturn(
mMediaControllers);
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
mMediaOutputController =
new MediaOutputController(
@@ -153,6 +162,7 @@
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ volumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = makeTestDialog(mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index ffbf62a..b3bd7d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -20,8 +20,8 @@
import android.os.Handler
import android.os.PowerManager
import android.view.ViewGroup
-import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.statusbar.CommandQueue
@@ -36,7 +36,7 @@
commandQueue: CommandQueue,
context: Context,
logger: MediaTttReceiverLogger,
- windowManager: WindowManager,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -55,7 +55,7 @@
commandQueue,
context,
logger,
- windowManager,
+ viewCaptureAwareWindowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index f1d833f..9afa5ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -31,12 +31,14 @@
import android.widget.ImageView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.taptotransfer.MediaTttFlags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryViewUiEventLogger
@@ -88,6 +90,9 @@
private lateinit var commandQueue: CommandQueue
@Mock
private lateinit var rippleController: MediaTttReceiverRippleController
+ @Mock
+ private lateinit var lazyViewCapture: Lazy<ViewCapture>
+ private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -122,11 +127,13 @@
fakeWakeLockBuilder = WakeLockFake.Builder(context)
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+ viewCaptureAwareWindowManager = ViewCaptureAwareWindowManager(windowManager,
+ lazyViewCapture, isViewCaptureEnabled = false)
controllerReceiver = FakeMediaTttChipControllerReceiver(
commandQueue,
context,
logger,
- windowManager,
+ viewCaptureAwareWindowManager,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -157,7 +164,7 @@
commandQueue,
context,
logger,
- windowManager,
+ viewCaptureAwareWindowManager,
FakeExecutor(FakeSystemClock()),
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 111b8d4..b4cad6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -33,6 +33,8 @@
import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.SysuiTestCase
@@ -100,6 +102,7 @@
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var swipeHandler: SwipeChipbarAwayGestureHandler
+ @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
private lateinit var chipbarCoordinator: ChipbarCoordinator
@@ -145,7 +148,8 @@
ChipbarCoordinator(
context,
chipbarLogger,
- windowManager,
+ ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
+ isViewCaptureEnabled = false),
fakeExecutor,
accessibilityManager,
configurationController,
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 b337ccf..8fbd3c8 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
@@ -24,7 +24,6 @@
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
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -141,27 +140,13 @@
}
@Test
- fun onRecentAppClicked_fullScreenTaskInForeground_flagOff_usesScaleUpAnimation() {
- mSetFlagsRule.disableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
-
- controller.onRecentAppClicked(fullScreenTask, taskView)
-
- assertThat(getStartedTaskActivityOptions(fullScreenTask.taskId).animationType)
- .isEqualTo(ActivityOptions.ANIM_SCALE_UP)
- }
-
- @Test
- fun onRecentAppClicked_fullScreenTaskInForeground_flagOn_usesDefaultAnimation() {
- mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
+ fun onRecentAppClicked_fullScreenTaskInForeground_usesDefaultAnimation() {
assertForegroundTaskUsesDefaultCloseAnimation(fullScreenTask)
}
@Test
fun onRecentAppClicked_splitScreenTaskInForeground_flagOn_usesDefaultAnimation() {
- mSetFlagsRule.enableFlags(
- FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX,
- FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN
- )
+ mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN)
assertForegroundTaskUsesDefaultCloseAnimation(splitScreenTask)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index a52ab0c..04d140c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -78,6 +78,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
@@ -214,6 +215,8 @@
@Mock
private WindowManager mWindowManager;
@Mock
+ private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ @Mock
private TelecomManager mTelecomManager;
@Mock
private InputMethodManager mInputMethodManager;
@@ -619,6 +622,7 @@
null,
context,
mWindowManager,
+ mViewCaptureAwareWindowManager,
() -> mAssistManager,
mock(AccessibilityManager.class),
deviceProvisionedController,
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 11b0bdf..7dae5cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -21,13 +21,13 @@
import android.testing.TestableLooper
import android.view.View
import android.widget.Spinner
+import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Dependency
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
@@ -60,7 +60,6 @@
@Mock private lateinit var starter: ActivityStarter
@Mock private lateinit var controller: RecordingController
@Mock private lateinit var userContextProvider: UserContextProvider
- @Mock private lateinit var flags: FeatureFlags
@Mock private lateinit var onStartRecordingClicked: Runnable
@Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
@@ -128,6 +127,32 @@
}
@Test
+ fun startButtonText_entireScreenSelected() {
+ showDialog()
+
+ onSpinnerItemSelected(ENTIRE_SCREEN)
+
+ assertThat(getStartButton().text)
+ .isEqualTo(
+ context.getString(R.string.screenrecord_permission_dialog_continue_entire_screen)
+ )
+ }
+
+ @Test
+ fun startButtonText_singleAppSelected() {
+ showDialog()
+
+ onSpinnerItemSelected(SINGLE_APP)
+
+ assertThat(getStartButton().text)
+ .isEqualTo(
+ context.getString(
+ R.string.media_projection_entry_generic_permission_dialog_continue_single_app
+ )
+ )
+ }
+
+ @Test
fun startClicked_singleAppSelected_passesHostUidToAppSelector() {
showDialog()
onSpinnerItemSelected(SINGLE_APP)
@@ -152,7 +177,8 @@
showDialog()
val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options)
- val singleApp = context.getString(R.string.screen_share_permission_dialog_option_single_app)
+ val singleApp =
+ context.getString(R.string.screenrecord_permission_dialog_option_text_single_app)
assertEquals(spinner.adapter.getItem(0), singleApp)
}
@@ -208,8 +234,10 @@
dialog.requireViewById<View>(android.R.id.button2).performClick()
}
+ private fun getStartButton() = dialog.requireViewById<TextView>(android.R.id.button1)
+
private fun clickOnStart() {
- dialog.requireViewById<View>(android.R.id.button1).performClick()
+ getStartButton().performClick()
}
private fun onSpinnerItemSelected(position: Int) {
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 5de31d8..cb5c739 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -34,6 +34,7 @@
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BACK_GESTURE
+import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
import com.android.systemui.ambient.touch.TouchMonitor
@@ -57,9 +58,11 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.controller.keyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -134,7 +137,9 @@
ambientTouchComponentFactory,
communalContent,
kosmos.sceneDataSourceDelegator,
- kosmos.notificationStackScrollLayoutController
+ kosmos.notificationStackScrollLayoutController,
+ kosmos.keyguardMediaController,
+ kosmos.lockscreenSmartspaceController
)
}
testableLooper = TestableLooper.get(this)
@@ -178,7 +183,9 @@
ambientTouchComponentFactory,
communalContent,
kosmos.sceneDataSourceDelegator,
- kosmos.notificationStackScrollLayoutController
+ kosmos.notificationStackScrollLayoutController,
+ kosmos.keyguardMediaController,
+ kosmos.lockscreenSmartspaceController
)
// First call succeeds.
@@ -205,6 +212,8 @@
communalContent,
kosmos.sceneDataSourceDelegator,
kosmos.notificationStackScrollLayoutController,
+ kosmos.keyguardMediaController,
+ kosmos.lockscreenSmartspaceController
)
assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
@@ -226,6 +235,8 @@
communalContent,
kosmos.sceneDataSourceDelegator,
kosmos.notificationStackScrollLayoutController,
+ kosmos.keyguardMediaController,
+ kosmos.lockscreenSmartspaceController
)
// Only initView without attaching a view as we don't want the flows to start collecting
@@ -425,7 +436,7 @@
}
@Test
- @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit() =
with(kosmos) {
testScope.runTest {
@@ -452,6 +463,27 @@
@Test
@DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+ @EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+ fun gestureExclusionZone_setAfterInit_fullSwipe() =
+ with(kosmos) {
+ testScope.runTest {
+ whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
+ goToScene(CommunalScenes.Communal)
+
+ assertThat(containerView.systemGestureExclusionRects)
+ .containsExactly(
+ Rect(
+ /* left= */ 0,
+ /* top= */ 0,
+ /* right= */ 0,
+ /* bottom= */ CONTAINER_HEIGHT
+ )
+ )
+ }
+ }
+
+ @Test
+ @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit_rtl() =
with(kosmos) {
testScope.runTest {
@@ -476,8 +508,29 @@
}
}
+ @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+ @EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+ fun gestureExclusionZone_setAfterInit_rtl_fullSwipe() =
+ with(kosmos) {
+ testScope.runTest {
+ whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
+ goToScene(CommunalScenes.Communal)
+
+ assertThat(containerView.systemGestureExclusionRects)
+ .containsExactly(
+ Rect(
+ /* left= */ 0,
+ /* top= */ 0,
+ /* right= */ CONTAINER_WIDTH,
+ /* bottom= */ CONTAINER_HEIGHT
+ )
+ )
+ }
+ }
+
@Test
@EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+ @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit_backGestureEnabled() =
with(kosmos) {
testScope.runTest {
@@ -503,7 +556,28 @@
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+ fun gestureExclusionZone_setAfterInit_backGestureEnabled_fullSwipe() =
+ with(kosmos) {
+ testScope.runTest {
+ whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
+ goToScene(CommunalScenes.Communal)
+
+ assertThat(containerView.systemGestureExclusionRects)
+ .containsExactly(
+ Rect(
+ /* left= */ 0,
+ /* top= */ 0,
+ /* right= */ FAKE_INSETS.right,
+ /* bottom= */ CONTAINER_HEIGHT
+ )
+ )
+ }
+ }
+
+ @Test
@EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+ @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit_backGestureEnabled_rtl() =
with(kosmos) {
testScope.runTest {
@@ -529,6 +603,28 @@
}
@Test
+ @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+ fun gestureExclusionZone_setAfterInit_backGestureEnabled_rtl_fullSwipe() =
+ with(kosmos) {
+ testScope.runTest {
+ whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
+ goToScene(CommunalScenes.Communal)
+
+ assertThat(containerView.systemGestureExclusionRects)
+ .containsExactly(
+ Rect(
+ Rect(
+ /* left= */ FAKE_INSETS.left,
+ /* top= */ 0,
+ /* right= */ CONTAINER_WIDTH,
+ /* bottom= */ CONTAINER_HEIGHT
+ )
+ )
+ )
+ }
+ }
+
+ @Test
fun gestureExclusionZone_unsetWhenShadeOpen() =
with(kosmos) {
testScope.runTest {
@@ -599,6 +695,30 @@
}
@Test
+ fun fullScreenSwipeGesture_doNotProcessTouchesInUmo() =
+ with(kosmos) {
+ testScope.runTest {
+ // Communal is closed.
+ goToScene(CommunalScenes.Blank)
+ whenever(keyguardMediaController.isWithinMediaViewBounds(any(), any()))
+ .thenReturn(true)
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ }
+ }
+
+ @Test
+ fun fullScreenSwipeGesture_doNotProcessTouchesInSmartspace() =
+ with(kosmos) {
+ testScope.runTest {
+ // Communal is closed.
+ goToScene(CommunalScenes.Blank)
+ whenever(lockscreenSmartspaceController.isWithinSmartspaceBounds(any(), any()))
+ .thenReturn(true)
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ }
+ }
+
+ @Test
fun onTouchEvent_hubOpen_touchesDispatched() =
with(kosmos) {
testScope.runTest {
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 e6afc1f..505f799 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -267,6 +267,7 @@
when(mPanelView.getParent()).thenReturn(mPanelViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
+ when(mQSFragment.getHeader()).thenReturn(mQsHeader);
doAnswer(invocation -> {
mLockscreenShadeTransitionCallback = invocation.getArgument(0);
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 e7db469..2e9d6e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
@@ -24,34 +24,41 @@
import static android.view.MotionEvent.BUTTON_SECONDARY;
import static android.view.MotionEvent.BUTTON_STYLUS_PRIMARY;
-import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR;
import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Rect;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
import android.view.MotionEvent;
+import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import java.util.List;
@@ -65,7 +72,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return progressionOf(FLAG_QS_UI_REFACTOR, FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT);
+ return progressionOf(FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT);
}
public QuickSettingsControllerImplTest(FlagsParameterization flags) {
@@ -244,6 +251,61 @@
}
@Test
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQsFragmentAttached_qsComposeFragmentDisabled_setHeaderInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController)
+ .setQsHeader((ViewGroup) mQSFragment.getHeader());
+ verify(mNotificationStackScrollLayoutController, never()).setQsHeaderBoundsProvider(any());
+ }
+
+ @Test
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQsFragmentAttached_qsComposeFragmentEnabled_setQsHeaderBoundsProviderInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController, never())
+ .setQsHeader((ViewGroup) mQSFragment.getHeader());
+ ArgumentCaptor<QSHeaderBoundsProvider> argumentCaptor =
+ ArgumentCaptor.forClass(QSHeaderBoundsProvider.class);
+
+ verify(mNotificationStackScrollLayoutController)
+ .setQsHeaderBoundsProvider(argumentCaptor.capture());
+
+ argumentCaptor.getValue().getLeftProvider().invoke();
+ argumentCaptor.getValue().getHeightProvider().invoke();
+ argumentCaptor.getValue().getBoundsOnScreenProvider().invoke(new Rect());
+ InOrder inOrderVerifier = inOrder(mQSFragment);
+
+ inOrderVerifier.verify(mQSFragment).getHeaderLeft();
+ inOrderVerifier.verify(mQSFragment).getHeaderHeight();
+ inOrderVerifier.verify(mQSFragment).getHeaderBoundsOnScreen(new Rect());
+ }
+
+ @Test
+ @DisableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQSFragmentDetached_qsComposeFragmentFlagDisabled_setViewToNullInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ mFragmentListener.onFragmentViewDestroyed(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController).setQsHeader(null);
+ verify(mNotificationStackScrollLayoutController, never()).setQsHeaderBoundsProvider(null);
+ }
+
+ @Test
+ @EnableFlags(QSComposeFragment.FLAG_NAME)
+ public void onQSFragmentDetached_qsComposeFragmentFlagEnabled_setBoundsProviderToNullInNSSL() {
+ mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
+
+ mFragmentListener.onFragmentViewDestroyed(QS.TAG, mQSFragment);
+
+ verify(mNotificationStackScrollLayoutController, never()).setQsHeader(null);
+ verify(mNotificationStackScrollLayoutController).setQsHeaderBoundsProvider(null);
+ }
+
+ @Test
public void onQsFragmentAttached_notFullWidth_setsFullWidthFalseOnQS() {
setIsFullWidth(false);
mFragmentListener.onFragmentViewCreated(QS.TAG, mQSFragment);
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 150f53d..022825a 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
@@ -22,8 +22,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.TRANSIT_CLOCK
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockMessageBuffers
@@ -76,7 +74,6 @@
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
private lateinit var registry: ClockRegistry
private lateinit var pickerConfig: ClockPickerConfig
- private val featureFlags = FakeFeatureFlags()
companion object {
private fun failFactory(clockId: ClockId): ClockController {
@@ -532,44 +529,4 @@
val actual = ClockSettings.serialize(ClockSettings("ID", null))
assertEquals(expected, actual)
}
-
- @Test
- fun testTransitClockEnabled_hasTransitClock() {
- testTransitClockFlag(true)
- }
-
- @Test
- fun testTransitClockDisabled_noTransitClock() {
- testTransitClockFlag(false)
- }
-
- private fun testTransitClockFlag(flag: Boolean) {
- featureFlags.set(TRANSIT_CLOCK, flag)
- registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
- val plugin = FakeClockPlugin()
- .addClock("clock_1")
- .addClock("DIGITAL_CLOCK_METRO")
- val lifecycle = FakeLifecycle("metro", plugin)
- pluginListener.onPluginLoaded(plugin, mockContext, lifecycle)
-
- val list = registry.getClocks()
- if (flag) {
- assertEquals(
- setOf(
- ClockMetadata(DEFAULT_CLOCK_ID),
- ClockMetadata("clock_1"),
- ClockMetadata("DIGITAL_CLOCK_METRO")
- ),
- list.toSet()
- )
- } else {
- assertEquals(
- setOf(
- ClockMetadata(DEFAULT_CLOCK_ID),
- ClockMetadata("clock_1")
- ),
- list.toSet()
- )
- }
- }
}
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 6916bbd..d10ea1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -15,7 +15,9 @@
package com.android.systemui.statusbar;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_ACTIVE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
@@ -194,9 +196,11 @@
@Test
public void testShowImeButton() {
- mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, 1, 2, true);
+ mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, IME_ACTIVE,
+ BACK_DISPOSITION_ADJUST_NOTHING, true);
waitForIdleSync();
- verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(true));
+ verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(IME_ACTIVE),
+ eq(BACK_DISPOSITION_ADJUST_NOTHING), eq(true));
}
@Test
@@ -204,11 +208,13 @@
// First show in default display to update the "last updated ime display"
testShowImeButton();
- mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, 1, 2, true);
+ mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, IME_ACTIVE,
+ BACK_DISPOSITION_ADJUST_NOTHING, true);
waitForIdleSync();
- verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(0),
+ verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(0) /* vis */,
eq(BACK_DISPOSITION_DEFAULT), eq(false));
- verify(mCallbacks).setImeWindowStatus(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(true));
+ verify(mCallbacks).setImeWindowStatus(eq(SECONDARY_DISPLAY), eq(IME_ACTIVE),
+ eq(BACK_DISPOSITION_ADJUST_NOTHING), eq(true));
}
@Test
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 25314f3..5b45781 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
@@ -20,6 +20,8 @@
import android.app.StatusBarManager.WINDOW_STATE_HIDING
import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.InputDevice
import android.view.LayoutInflater
import android.view.MotionEvent
@@ -104,7 +106,7 @@
val parent = FrameLayout(mContext) // add parent to keep layout params
view =
LayoutInflater.from(mContext).inflate(R.layout.status_bar, parent, false)
- as PhoneStatusBarView
+ as PhoneStatusBarView
controller = createAndInitController(view)
}
}
@@ -112,8 +114,8 @@
@Test
fun onViewAttachedAndDrawn_startListeningConfigurationControllerCallback() {
val view = createViewMock()
- val argumentCaptor = ArgumentCaptor.forClass(
- ConfigurationController.ConfigurationListener::class.java)
+ val argumentCaptor =
+ ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
controller = createAndInitController(view)
}
@@ -159,7 +161,7 @@
fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false)
val returnVal =
- view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+ view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0))
assertThat(returnVal).isFalse()
verify(shadeViewController, never()).handleExternalTouch(any())
}
@@ -169,7 +171,7 @@
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(false)
val returnVal =
- view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+ view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0))
assertThat(returnVal).isTrue()
verify(shadeViewController, never()).handleExternalTouch(any())
}
@@ -178,7 +180,7 @@
fun handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
`when`(shadeViewController.isViewEnabled).thenReturn(false)
- val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 2f, 0)
view.onTouchEvent(event)
@@ -208,6 +210,50 @@
}
@Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOff_viewReturnsFalse() {
+ `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ val returnVal = view.onInterceptTouchEvent(event)
+
+ assertThat(returnVal).isFalse()
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOn_viewReturnsFalse() {
+ `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ val returnVal = view.onInterceptTouchEvent(event)
+
+ assertThat(returnVal).isFalse()
+ }
+
+ @Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun handleInterceptTouchEventFromStatusBar_shadeReturnsTrue_flagOff_viewReturnsFalse() {
+ `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(true)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ val returnVal = view.onInterceptTouchEvent(event)
+
+ assertThat(returnVal).isFalse()
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun handleInterceptTouchEventFromStatusBar_shadeReturnsTrue_flagOn_viewReturnsTrue() {
+ `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(true)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+ val returnVal = view.onInterceptTouchEvent(event)
+
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
fun onTouch_windowHidden_centralSurfacesNotNotified() {
val callback = getCommandQueueCallback()
callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN)
@@ -244,9 +290,7 @@
controller = createAndInitController(view)
}
val statusContainer = view.requireViewById<View>(R.id.system_icons)
- statusContainer.dispatchTouchEvent(
- getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
- )
+ statusContainer.dispatchTouchEvent(getActionUpEventFromSource(InputDevice.SOURCE_MOUSE))
verify(shadeControllerImpl).animateExpandShade()
}
@@ -257,9 +301,10 @@
controller = createAndInitController(view)
}
val statusContainer = view.requireViewById<View>(R.id.system_icons)
- val handled = statusContainer.dispatchTouchEvent(
- getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
- )
+ val handled =
+ statusContainer.dispatchTouchEvent(
+ getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
+ )
assertThat(handled).isFalse()
}
@@ -295,21 +340,21 @@
private fun createAndInitController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
- Optional.of(sysuiUnfoldComponent),
- Optional.of(progressProvider),
- featureFlags,
- userChipViewModel,
- centralSurfacesImpl,
- statusBarWindowStateController,
- shadeControllerImpl,
- shadeViewController,
- panelExpansionInteractor,
- windowRootView,
- shadeLogger,
- viewUtil,
- configurationController,
- mStatusOverlayHoverListenerFactory
- )
+ Optional.of(sysuiUnfoldComponent),
+ Optional.of(progressProvider),
+ featureFlags,
+ userChipViewModel,
+ centralSurfacesImpl,
+ statusBarWindowStateController,
+ shadeControllerImpl,
+ shadeViewController,
+ panelExpansionInteractor,
+ windowRootView,
+ shadeLogger,
+ viewUtil,
+ configurationController,
+ mStatusOverlayHoverListenerFactory
+ )
.create(view)
.also { it.init() }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index eae4f23..abc50bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -19,6 +19,8 @@
import android.content.res.Configuration
import android.graphics.Insets
import android.graphics.Rect
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import android.view.DisplayCutout
import android.view.DisplayShape
@@ -30,6 +32,7 @@
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP
import com.android.systemui.Gefingerpoken
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.DarkIconDispatcher
@@ -82,7 +85,8 @@
}
@Test
- fun onInterceptTouchEvent_listenerNotified() {
+ @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun onInterceptTouchEvent_flagOff_listenerNotified() {
val handler = TestTouchEventHandler()
view.setTouchEventHandler(handler)
@@ -93,6 +97,66 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun onInterceptTouchEvent_flagOn_listenerNotified() {
+ val handler = TestTouchEventHandler()
+ view.setTouchEventHandler(handler)
+
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ view.onInterceptTouchEvent(event)
+
+ assertThat(handler.lastInterceptEvent).isEqualTo(event)
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun onInterceptTouchEvent_listenerReturnsFalse_flagOff_viewReturnsFalse() {
+ val handler = TestTouchEventHandler()
+ view.setTouchEventHandler(handler)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+ handler.handleTouchReturnValue = false
+
+ assertThat(view.onInterceptTouchEvent(event)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun onInterceptTouchEvent_listenerReturnsFalse_flagOn_viewReturnsFalse() {
+ val handler = TestTouchEventHandler()
+ view.setTouchEventHandler(handler)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+ handler.handleTouchReturnValue = false
+
+ assertThat(view.onInterceptTouchEvent(event)).isFalse()
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun onInterceptTouchEvent_listenerReturnsTrue_flagOff_viewReturnsFalse() {
+ val handler = TestTouchEventHandler()
+ view.setTouchEventHandler(handler)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+ handler.handleTouchReturnValue = true
+
+ assertThat(view.onInterceptTouchEvent(event)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+ fun onInterceptTouchEvent_listenerReturnsTrue_flagOn_viewReturnsTrue() {
+ val handler = TestTouchEventHandler()
+ view.setTouchEventHandler(handler)
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+ handler.handleTouchReturnValue = true
+
+ assertThat(view.onInterceptTouchEvent(event)).isTrue()
+ }
+
+ @Test
fun onTouchEvent_listenerReturnsTrue_viewReturnsTrue() {
val handler = TestTouchEventHandler()
view.setTouchEventHandler(handler)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index af5e60e..9b61105 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -1068,7 +1068,7 @@
public void testShowBouncerOrKeyguard_needsFullScreen() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
verify(mCentralSurfaces).hideKeyguard();
verify(mPrimaryBouncerInteractor).show(true);
}
@@ -1084,7 +1084,7 @@
.thenReturn(KeyguardState.LOCKSCREEN);
reset(mCentralSurfaces);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
verify(mPrimaryBouncerInteractor).show(true);
verify(mCentralSurfaces).showKeyguard();
}
@@ -1092,11 +1092,26 @@
@Test
@DisableSceneContainer
public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing() {
+ boolean isFalsingReset = false;
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset);
verify(mCentralSurfaces, never()).hideKeyguard();
+ verify(mPrimaryBouncerInteractor).show(true);
+ }
+
+ @Test
+ @DisableSceneContainer
+ public void testShowBouncerOrKeyguard_needsFullScreen_bouncerAlreadyShowing_onFalsing() {
+ boolean isFalsingReset = true;
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.SimPin);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset);
+ verify(mCentralSurfaces, never()).hideKeyguard();
+
+ // Do not refresh the full screen bouncer if the call is from falsing
verify(mPrimaryBouncerInteractor, never()).show(true);
}
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 8fd0b31..171520f 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
@@ -21,6 +21,7 @@
import android.content.Intent
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN
@@ -59,6 +60,7 @@
import android.telephony.TelephonyManager.ERI_OFF
import android.telephony.TelephonyManager.ERI_ON
import android.telephony.TelephonyManager.EXTRA_CARRIER_ID
+import android.telephony.TelephonyManager.EXTRA_DATA_SPN
import android.telephony.TelephonyManager.EXTRA_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
@@ -69,6 +71,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
@@ -85,7 +88,6 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
@@ -93,8 +95,6 @@
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -112,6 +112,8 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@@ -807,6 +809,7 @@
}
@Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_usesBroadcastInfo_returnsDerived() =
testScope.runTest {
var latest: NetworkNameModel? = null
@@ -815,14 +818,34 @@
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ // spnIntent() sets all values to true and test strings
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
job.cancel()
}
@Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_usesBroadcastInfo_returnsDerived_flagOff() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
+
+ // spnIntent() sets all values to true and test strings
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+ job.cancel()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_broadcastNotForThisSubId_keepsOldValue() =
testScope.runTest {
var latest: NetworkNameModel? = null
@@ -831,22 +854,48 @@
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
// WHEN an intent with a different subId is sent
val wrongSubIntent = spnIntent(subId = 101)
- captor.value!!.onReceive(context, wrongSubIntent)
+ captor.lastValue.onReceive(context, wrongSubIntent)
// THEN the previous intent's name is still used
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
job.cancel()
}
@Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_broadcastNotForThisSubId_keepsOldValue_flagOff() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
+
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+ // WHEN an intent with a different subId is sent
+ val wrongSubIntent = spnIntent(subId = 101)
+
+ captor.lastValue.onReceive(context, wrongSubIntent)
+
+ // THEN the previous intent's name is still used
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+ job.cancel()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_broadcastHasNoData_updatesToDefault() =
testScope.runTest {
var latest: NetworkNameModel? = null
@@ -855,9 +904,9 @@
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
val intentWithoutInfo =
spnIntent(
@@ -865,7 +914,7 @@
showPlmn = false,
)
- captor.value!!.onReceive(context, intentWithoutInfo)
+ captor.lastValue.onReceive(context, intentWithoutInfo)
assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
@@ -873,6 +922,34 @@
}
@Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_broadcastHasNoData_updatesToDefault_flagOff() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
+
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+ val intentWithoutInfo =
+ spnIntent(
+ showSpn = false,
+ showPlmn = false,
+ )
+
+ captor.lastValue.onReceive(context, intentWithoutInfo)
+
+ assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+
+ job.cancel()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers() =
testScope.runTest {
// Use the [StateFlow.value] getter so we can prove that the collection happens
@@ -884,10 +961,172 @@
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
// The value is still there despite no active subscribers
- assertThat(underTest.networkName.value).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(underTest.networkName.value)
+ .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers_flagOff() =
+ testScope.runTest {
+ // Use the [StateFlow.value] getter so we can prove that the collection happens
+ // even when there is no [Job]
+
+ // Starts out default
+ assertThat(underTest.networkName.value).isEqualTo(DEFAULT_NAME_MODEL)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
+
+ // The value is still there despite no active subscribers
+ assertThat(underTest.networkName.value)
+ .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_allFieldsSet_doesNotUseDataSpn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_allFieldsSet_flagOff() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNotNull_showSpn_spnNull_dataSpnNotNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = null,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNotNull_showSpn_spnNull_dataSpnNotNull_flagOff() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = null,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ fun networkName_showPlmn_noShowSPN() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = false,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$SPN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn_flagOff() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$DATA_SPN"))
}
@Test
@@ -1128,14 +1367,16 @@
private fun spnIntent(
subId: Int = SUB_1_ID,
showSpn: Boolean = true,
- spn: String = SPN,
+ spn: String? = SPN,
+ dataSpn: String? = DATA_SPN,
showPlmn: Boolean = true,
- plmn: String = PLMN,
+ plmn: String? = PLMN,
): Intent =
Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
putExtra(EXTRA_SHOW_SPN, showSpn)
putExtra(EXTRA_SPN, spn)
+ putExtra(EXTRA_DATA_SPN, dataSpn)
putExtra(EXTRA_SHOW_PLMN, showPlmn)
putExtra(EXTRA_PLMN, plmn)
}
@@ -1148,6 +1389,7 @@
private const val SEP = "-"
private const val SPN = "testSpn"
+ private const val DATA_SPN = "testDataSpn"
private const val PLMN = "testPlmn"
}
}
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 bb6ba46..54df9e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -25,12 +25,13 @@
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -77,7 +78,7 @@
@Mock
private lateinit var dumpManager: DumpManager
@Mock
- private lateinit var windowManager: WindowManager
+ private lateinit var windowManager: ViewCaptureAwareWindowManager
@Mock
private lateinit var powerManager: PowerManager
@@ -1142,7 +1143,7 @@
inner class TestController(
context: Context,
logger: TemporaryViewLogger<ViewInfo>,
- windowManager: WindowManager,
+ viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -1154,7 +1155,7 @@
) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger<ViewInfo>>(
context,
logger,
- windowManager,
+ viewCaptureAwareWindowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 664f2df..4260b65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -30,6 +30,8 @@
import androidx.core.animation.doOnCancel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
@@ -84,6 +86,7 @@
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler
+ @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var chipbarAnimator: TestChipbarAnimator
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
@@ -112,7 +115,8 @@
ChipbarCoordinator(
context,
logger,
- windowManager,
+ ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
+ isViewCaptureEnabled = false),
fakeExecutor,
accessibilityManager,
configurationController,
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/graphics/ImageLoaderKosmos.kt
similarity index 69%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/graphics/ImageLoaderKosmos.kt
index 460de8c..6b8919d 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/graphics/ImageLoaderKosmos.kt
@@ -14,18 +14,10 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.systemui.graphics
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import android.content.testableContext
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
-}
+var Kosmos.imageLoader by Kosmos.Fixture { ImageLoader(testableContext, testDispatcher) }
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
index 2919d3f..1e95fc1 100644
--- 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
@@ -17,7 +17,6 @@
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
@@ -25,7 +24,6 @@
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.dismissCallbackRegistry
import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
@@ -50,10 +48,10 @@
alternateBouncerDependencies = { alternateBouncerDependencies },
windowManager = { windowManager },
layoutInflater = { mockedLayoutInflater },
- dismissCallbackRegistry = dismissCallbackRegistry,
)
}
+@ExperimentalCoroutinesApi
private val Kosmos.alternateBouncerDependencies by
Kosmos.Fixture {
AlternateBouncerDependencies(
@@ -69,6 +67,7 @@
)
}
+@ExperimentalCoroutinesApi
private val Kosmos.alternateBouncerUdfpsIconViewModel by
Kosmos.Fixture {
AlternateBouncerUdfpsIconViewModel(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
index bdd4afa..2958315 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
@@ -18,6 +18,8 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,5 +30,7 @@
AlternateBouncerViewModel(
statusBarKeyguardViewManager = statusBarKeyguardViewManager,
keyguardTransitionInteractor = keyguardTransitionInteractor,
+ dismissCallbackRegistry = dismissCallbackRegistry,
+ alternateBouncerInteractor = { alternateBouncerInteractor },
)
}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt
similarity index 65%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt
index 460de8c..aed320c 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt
@@ -14,18 +14,10 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.systemui.media.controls.controller
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
+import com.android.systemui.util.mockito.mock
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
-}
+val Kosmos.keyguardMediaController by Kosmos.Fixture { mock<KeyguardMediaController>() }
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/airplane/AirplaneModeTileKosmos.kt
similarity index 64%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/airplane/AirplaneModeTileKosmos.kt
index 460de8c..73b1859 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/airplane/AirplaneModeTileKosmos.kt
@@ -14,18 +14,11 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.systemui.qs.tiles.impl.airplane
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.statusbar.connectivity.ConnectivityModule
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
-}
+val Kosmos.qsAirplaneModeTileConfig by
+ Kosmos.Fixture { ConnectivityModule.provideAirplaneModeTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index b6194e3..bbe753e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -27,7 +27,9 @@
class FakeQSSceneAdapter(
private val inflateDelegate: suspend (Context) -> View,
override val qqsHeight: Int = 0,
+ override val squishedQqsHeight: Int = 0,
override val qsHeight: Int = 0,
+ override val squishedQsHeight: Int = 0,
) : QSSceneAdapter {
private val _customizerState = MutableStateFlow<CustomizerState>(CustomizerState.Hidden)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 8e76a0b..53b6a2e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -18,6 +18,7 @@
import com.android.internal.logging.uiEventLogger
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
import com.android.systemui.classifier.falsingCollector
@@ -80,5 +81,6 @@
keyguardEnabledInteractor = keyguardEnabledInteractor,
dismissCallbackRegistry = dismissCallbackRegistry,
statusBarStateController = sysuiStatusBarStateController,
+ alternateBouncerInteractor = alternateBouncerInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneContentViewModelKosmos.kt
deleted file mode 100644
index 9240102..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneContentViewModelKosmos.kt
+++ /dev/null
@@ -1,34 +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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.shade.ui.viewmodel
-
-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.notifications.ui.viewmodel.NotificationsShadeSceneContentViewModel
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-val Kosmos.notificationsShadeSceneContentViewModel:
- NotificationsShadeSceneContentViewModel by Fixture {
- NotificationsShadeSceneContentViewModel(
- deviceEntryInteractor = deviceEntryInteractor,
- sceneInteractor = sceneInteractor,
- )
-}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
similarity index 71%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 460de8c..2a522ce 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -14,18 +14,10 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package com.android.systemui.statusbar.lockscreen
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
-}
+val Kosmos.lockscreenSmartspaceController by
+ Kosmos.Fixture { mock<LockscreenSmartspaceController>() }
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 20dc668..1542bb34 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
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.dump.dumpManager
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,7 +25,6 @@
val Kosmos.notificationsPlaceholderViewModel by Fixture {
NotificationsPlaceholderViewModel(
- dumpManager = dumpManager,
interactor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
headsUpNotificationInteractor = headsUpNotificationInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
index 2ba1211..0b438d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.shared.volumePanelLogger
val Kosmos.volumePanelGlobalStateRepository by
- Kosmos.Fixture { VolumePanelGlobalStateRepository(dumpManager) }
+ Kosmos.Fixture { VolumePanelGlobalStateRepository(dumpManager, volumePanelLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
index a18f498..3804a9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
@@ -28,6 +28,7 @@
import com.android.systemui.volume.panel.domain.defaultCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
import com.android.systemui.volume.panel.ui.composable.enabledComponents
+import com.android.systemui.volume.shared.volumePanelLogger
import javax.inject.Provider
var Kosmos.criteriaByKey: Map<VolumePanelComponentKey, Provider<ComponentAvailabilityCriteria>> by
@@ -50,6 +51,7 @@
enabledComponents,
{ defaultCriteria },
testScope.backgroundScope,
+ volumePanelLogger,
criteriaByKey,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
index 34a008f..c4fb9e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
@@ -18,17 +18,19 @@
import android.content.applicationContext
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory
import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
+import com.android.systemui.volume.shared.volumePanelLogger
var Kosmos.volumePanelStartables: Set<VolumePanelStartable> by Kosmos.Fixture { emptySet() }
var Kosmos.volumePanelViewModel: VolumePanelViewModel by
- Kosmos.Fixture { volumePanelViewModelFactory.create(testScope.backgroundScope) }
+ Kosmos.Fixture { volumePanelViewModelFactory.create(applicationCoroutineScope) }
val Kosmos.volumePanelViewModelFactory: VolumePanelViewModel.Factory by
Kosmos.Fixture {
@@ -37,6 +39,8 @@
volumePanelComponentFactory,
configurationController,
broadcastDispatcher,
+ dumpManager,
+ volumePanelLogger,
volumePanelGlobalStateInteractor,
)
}
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 68f185e..cc9b70e 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -281,6 +281,12 @@
com.android.server.LocalServices
+com.android.internal.graphics.cam.Cam
+com.android.internal.graphics.cam.CamUtils
+com.android.internal.graphics.cam.Frame
+com.android.internal.graphics.cam.HctSolver
+com.android.internal.graphics.ColorUtils
+
com.android.internal.util.BitUtils
com.android.internal.util.BitwiseInputStream
com.android.internal.util.BitwiseOutputStream
diff --git a/services/Android.bp b/services/Android.bp
index ded7379..0006455 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -120,6 +120,7 @@
":services.backup-sources",
":services.companion-sources",
":services.contentcapture-sources",
+ ":services.appfunctions-sources",
":services.contentsuggestions-sources",
":services.contextualsearch-sources",
":services.coverage-sources",
@@ -217,6 +218,7 @@
"services.autofill",
"services.backup",
"services.companion",
+ "services.appfunctions",
"services.contentcapture",
"services.contentsuggestions",
"services.contextualsearch",
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING
index 299d33f..38b4148 100644
--- a/services/accessibility/TEST_MAPPING
+++ b/services/accessibility/TEST_MAPPING
@@ -36,21 +36,7 @@
]
},
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.accessibilityservice"
- },
- {
- "include-filter": "android.view.accessibility"
- },
- {
- "include-filter": "com.android.internal.accessibility"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
+ "name": "FrameworksCoreTests_accessibility_NO_FLAKES"
}
],
"postsubmit": [
diff --git a/services/appfunctions/Android.bp b/services/appfunctions/Android.bp
new file mode 100644
index 0000000..f8ee823
--- /dev/null
+++ b/services/appfunctions/Android.bp
@@ -0,0 +1,25 @@
+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"],
+}
+
+filegroup {
+ name: "services.appfunctions-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.appfunctions",
+ defaults: ["platform_service_defaults"],
+ srcs: [
+ ":services.appfunctions-sources",
+ "java/**/*.logtags",
+ ],
+ libs: ["services.core"],
+}
diff --git a/services/appfunctions/OWNERS b/services/appfunctions/OWNERS
new file mode 100644
index 0000000..b310894
--- /dev/null
+++ b/services/appfunctions/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/appfunctions/OWNERS
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
new file mode 100644
index 0000000..f30e770
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+
+import android.app.appfunctions.IAppFunctionManager;
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/**
+ * Service that manages app functions.
+ */
+public class AppFunctionManagerService extends SystemService {
+
+ public AppFunctionManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ if (enableAppFunctionManager()) {
+ publishBinderService(Context.APP_FUNCTION_SERVICE, new AppFunctionManagerStub());
+ }
+ }
+
+ private static class AppFunctionManagerStub extends IAppFunctionManager.Stub {
+
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 668852b..cd2a535 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -16,10 +16,13 @@
package com.android.server.autofill;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+
import static com.android.server.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.assist.AssistStructure;
@@ -29,6 +32,7 @@
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.metrics.LogMaker;
+import android.os.UserHandle;
import android.os.UserManager;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
@@ -57,7 +61,6 @@
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
-
public final class Helper {
private static final String TAG = "AutofillHelper";
@@ -69,7 +72,7 @@
* {@code cmd autofill set log_level debug} or through
* {@link android.provider.Settings.Global#AUTOFILL_LOGGING_LEVEL}.
*/
- public static boolean sDebug = false;
+ public static boolean sDebug = true;
/**
* Defines a logging flag that can be dynamically changed at runtime using
@@ -104,6 +107,26 @@
}
/**
+ * Creates the context as the foreground user
+ *
+ * <p>Returns the current context as the current foreground user
+ */
+ @RequiresPermission(INTERACT_ACROSS_USERS)
+ public static Context getUserContext(Context context) {
+ int userId = ActivityManager.getCurrentUser();
+ Context c = context.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+ if (sDebug) {
+ Slog.d(
+ TAG,
+ "Current User: "
+ + userId
+ + ", context created as: "
+ + c.getContentResolver().getUserId());
+ }
+ return c;
+ }
+
+ /**
* Checks the URI permissions of the remote view,
* to see if the current userId is able to access it.
*
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b7508b4..6dea8b0 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -5084,11 +5084,17 @@
// Try to get the custom Icon, if one was passed through FillResponse
int iconResourceId = response.getIconResourceId();
if (iconResourceId != 0) {
- serviceIcon = mService.getMaster().getContext().getPackageManager()
- .getDrawable(
- mService.getServicePackageName(),
- iconResourceId,
- null);
+ long token = Binder.clearCallingIdentity();
+ try {
+ serviceIcon =
+ mService.getMaster()
+ .getContext()
+ .getPackageManager()
+ .getDrawable(
+ mService.getServicePackageName(), iconResourceId, null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
// Custom icon wasn't fetched, use the default package icon instead
@@ -5114,11 +5120,19 @@
// Try to get the custom Service name, if one was passed through FillResponse
int customServiceNameId = response.getServiceDisplayNameResourceId();
if (customServiceNameId != 0) {
- serviceLabel = mService.getMaster().getContext().getPackageManager()
- .getText(
- mService.getServicePackageName(),
- customServiceNameId,
- null);
+ long token = Binder.clearCallingIdentity();
+ try {
+ serviceLabel =
+ mService.getMaster()
+ .getContext()
+ .getPackageManager()
+ .getText(
+ mService.getServicePackageName(),
+ customServiceNameId,
+ null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
// Custom label wasn't fetched, use the default package name instead
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index fa414e3..5a71b89 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -95,6 +95,7 @@
private final ComponentName mComponentName;
private final int mThemeId;
private final @NonNull Context mContext;
+ private final @NonNull Context mUserContext;
private final @NonNull UiCallback mCallback;
private final @NonNull ListView mListView;
private final @Nullable ItemsAdapter mAdapter;
@@ -104,6 +105,8 @@
private @Nullable AnnounceFilterResult mAnnounceFilterResult;
private boolean mDestroyed;
+ // System has all permissions, see b/228957088
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
DialogFillUi(@NonNull Context context, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @Nullable String filterText,
@Nullable Drawable serviceIcon, @Nullable String servicePackageName,
@@ -117,6 +120,7 @@
mComponentName = componentName;
mContext = new ContextThemeWrapper(context, mThemeId);
+ mUserContext = Helper.getUserContext(mContext);
final LayoutInflater inflater = LayoutInflater.from(mContext);
final View decor = inflater.inflate(R.layout.autofill_fill_dialog, null);
@@ -224,7 +228,7 @@
};
final View content = presentation.applyWithTheme(
- mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+ mUserContext, (ViewGroup) decor, interceptionHandler, mThemeId);
container.addView(content);
container.setVisibility(View.VISIBLE);
}
@@ -263,7 +267,7 @@
return true;
};
final View content = presentation.applyWithTheme(
- mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+ mUserContext, (ViewGroup) decor, interceptionHandler, mThemeId);
container.addView(content);
container.setVisibility(View.VISIBLE);
container.setFocusable(true);
@@ -305,7 +309,7 @@
try {
if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
view = presentation.applyWithTheme(
- mContext, null, interceptionHandler, mThemeId);
+ mUserContext, null, interceptionHandler, mThemeId);
} catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e);
continue;
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 1bda70d..14a3211 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -107,6 +107,7 @@
new AutofillWindowPresenter();
private final @NonNull Context mContext;
+ private final @NonNull Context mUserContext;
private final @NonNull AnchoredWindow mWindow;
@@ -141,6 +142,8 @@
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
+ // System has all permissions, see b/228957088
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
FillUi(@NonNull Context context, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @Nullable String filterText,
@NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
@@ -153,6 +156,7 @@
mCallback = callback;
mFullScreen = isFullScreen(context);
mContext = new ContextThemeWrapper(context, mThemeId);
+ mUserContext = Helper.getUserContext(mContext);
mMaxInputLengthForAutofill = maxInputLengthForAutofill;
final LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -245,7 +249,7 @@
throw new RuntimeException("Permission error accessing RemoteView");
}
content = response.getPresentation().applyWithTheme(
- mContext, decor, interceptionHandler, mThemeId);
+ mUserContext, decor, interceptionHandler, mThemeId);
container.addView(content);
} catch (RuntimeException e) {
callback.onCanceled();
@@ -286,7 +290,7 @@
if (headerPresentation != null) {
interactionBlocker = newInteractionBlocker();
mHeader = headerPresentation.applyWithTheme(
- mContext, null, interactionBlocker, mThemeId);
+ mUserContext, null, interactionBlocker, mThemeId);
final LinearLayout headerContainer =
decor.findViewById(R.id.autofill_dataset_header);
applyCancelAction(mHeader, response.getCancelIds());
@@ -305,7 +309,7 @@
interactionBlocker = newInteractionBlocker();
}
mFooter = footerPresentation.applyWithTheme(
- mContext, null, interactionBlocker, mThemeId);
+ mUserContext, null, interactionBlocker, mThemeId);
applyCancelAction(mFooter, response.getCancelIds());
// Footer not supported on some platform e.g. TV
if (sVerbose) Slog.v(TAG, "adding footer");
@@ -334,7 +338,7 @@
try {
if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
view = presentation.applyWithTheme(
- mContext, null, interceptionHandler, mThemeId);
+ mUserContext, null, interceptionHandler, mThemeId);
} catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e);
continue;
@@ -812,6 +816,7 @@
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix); pw.print("mUserContext: "); pw.println(mUserContext);
pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId);
switch (mThemeId) {
case THEME_ID_DARK:
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 4d42f15..2ecce0b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -176,6 +176,8 @@
private boolean mDestroyed;
+ // System has all permissions, see b/228957088
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi,
@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
@Nullable String servicePackageName, @NonNull ComponentName componentName,
@@ -193,7 +195,7 @@
mComponentName = componentName;
mCompatMode = compatMode;
- context = new ContextThemeWrapper(context, mThemeId) {
+ context = new ContextThemeWrapper(Helper.getUserContext(context), mThemeId) {
@Override
public void startActivity(Intent intent) {
if (resolveActivity(intent) == null) {
@@ -235,6 +237,7 @@
return null;
}
};
+
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null);
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java
index b7f2b8d..191137a 100644
--- a/services/core/java/com/android/server/AlarmManagerInternal.java
+++ b/services/core/java/com/android/server/AlarmManagerInternal.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.UserIdInt;
import android.app.PendingIntent;
import com.android.server.SystemClockTime.TimeConfidence;
@@ -36,6 +37,16 @@
/** Returns true if AlarmManager is delaying alarms due to device idle. */
boolean isIdling();
+ /**
+ * Returns the time at which the next alarm for the given user is going to trigger, or 0 if
+ * there is none.
+ *
+ * <p>This value is UTC wall clock time in milliseconds, as returned by
+ * {@link System#currentTimeMillis()} for example.
+ * @see android.app.AlarmManager.AlarmClockInfo#getTriggerTime()
+ */
+ long getNextAlarmTriggerTimeForUser(@UserIdInt int userId);
+
public void removeAlarmsForUid(int uid);
public void registerInFlightListener(InFlightListener callback);
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index e2ab0d9..d80e40c 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -371,6 +371,10 @@
// exempt from ECM (i.e., they will never be considered "restricted").
private final ArraySet<SignedPackage> mEnhancedConfirmationTrustedInstallers = new ArraySet<>();
+ // A map of UIDs defined by OEMs, mapping from name to value. The UIDs will be registered at the
+ // start of the system which allows OEMs to create and register their system services.
+ @NonNull private final ArrayMap<String, Integer> mOemDefinedUids = new ArrayMap<>();
+
/**
* Map of system pre-defined, uniquely named actors; keys are namespace,
* value maps actor name to package name.
@@ -594,6 +598,10 @@
return mEnhancedConfirmationTrustedInstallers;
}
+ @NonNull
+ public ArrayMap<String, Integer> getOemDefinedUids() {
+ return mOemDefinedUids;
+ }
/**
* Only use for testing. Do NOT use in production code.
* @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1628,6 +1636,26 @@
}
}
} break;
+ case "oem-defined-uid": {
+ final String uidName = parser.getAttributeValue(null, "name");
+ final String uidValue = parser.getAttributeValue(null, "uid");
+ if (TextUtils.isEmpty(uidName)) {
+ Slog.w(TAG, "<" + name + "> without valid uid name in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else if (TextUtils.isEmpty(uidValue)) {
+ Slog.w(TAG, "<" + name + "> without valid uid value in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ try {
+ final int oemDefinedUid = Integer.parseInt(uidValue);
+ mOemDefinedUids.put(uidName, oemDefinedUid);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "<" + name + "> with invalid uid value: "
+ + uidValue + " in " + permFile
+ + " at " + parser.getPositionDescription());
+ }
+ }
+ } break;
case "enhanced-confirmation-trusted-package": {
if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
SignedPackage signedPackage = parseEnhancedConfirmationTrustedPackage(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2e1416b..d4f729c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -6962,7 +6962,8 @@
}
private boolean collectPackageServicesLocked(String packageName, Set<String> filterByClasses,
- boolean evenPersistent, boolean doit, ArrayMap<ComponentName, ServiceRecord> services) {
+ boolean evenPersistent, boolean doit, int minOomAdj,
+ ArrayMap<ComponentName, ServiceRecord> services) {
boolean didSomething = false;
for (int i = services.size() - 1; i >= 0; i--) {
ServiceRecord service = services.valueAt(i);
@@ -6970,6 +6971,11 @@
|| (service.packageName.equals(packageName)
&& (filterByClasses == null
|| filterByClasses.contains(service.name.getClassName())));
+ if (service.app != null && service.app.mState.getCurAdj() < minOomAdj) {
+ Slog.i(TAG, "Skip force stopping service " + service
+ + ": below minimum oom adj level");
+ continue;
+ }
if (sameComponent
&& (service.app == null || evenPersistent || !service.app.isPersistent())) {
if (!doit) {
@@ -6993,6 +6999,12 @@
boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses,
int userId, boolean evenPersistent, boolean fullStop, boolean doit) {
+ return bringDownDisabledPackageServicesLocked(packageName, filterByClasses, userId,
+ evenPersistent, fullStop, doit, ProcessList.INVALID_ADJ);
+ }
+
+ boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses,
+ int userId, boolean evenPersistent, boolean fullStop, boolean doit, int minOomAdj) {
boolean didSomething = false;
if (mTmpCollectionResults != null) {
@@ -7002,7 +7014,8 @@
if (userId == UserHandle.USER_ALL) {
for (int i = mServiceMap.size() - 1; i >= 0; i--) {
didSomething |= collectPackageServicesLocked(packageName, filterByClasses,
- evenPersistent, doit, mServiceMap.valueAt(i).mServicesByInstanceName);
+ evenPersistent, doit, minOomAdj,
+ mServiceMap.valueAt(i).mServicesByInstanceName);
if (!doit && didSomething) {
return true;
}
@@ -7015,7 +7028,7 @@
if (smap != null) {
ArrayMap<ComponentName, ServiceRecord> items = smap.mServicesByInstanceName;
didSomething = collectPackageServicesLocked(packageName, filterByClasses,
- evenPersistent, doit, items);
+ evenPersistent, doit, minOomAdj, items);
}
if (doit && filterByClasses == null) {
forceStopPackageLocked(packageName, userId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d8f337..4a18cb1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4377,6 +4377,16 @@
}
@GuardedBy("this")
+ final boolean forceStopUserPackagesLocked(int userId, String reasonString,
+ boolean evenImportantServices) {
+ int minOomAdj = evenImportantServices ? ProcessList.INVALID_ADJ
+ : ProcessList.FOREGROUND_APP_ADJ;
+ return forceStopPackageInternalLocked(null, -1, false, false,
+ true, false, false, false, userId, reasonString,
+ ApplicationExitInfo.REASON_USER_STOPPED, minOomAdj);
+ }
+
+ @GuardedBy("this")
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
@@ -4385,7 +4395,6 @@
: ApplicationExitInfo.REASON_USER_REQUESTED;
return forceStopPackageLocked(packageName, appId, callerWillRestart, purgeCache, doit,
evenPersistent, uninstalling, packageStateStopped, userId, reasonString, reason);
-
}
@GuardedBy("this")
@@ -4393,6 +4402,16 @@
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
int userId, String reasonString, int reason) {
+ return forceStopPackageInternalLocked(packageName, appId, callerWillRestart, purgeCache,
+ doit, evenPersistent, uninstalling, packageStateStopped, userId, reasonString,
+ reason, ProcessList.INVALID_ADJ);
+ }
+
+ @GuardedBy("this")
+ private boolean forceStopPackageInternalLocked(String packageName, int appId,
+ boolean callerWillRestart, boolean purgeCache, boolean doit,
+ boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
+ int userId, String reasonString, int reason, int minOomAdj) {
int i;
if (userId == UserHandle.USER_ALL && packageName == null) {
@@ -4431,7 +4450,7 @@
}
didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
- ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
+ minOomAdj, callerWillRestart, false /* allowRestart */, doit,
evenPersistent, true /* setRemoved */, uninstalling,
reason,
subReason,
@@ -4440,7 +4459,8 @@
}
if (mServices.bringDownDisabledPackageServicesLocked(
- packageName, null /* filterByClasses */, userId, evenPersistent, true, doit)) {
+ packageName, null /* filterByClasses */, userId, evenPersistent,
+ true, doit, minOomAdj)) {
if (!doit) {
return true;
}
@@ -19872,6 +19892,11 @@
}
@Override
+ public boolean isEarlyPackageKillEnabledForUserSwitch(int fromUserId, int toUserId) {
+ return mUserController.isEarlyPackageKillEnabledForUserSwitch(fromUserId, toUserId);
+ }
+
+ @Override
public void setStopUserOnSwitch(int value) {
ActivityManagerService.this.setStopUserOnSwitch(value);
}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 4a31fd1..4c87e1c 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -308,7 +308,7 @@
/**
* Cache the package name and information about if it's a system module.
*/
- @GuardedBy("mLock")
+ @GuardedBy("mSystemModulesCache")
private final HashMap<String, Boolean> mSystemModulesCache = new HashMap<>();
/**
@@ -1603,7 +1603,7 @@
if (moduleInfos == null) {
return;
}
- synchronized (mLock) {
+ synchronized (mSystemModulesCache) {
for (ModuleInfo info : moduleInfos) {
mSystemModulesCache.put(info.getPackageName(), Boolean.TRUE);
}
@@ -1611,7 +1611,7 @@
}
private boolean isSystemModule(String packageName) {
- synchronized (mLock) {
+ synchronized (mSystemModulesCache) {
final Boolean val = mSystemModulesCache.get(packageName);
if (val != null) {
return val.booleanValue();
@@ -1639,7 +1639,7 @@
}
}
// Update the cache.
- synchronized (mLock) {
+ synchronized (mSystemModulesCache) {
mSystemModulesCache.put(packageName, isSystemModule);
}
return isSystemModule;
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index bac5132..45d7206 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -79,11 +79,7 @@
},
{
"file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"],
- "name": "FrameworksCoreTests",
- "options": [
- { "include-filter": "com.android.internal.os.BatteryStatsTests" },
- { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
- ]
+ "name": "FrameworksCoreTests_battery_stats"
},
{
"file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"],
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 30efa3e..bdba6bc 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -131,6 +131,7 @@
import com.android.internal.util.ObjectUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.AlarmManagerInternal;
import com.android.server.FactoryResetter;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -247,6 +248,12 @@
private static final int USER_COMPLETED_EVENT_DELAY_MS = 5 * 1000;
/**
+ * If a user has an alarm in the next this many milliseconds, avoid stopping it due to
+ * scheduled background stopping.
+ */
+ private static final long TIME_BEFORE_USERS_ALARM_TO_AVOID_STOPPING_MS = 60 * 60_000; // 60 mins
+
+ /**
* Maximum number of users we allow to be running at a time, including system user.
*
* <p>This parameter only affects how many background users will be stopped when switching to a
@@ -439,6 +446,15 @@
@GuardedBy("mLock")
private final List<PendingUserStart> mPendingUserStarts = new ArrayList<>();
+ /**
+ * Contains users which cannot abort the shutdown process.
+ *
+ * <p> For example, we don't abort shutdown for users whose processes have already been stopped
+ * due to {@link #isEarlyPackageKillEnabledForUserSwitch(int, int)}.
+ */
+ @GuardedBy("mLock")
+ private final ArraySet<Integer> mDoNotAbortShutdownUserIds = new ArraySet<>();
+
private final UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() {
@Override
public void onUserCreated(UserInfo user, Object token) {
@@ -509,11 +525,11 @@
}
}
- private boolean shouldStopUserOnSwitch() {
+ private boolean isStopUserOnSwitchEnabled() {
synchronized (mLock) {
if (mStopUserOnSwitch != STOP_USER_ON_SWITCH_DEFAULT) {
final boolean value = mStopUserOnSwitch == STOP_USER_ON_SWITCH_TRUE;
- Slogf.i(TAG, "shouldStopUserOnSwitch(): returning overridden value (%b)", value);
+ Slogf.i(TAG, "isStopUserOnSwitchEnabled(): returning overridden value (%b)", value);
return value;
}
}
@@ -521,6 +537,26 @@
return property == -1 ? mDelayUserDataLocking : property == 1;
}
+ /**
+ * Get whether or not the previous user's packages will be killed before the user is
+ * stopped during a user switch.
+ *
+ * <p> The primary use case of this method is for {@link com.android.server.SystemService}
+ * classes to call this API in their
+ * {@link com.android.server.SystemService#onUserSwitching} method implementation to prevent
+ * restarting any of the previous user's processes that will be killed during the user switch.
+ */
+ boolean isEarlyPackageKillEnabledForUserSwitch(int fromUserId, int toUserId) {
+ // NOTE: The logic in this method could be extended to cover other cases where
+ // the previous user is also stopped like: guest users, ephemeral users,
+ // and users with DISALLOW_RUN_IN_BACKGROUND. Currently, this is not done
+ // because early killing is not enabled for these cases by default.
+ if (fromUserId == UserHandle.USER_SYSTEM) {
+ return false;
+ }
+ return isStopUserOnSwitchEnabled();
+ }
+
void finishUserSwitch(UserState uss) {
// This call holds the AM lock so we post to the handler.
mHandler.post(() -> {
@@ -1247,6 +1283,7 @@
return;
}
uss.setState(UserState.STATE_SHUTDOWN);
+ mDoNotAbortShutdownUserIds.remove(userId);
}
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("setUserState-STATE_SHUTDOWN-" + userId + "-[stopUser]");
@@ -1555,7 +1592,8 @@
private void stopPackagesOfStoppedUser(@UserIdInt int userId, String reason) {
if (DEBUG_MU) Slogf.i(TAG, "stopPackagesOfStoppedUser(%d): %s", userId, reason);
- mInjector.activityManagerForceStopPackage(userId, reason);
+ mInjector.activityManagerForceStopUserPackages(userId, reason,
+ /* evenImportantServices= */ true);
if (mInjector.getUserManager().isPreCreated(userId)) {
// Don't fire intent for precreated.
return;
@@ -1608,6 +1646,21 @@
}
}
+ private void stopPreviousUserPackagesIfEnabled(int fromUserId, int toUserId) {
+ if (!android.multiuser.Flags.stopPreviousUserApps()
+ || !isEarlyPackageKillEnabledForUserSwitch(fromUserId, toUserId)) {
+ return;
+ }
+ // Stop the previous user's packages early to reduce resource usage
+ // during user switching. Only do this when the previous user will
+ // be stopped regardless.
+ synchronized (mLock) {
+ mDoNotAbortShutdownUserIds.add(fromUserId);
+ }
+ mInjector.activityManagerForceStopUserPackages(fromUserId,
+ "early stop user packages", /* evenImportantServices= */ false);
+ }
+
void scheduleStartProfiles() {
// Parent user transition to RUNNING_UNLOCKING happens on FgThread, so it is busy, there is
// a chance the profile will reach RUNNING_LOCKED while parent is still locked, so no
@@ -1889,7 +1942,8 @@
updateStartedUserArrayLU();
needStart = true;
updateUmState = true;
- } else if (uss.state == UserState.STATE_SHUTDOWN) {
+ } else if (uss.state == UserState.STATE_SHUTDOWN
+ || mDoNotAbortShutdownUserIds.contains(userId)) {
Slogf.i(TAG, "User #" + userId
+ " is shutting down - will start after full shutdown");
mPendingUserStarts.add(new PendingUserStart(userId, userStartMode,
@@ -2293,7 +2347,7 @@
hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, oldUserId);
synchronized (mLock) {
// If running in background is disabled or mStopUserOnSwitch mode, stop the user.
- if (hasRestriction || shouldStopUserOnSwitch()) {
+ if (hasRestriction || isStopUserOnSwitchEnabled()) {
Slogf.i(TAG, "Stopping user %d and its profiles on user switch", oldUserId);
stopUsersLU(oldUserId, /* allowDelayedLocking= */ false, null, null);
return;
@@ -2371,6 +2425,12 @@
void processScheduledStopOfBackgroundUser(Integer userIdInteger) {
final int userId = userIdInteger;
Slogf.d(TAG, "Considering stopping background user %d due to inactivity", userId);
+
+ if (avoidStoppingUserDueToUpcomingAlarm(userId)) {
+ // We want this user running soon for alarm-purposes, so don't stop it now. Reschedule.
+ scheduleStopOfBackgroundUser(userId);
+ return;
+ }
synchronized (mLock) {
if (getCurrentOrTargetUserIdLU() == userId) {
return;
@@ -2390,6 +2450,18 @@
}
}
+ /**
+ * Returns whether we should avoid stopping the user now due to it having an alarm set to fire
+ * soon.
+ */
+ private boolean avoidStoppingUserDueToUpcomingAlarm(@UserIdInt int userId) {
+ final long alarmWallclockMs
+ = mInjector.getAlarmManagerInternal().getNextAlarmTriggerTimeForUser(userId);
+ return System.currentTimeMillis() < alarmWallclockMs
+ && (alarmWallclockMs
+ < System.currentTimeMillis() + TIME_BEFORE_USERS_ALARM_TO_AVOID_STOPPING_MS);
+ }
+
private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
t.traceBegin("timeoutUserSwitch-" + oldUserId + "-to-" + newUserId);
@@ -3425,7 +3497,7 @@
pw.println(" mLastActiveUsersForDelayedLocking:" + mLastActiveUsersForDelayedLocking);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
pw.println(" mAllowUserUnlocking:" + mAllowUserUnlocking);
- pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
+ pw.println(" isStopUserOnSwitchEnabled():" + isStopUserOnSwitchEnabled());
pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mBackgroundUserScheduledStopTimeSecs:"
@@ -3522,6 +3594,7 @@
Integer.toString(msg.arg1), msg.arg1);
mInjector.getSystemServiceManager().onUserSwitching(msg.arg2, msg.arg1);
+ stopPreviousUserPackagesIfEnabled(msg.arg2, msg.arg1);
scheduleOnUserCompletedEvent(msg.arg1,
UserCompletedEventType.EVENT_TYPE_USER_SWITCHING,
USER_COMPLETED_EVENT_DELAY_MS);
@@ -3860,6 +3933,10 @@
return mPowerManagerInternal;
}
+ AlarmManagerInternal getAlarmManagerInternal() {
+ return LocalServices.getService(AlarmManagerInternal.class);
+ }
+
KeyguardManager getKeyguardManager() {
return mService.mContext.getSystemService(KeyguardManager.class);
}
@@ -3896,10 +3973,10 @@
}.sendNext();
}
- void activityManagerForceStopPackage(@UserIdInt int userId, String reason) {
+ void activityManagerForceStopUserPackages(@UserIdInt int userId, String reason,
+ boolean evenImportantServices) {
synchronized (mService) {
- mService.forceStopPackageLocked(null, -1, false, false, true, false, false, false,
- userId, reason);
+ mService.forceStopUserPackagesLocked(userId, reason, evenImportantServices);
}
};
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 539dbca..2ce4623 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -1413,11 +1413,11 @@
pw.print("-");
pw.print(flagsToString(mOpFlag));
pw.print("] at ");
- date.setTime(discretizeTimeStamp(mNoteTime));
+ date.setTime(mNoteTime);
pw.print(sdf.format(date));
if (mNoteDuration != -1) {
pw.print(" for ");
- pw.print(discretizeDuration(mNoteDuration));
+ pw.print(mNoteDuration);
pw.print(" milliseconds ");
}
if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index df29ca4..fb8a81b 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -586,6 +586,8 @@
}
}
+ // LINT.IfChange
+
/**
* Checks if a client package is running in the background.
*
@@ -618,4 +620,6 @@
return true;
}
+ // LINT.ThenChange(frameworks/base/packages/SystemUI/shared/biometrics/src/com/android
+ // /systemui/biometrics/Utils.kt)
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 0fdd57d..dca1491 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -264,4 +264,11 @@
}
});
}
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
+ @Override
+ public int getSensorId() {
+ super.getSensorId_enforcePermission();
+ return mSensorId;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 8dc560b..caa2c1c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -293,4 +293,11 @@
}
});
}
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
+ @Override
+ public int getSensorId() {
+ super.getSensorId_enforcePermission();
+ return mSensorId;
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 3161b77..310f592 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -485,6 +485,7 @@
* @return true if the hour is valid
*/
private static boolean isValidHour(int value) {
+ value = bcdToDecimal(value);
return isWithinRange(value, 0, 23);
}
@@ -496,6 +497,7 @@
* @return true if the minute is valid
*/
private static boolean isValidMinute(int value) {
+ value = bcdToDecimal(value);
return isWithinRange(value, 0, 59);
}
@@ -507,10 +509,24 @@
* @return true if the duration hours is valid
*/
private static boolean isValidDurationHours(int value) {
+ value = bcdToDecimal(value);
return isWithinRange(value, 0, 99);
}
/**
+ * Convert BCD value to decimal value.
+ *
+ * @param value BCD value
+ * @return decimal value
+ */
+ private static int bcdToDecimal(int value) {
+ int tens = (value & 0xF0) >> 4;
+ int ones = (value & 0x0F);
+
+ return tens * 10 + ones;
+ }
+
+ /**
* Check if the given value is a valid recording sequence. A valid value is adheres to range
* description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
*
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e555761..a69c7ef 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -20,6 +20,8 @@
import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static com.android.hardware.input.Flags.touchpadVisualizer;
+
import android.Manifest;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
@@ -121,6 +123,7 @@
import com.android.server.Watchdog;
import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.input.debug.FocusEventDebugView;
+import com.android.server.input.debug.TouchpadDebugViewController;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -303,6 +306,9 @@
// Manages battery state for input devices.
private final BatteryController mBatteryController;
+ @Nullable
+ private final TouchpadDebugViewController mTouchpadDebugViewController;
+
// Manages Keyboard backlight
private final KeyboardBacklightControllerInterface mKeyboardBacklightController;
@@ -460,6 +466,9 @@
mSettingsObserver = new InputSettingsObserver(mContext, mHandler, this, mNative);
mKeyboardLayoutManager = new KeyboardLayoutManager(mContext, mNative, mDataStore,
injector.getLooper());
+ mTouchpadDebugViewController =
+ touchpadVisualizer() ? new TouchpadDebugViewController(mContext,
+ injector.getLooper()) : null;
mBatteryController = new BatteryController(mContext, mNative, injector.getLooper(),
injector.getUEventManager());
mKeyboardBacklightController = InputFeatureFlagProvider.isKeyboardBacklightControlEnabled()
@@ -589,6 +598,9 @@
mKeyRemapper.systemRunning();
mPointerIconCache.systemRunning();
mKeyboardGlyphManager.systemRunning();
+ if (mTouchpadDebugViewController != null) {
+ mTouchpadDebugViewController.systemRunning();
+ }
}
private void reloadDeviceAliases() {
diff --git a/services/core/java/com/android/server/input/OWNERS b/services/core/java/com/android/server/input/OWNERS
index 4c20c4d..e2834ec 100644
--- a/services/core/java/com/android/server/input/OWNERS
+++ b/services/core/java/com/android/server/input/OWNERS
@@ -1 +1,2 @@
+# Bug component: 136048
include /INPUT_OWNERS
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
new file mode 100644
index 0000000..5fca771
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input.debug;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.Gravity;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class TouchpadDebugView extends LinearLayout {
+
+ /**
+ * Input device ID for the touchpad that this debug view is displaying.
+ */
+ private final int mTouchpadId;
+
+ public TouchpadDebugView(Context context, int touchpadId) {
+ super(context);
+ mTouchpadId = touchpadId;
+ init(context);
+ }
+
+ private void init(Context context) {
+ setOrientation(VERTICAL);
+ setLayoutParams(new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ setBackgroundColor(Color.TRANSPARENT);
+
+ // TODO(b/286551975): Replace this content with the touchpad debug view.
+
+ TextView textView1 = new TextView(context);
+ textView1.setBackgroundColor(Color.parseColor("#FFFF0000"));
+ textView1.setTextSize(20);
+ textView1.setText("Touchpad Debug View 1");
+ textView1.setGravity(Gravity.CENTER);
+ textView1.setTextColor(Color.WHITE);
+
+ textView1.setLayoutParams(new LayoutParams(1000, 200));
+
+ TextView textView2 = new TextView(context);
+ textView2.setBackgroundColor(Color.BLUE);
+ textView2.setTextSize(20);
+ textView2.setText("Touchpad Debug View 2");
+ textView2.setGravity(Gravity.CENTER);
+ textView2.setTextColor(Color.WHITE);
+ textView2.setLayoutParams(new LayoutParams(1000, 200));
+
+ addView(textView1);
+ addView(textView2);
+ }
+
+ public int getTouchpadId() {
+ return mTouchpadId;
+ }
+}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
new file mode 100644
index 0000000..9c2aa36
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input.debug;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.WindowManager;
+
+import java.util.Objects;
+
+public class TouchpadDebugViewController {
+
+ private static final String TAG = "TouchpadDebugViewController";
+
+ private final Context mContext;
+ private final Handler mHandler;
+ @Nullable
+ private TouchpadDebugView mTouchpadDebugView;
+
+ public TouchpadDebugViewController(Context context, Looper looper) {
+ final DisplayManager displayManager = Objects.requireNonNull(
+ context.getSystemService(DisplayManager.class));
+ final Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ mContext = context.createDisplayContext(defaultDisplay);
+ mHandler = new Handler(looper);
+ }
+
+ public void systemRunning() {
+ final InputManager inputManager = Objects.requireNonNull(
+ mContext.getSystemService(InputManager.class));
+ inputManager.registerInputDeviceListener(mInputDeviceListener, mHandler);
+ for (int deviceId : inputManager.getInputDeviceIds()) {
+ mInputDeviceListener.onInputDeviceAdded(deviceId);
+ }
+ }
+
+ private final InputManager.InputDeviceListener mInputDeviceListener =
+ new InputManager.InputDeviceListener() {
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ final InputManager inputManager = Objects.requireNonNull(
+ mContext.getSystemService(InputManager.class));
+ InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+
+ if (Objects.requireNonNull(inputDevice).supportsSource(
+ InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)) {
+ showDebugView(deviceId);
+ }
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ hideDebugView(deviceId);
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ }
+ };
+
+ private void showDebugView(int touchpadId) {
+ if (mTouchpadDebugView != null) {
+ return;
+ }
+ final WindowManager wm = Objects.requireNonNull(
+ mContext.getSystemService(WindowManager.class));
+
+ mTouchpadDebugView = new TouchpadDebugView(mContext, touchpadId);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
+ lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setFitInsetsTypes(0);
+ lp.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ lp.format = PixelFormat.TRANSLUCENT;
+ lp.setTitle("TouchpadDebugView - display " + mContext.getDisplayId());
+ lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
+
+ lp.x = 40;
+ lp.y = 100;
+ lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.TOP | Gravity.LEFT;
+
+ wm.addView(mTouchpadDebugView, lp);
+ Slog.d(TAG, "Touchpad debug view created.");
+ }
+
+ private void hideDebugView(int touchpadId) {
+ if (mTouchpadDebugView == null || mTouchpadDebugView.getTouchpadId() != touchpadId) {
+ return;
+ }
+ final WindowManager wm = Objects.requireNonNull(
+ mContext.getSystemService(WindowManager.class));
+ wm.removeView(mTouchpadDebugView);
+ mTouchpadDebugView = null;
+ Slog.d(TAG, "Touchpad debug view removed.");
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 079b724..ec1993a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -18,6 +18,7 @@
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.INVALID_DISPLAY;
@@ -32,6 +33,8 @@
import android.content.ServiceConnection;
import android.content.pm.PackageManagerInternal;
import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
@@ -99,24 +102,16 @@
/**
* A set of status bits regarding the active IME.
*
- * <p>This value is a combination of following two bits:</p>
- * <dl>
- * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
- * <dd>
- * If this bit is ON, connected IME is ready to accept touch/key events.
- * </dd>
- * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
- * <dd>
- * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
- * </dd>
- * </dl>
- * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
- * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
+ * <em>Do not update this value outside of {@link #setImeWindowVis} and
+ * {@link InputMethodBindingController#unbindCurrentMethod}.</em>
*/
- @GuardedBy("ImfLock.class") private int mImeWindowVis;
-
+ @ImeWindowVisibility
@GuardedBy("ImfLock.class")
- private int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
+ private int mImeWindowVis;
+
+ @BackDispositionMode
+ @GuardedBy("ImfLock.class")
+ private int mBackDisposition = BACK_DISPOSITION_DEFAULT;
@Nullable private CountDownLatch mLatchForTesting;
@@ -718,22 +713,24 @@
}
@GuardedBy("ImfLock.class")
- void setImeWindowVis(int imeWindowVis) {
+ void setImeWindowVis(@ImeWindowVisibility int imeWindowVis) {
mImeWindowVis = imeWindowVis;
}
+ @ImeWindowVisibility
@GuardedBy("ImfLock.class")
int getImeWindowVis() {
return mImeWindowVis;
}
+ @BackDispositionMode
@GuardedBy("ImfLock.class")
int getBackDisposition() {
return mBackDisposition;
}
@GuardedBy("ImfLock.class")
- void setBackDisposition(int backDisposition) {
+ void setBackDisposition(@BackDispositionMode int backDisposition) {
mBackDisposition = backDisposition;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8afbd56..3654283 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -86,6 +86,8 @@
import android.content.res.Resources;
import android.hardware.input.InputManager;
import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.media.AudioManagerInternal;
import android.net.Uri;
import android.os.Binder;
@@ -2618,7 +2620,8 @@
}
@GuardedBy("ImfLock.class")
- private boolean shouldShowImeSwitcherLocked(int visibility, @UserIdInt int userId) {
+ private boolean shouldShowImeSwitcherLocked(@ImeWindowVisibility int visibility,
+ @UserIdInt int userId) {
if (!mShowOngoingImeSwitcherForPhones) return false;
// When the IME switcher dialog is shown, the IME switcher button should be hidden.
// TODO(b/305849394): Make mMenuController multi-user aware.
@@ -2722,8 +2725,8 @@
@BinderThread
@GuardedBy("ImfLock.class")
@SuppressWarnings("deprecation")
- private void setImeWindowStatusLocked(int vis, int backDisposition,
- @NonNull UserData userData) {
+ private void setImeWindowStatusLocked(@ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, @NonNull UserData userData) {
final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
final int userId = userData.mUserId;
@@ -2772,7 +2775,7 @@
final int userId = resolveImeUserIdFromDisplayIdLocked(displayId);
if (disableImeIcon) {
final var bindingController = getInputMethodBindingController(userId);
- updateSystemUiLocked(0, bindingController.getBackDisposition(), userId);
+ updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(), userId);
} else {
updateSystemUiLocked(userId);
}
@@ -2787,7 +2790,8 @@
}
@GuardedBy("ImfLock.class")
- private void updateSystemUiLocked(int vis, int backDisposition, @UserIdInt int userId) {
+ private void updateSystemUiLocked(@ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, @UserIdInt int userId) {
// To minimize app compat risk, ignore background users' request for single-user mode.
// TODO(b/357178609): generalize the logic and remove this special rule.
if (!mConcurrentMultiUserModeEnabled && userId != mCurrentImeUserId) {
@@ -6812,7 +6816,8 @@
@BinderThread
@Override
- public void setImeWindowStatusAsync(int vis, int backDisposition) {
+ public void setImeWindowStatusAsync(@ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition) {
synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(mToken, mUserData)) {
return;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 4dbbfa2..202543c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -512,6 +512,9 @@
/**
* Gets the next input method and subtype from the given ones.
*
+ * <p>If the given input method and subtype are not found, this returns the most recent
+ * input method and subtype.</p>
+ *
* @param imi the input method to find the next value from.
* @param subtype the input method subtype to find the next value from, if any.
* @param onlyCurrentIme whether to consider only subtypes of the current input method.
@@ -523,17 +526,20 @@
public ImeSubtypeListItem next(@NonNull InputMethodInfo imi,
@Nullable InputMethodSubtype subtype, boolean onlyCurrentIme,
boolean useRecency, boolean forward) {
- final int size = mItems.size();
- if (size <= 1) {
+ if (mItems.isEmpty()) {
return null;
}
final int index = getIndex(imi, subtype, useRecency);
if (index < 0) {
- return null;
+ Slog.w(TAG, "Trying to switch away from input method: " + imi
+ + " and subtype " + subtype + " which are not in the list,"
+ + " falling back to most recent item in list.");
+ return mItems.get(mRecencyMap[0]);
}
final int incrementSign = (forward ? 1 : -1);
+ final int size = mItems.size();
for (int i = 1; i < size; i++) {
final int nextIndex = (index + i * incrementSign + size) % size;
final int mappedIndex = useRecency ? mRecencyMap[nextIndex] : nextIndex;
@@ -554,7 +560,7 @@
*/
public boolean setMostRecent(@NonNull InputMethodInfo imi,
@Nullable InputMethodSubtype subtype) {
- if (mItems.size() <= 1) {
+ if (mItems.isEmpty()) {
return false;
}
@@ -608,247 +614,69 @@
}
}
- @VisibleForTesting
- public static class ControllerImpl {
-
- @NonNull
- private final DynamicRotationList mSwitchingAwareRotationList;
- @NonNull
- private final StaticRotationList mSwitchingUnawareRotationList;
- /** List of input methods and subtypes. */
- @Nullable
- private final RotationList mRotationList;
- /** List of input methods and subtypes suitable for hardware keyboards. */
- @Nullable
- private final RotationList mHardwareRotationList;
-
- /**
- * Whether there was a user action since the last input method and subtype switch.
- * Used to determine the switching behaviour for {@link #MODE_AUTO}.
- */
- private boolean mUserActionSinceSwitch;
-
- @NonNull
- public static ControllerImpl createFrom(@Nullable ControllerImpl currentInstance,
- @NonNull List<ImeSubtypeListItem> sortedEnabledItems,
- @NonNull List<ImeSubtypeListItem> hardwareKeyboardItems) {
- 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);
- }
-
- 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);
- }
-
- final RotationList rotationList;
- if (!Flags.imeSwitcherRevamp()) {
- rotationList = null;
- } else if (currentInstance != null && currentInstance.mRotationList != null
- && Objects.equals(
- currentInstance.mRotationList.mItems, sortedEnabledItems)) {
- // Can reuse the current instance.
- rotationList = currentInstance.mRotationList;
- } else {
- rotationList = new RotationList(sortedEnabledItems);
- }
-
- final RotationList hardwareRotationList;
- if (!Flags.imeSwitcherRevamp()) {
- hardwareRotationList = null;
- } else if (currentInstance != null && currentInstance.mHardwareRotationList != null
- && Objects.equals(
- currentInstance.mHardwareRotationList.mItems, hardwareKeyboardItems)) {
- // Can reuse the current instance.
- hardwareRotationList = currentInstance.mHardwareRotationList;
- } else {
- hardwareRotationList = new RotationList(hardwareKeyboardItems);
- }
-
- return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList,
- rotationList, hardwareRotationList);
- }
-
- private ControllerImpl(@NonNull DynamicRotationList switchingAwareRotationList,
- @NonNull StaticRotationList switchingUnawareRotationList,
- @Nullable RotationList rotationList,
- @Nullable RotationList hardwareRotationList) {
- mSwitchingAwareRotationList = switchingAwareRotationList;
- mSwitchingUnawareRotationList = switchingUnawareRotationList;
- mRotationList = rotationList;
- mHardwareRotationList = hardwareRotationList;
- }
-
- @Nullable
- public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme,
- @Nullable InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
- @SwitchMode int mode, boolean forward) {
- if (imi == null) {
- return null;
- }
- if (Flags.imeSwitcherRevamp() && mRotationList != null) {
- return mRotationList.next(imi, subtype, onlyCurrentIme,
- isRecency(mode, forward), forward);
- } else if (imi.supportsSwitchingToNextInputMethod()) {
- return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
- subtype);
- } else {
- return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
- subtype);
- }
- }
-
- @Nullable
- public ImeSubtypeListItem getNextInputMethodForHardware(boolean onlyCurrentIme,
- @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
- @SwitchMode int mode, boolean forward) {
- if (Flags.imeSwitcherRevamp() && mHardwareRotationList != null) {
- return mHardwareRotationList.next(imi, subtype, onlyCurrentIme,
- isRecency(mode, forward), forward);
- }
- return null;
- }
-
- /**
- * Called when the user took an action that should update the recency of the current
- * input method and subtype in the switching list.
- *
- * @param imi the currently selected input method.
- * @param subtype the currently selected input method subtype, if any.
- * @return {@code true} if the recency was updated, otherwise {@code false}.
- * @see android.inputmethodservice.InputMethodServiceInternal#notifyUserActionIfNecessary()
- */
- public boolean onUserActionLocked(@NonNull InputMethodInfo imi,
- @Nullable InputMethodSubtype subtype) {
- boolean recencyUpdated = false;
- if (Flags.imeSwitcherRevamp()) {
- if (mRotationList != null) {
- recencyUpdated |= mRotationList.setMostRecent(imi, subtype);
- }
- if (mHardwareRotationList != null) {
- recencyUpdated |= mHardwareRotationList.setMostRecent(imi, subtype);
- }
- if (recencyUpdated) {
- mUserActionSinceSwitch = true;
- }
- } else if (imi.supportsSwitchingToNextInputMethod()) {
- mSwitchingAwareRotationList.onUserAction(imi, subtype);
- }
- return recencyUpdated;
- }
-
- /** Called when the input method and subtype was changed. */
- public void onInputMethodSubtypeChanged() {
- mUserActionSinceSwitch = false;
- }
-
- /**
- * Whether the given mode and direction result in recency or static order.
- *
- * <p>{@link #MODE_AUTO} resolves to the recency order for the first forwards switch
- * after an {@link #onUserActionLocked user action}, and otherwise to the static order.</p>
- *
- * @param mode the switching mode.
- * @param forward the switching direction.
- * @return {@code true} for the recency order, otherwise {@code false}.
- */
- private boolean isRecency(@SwitchMode int mode, boolean forward) {
- if (mode == MODE_AUTO && mUserActionSinceSwitch && forward) {
- return true;
- } else {
- return mode == MODE_RECENT;
- }
- }
-
- @NonNull
- private static List<ImeSubtypeListItem> filterImeSubtypeList(
- @NonNull List<ImeSubtypeListItem> items,
- boolean supportsSwitchingToNextInputMethod) {
- final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
- final int numItems = items.size();
- for (int i = 0; i < numItems; i++) {
- final ImeSubtypeListItem item = items.get(i);
- if (item.mImi.supportsSwitchingToNextInputMethod()
- == supportsSwitchingToNextInputMethod) {
- result.add(item);
- }
- }
- return result;
- }
-
- protected void dump(@NonNull Printer pw, @NonNull String prefix) {
- pw.println(prefix + "mSwitchingAwareRotationList:");
- mSwitchingAwareRotationList.dump(pw, prefix + " ");
- pw.println(prefix + "mSwitchingUnawareRotationList:");
- mSwitchingUnawareRotationList.dump(pw, prefix + " ");
- if (Flags.imeSwitcherRevamp()) {
- if (mRotationList != null) {
- pw.println(prefix + "mRotationList:");
- mRotationList.dump(pw, prefix + " ");
- }
- if (mHardwareRotationList != null) {
- pw.println(prefix + "mHardwareRotationList:");
- mHardwareRotationList.dump(pw, prefix + " ");
- }
- pw.println(prefix + "User action since last switch: " + mUserActionSinceSwitch);
- }
- }
- }
-
@NonNull
- private ControllerImpl mController;
-
- InputMethodSubtypeSwitchingController() {
- mController = ControllerImpl.createFrom(null, Collections.emptyList(),
- Collections.emptyList());
- }
+ private DynamicRotationList mSwitchingAwareRotationList =
+ new DynamicRotationList(Collections.emptyList());
+ @NonNull
+ private StaticRotationList mSwitchingUnawareRotationList =
+ new StaticRotationList(Collections.emptyList());
+ /** List of input methods and subtypes. */
+ @NonNull
+ private RotationList mRotationList = new RotationList(Collections.emptyList());
+ /** List of input methods and subtypes suitable for hardware keyboards. */
+ @NonNull
+ private RotationList mHardwareRotationList = new RotationList(Collections.emptyList());
/**
- * Called when the user took an action that should update the recency of the current
- * input method and subtype in the switching list.
- *
- * @param imi the currently selected input method.
- * @param subtype the currently selected input method subtype, if any.
- * @see android.inputmethodservice.InputMethodServiceInternal#notifyUserActionIfNecessary()
+ * Whether there was a user action since the last input method and subtype switch.
+ * Used to determine the switching behaviour for {@link #MODE_AUTO}.
*/
- public void onUserActionLocked(@NonNull InputMethodInfo imi,
- @Nullable InputMethodSubtype subtype) {
- mController.onUserActionLocked(imi, subtype);
- }
+ private boolean mUserActionSinceSwitch;
- /** Called when the input method and subtype was changed. */
- public void onInputMethodSubtypeChanged() {
- mController.onInputMethodSubtypeChanged();
- }
+ /**
+ * Updates the list of input methods and subtypes used for switching. If the given items are
+ * equal to the existing ones (regardless of recency order), the update is skipped and the
+ * current recency order is kept. Otherwise, the recency order is reset.
+ *
+ * @param sortedEnabledItems the sorted list of enabled input methods and subtypes.
+ * @param hardwareKeyboardItems the unsorted list of enabled input method and subtypes
+ * suitable for hardware keyboards.
+ */
+ @VisibleForTesting
+ void update(@NonNull List<ImeSubtypeListItem> sortedEnabledItems,
+ @NonNull List<ImeSubtypeListItem> hardwareKeyboardItems) {
+ final var switchingAwareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
+ true /* supportsSwitchingToNextInputMethod */);
+ final var switchingUnawareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
+ false /* supportsSwitchingToNextInputMethod */);
- public void resetCircularListLocked(@NonNull Context context,
- @NonNull InputMethodSettings settings) {
- mController = ControllerImpl.createFrom(mController,
- getSortedInputMethodAndSubtypeList(
- false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
- false /* forImeMenu */, context, settings),
- getInputMethodAndSubtypeListForHardwareKeyboard(context, settings));
+ if (!Objects.equals(mSwitchingAwareRotationList.mImeSubtypeList,
+ switchingAwareImeSubtypes)) {
+ mSwitchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
+ }
+
+ if (!Objects.equals(mSwitchingUnawareRotationList.mImeSubtypeList,
+ switchingUnawareImeSubtypes)) {
+ mSwitchingUnawareRotationList = new StaticRotationList(switchingUnawareImeSubtypes);
+ }
+
+ if (Flags.imeSwitcherRevamp()
+ && !Objects.equals(mRotationList.mItems, sortedEnabledItems)) {
+ mRotationList = new RotationList(sortedEnabledItems);
+ }
+
+ if (Flags.imeSwitcherRevamp()
+ && !Objects.equals(mHardwareRotationList.mItems, hardwareKeyboardItems)) {
+ mHardwareRotationList = new RotationList(hardwareKeyboardItems);
+ }
}
/**
* Gets the next input method and subtype, starting from the given ones, in the given direction.
*
+ * <p>If the given input method and subtype are not found, this returns the most recent
+ * input method and subtype.</p>
+ *
* @param onlyCurrentIme whether to consider only subtypes of the current input method.
* @param imi the input method to find the next value from.
* @param subtype the input method subtype to find the next value from, if any.
@@ -860,13 +688,28 @@
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
@Nullable InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
@SwitchMode int mode, boolean forward) {
- return mController.getNextInputMethod(onlyCurrentIme, imi, subtype, mode, forward);
+ if (imi == null) {
+ return null;
+ }
+ if (Flags.imeSwitcherRevamp()) {
+ return mRotationList.next(imi, subtype, onlyCurrentIme,
+ isRecency(mode, forward), forward);
+ } else if (imi.supportsSwitchingToNextInputMethod()) {
+ return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ } else {
+ return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
+ subtype);
+ }
}
/**
* Gets the next input method and subtype suitable for hardware keyboards, starting from the
* given ones, in the given direction.
*
+ * <p>If the given input method and subtype are not found, this returns the most recent
+ * input method and subtype.</p>
+ *
* @param onlyCurrentIme whether to consider only subtypes of the current input method.
* @param imi the input method to find the next value from.
* @param subtype the input method subtype to find the next value from, if any.
@@ -878,11 +721,98 @@
public ImeSubtypeListItem getNextInputMethodForHardware(boolean onlyCurrentIme,
@NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
@SwitchMode int mode, boolean forward) {
- return mController.getNextInputMethodForHardware(onlyCurrentIme, imi, subtype, mode,
- forward);
+ if (Flags.imeSwitcherRevamp()) {
+ return mHardwareRotationList.next(imi, subtype, onlyCurrentIme,
+ isRecency(mode, forward), forward);
+ }
+ return null;
}
- public void dump(@NonNull Printer pw, @NonNull String prefix) {
- mController.dump(pw, prefix);
+ /**
+ * Called when the user took an action that should update the recency of the current
+ * input method and subtype in the switching list.
+ *
+ * @param imi the currently selected input method.
+ * @param subtype the currently selected input method subtype, if any.
+ * @return {@code true} if the recency was updated, otherwise {@code false}.
+ * @see android.inputmethodservice.InputMethodServiceInternal#notifyUserActionIfNecessary()
+ */
+ public boolean onUserActionLocked(@NonNull InputMethodInfo imi,
+ @Nullable InputMethodSubtype subtype) {
+ boolean recencyUpdated = false;
+ if (Flags.imeSwitcherRevamp()) {
+ recencyUpdated |= mRotationList.setMostRecent(imi, subtype);
+ recencyUpdated |= mHardwareRotationList.setMostRecent(imi, subtype);
+ if (recencyUpdated) {
+ mUserActionSinceSwitch = true;
+ }
+ } else if (imi.supportsSwitchingToNextInputMethod()) {
+ mSwitchingAwareRotationList.onUserAction(imi, subtype);
+ }
+ return recencyUpdated;
+ }
+
+ /** Called when the input method and subtype was changed. */
+ public void onInputMethodSubtypeChanged() {
+ mUserActionSinceSwitch = false;
+ }
+
+ /**
+ * Whether the given mode and direction result in recency or static order.
+ *
+ * <p>{@link #MODE_AUTO} resolves to the recency order for the first forwards switch
+ * after an {@link #onUserActionLocked user action}, and otherwise to the static order.</p>
+ *
+ * @param mode the switching mode.
+ * @param forward the switching direction.
+ * @return {@code true} for the recency order, otherwise {@code false}.
+ */
+ private boolean isRecency(@SwitchMode int mode, boolean forward) {
+ if (mode == MODE_AUTO && mUserActionSinceSwitch && forward) {
+ return true;
+ } else {
+ return mode == MODE_RECENT;
+ }
+ }
+
+ @NonNull
+ private static List<ImeSubtypeListItem> filterImeSubtypeList(
+ @NonNull List<ImeSubtypeListItem> items,
+ boolean supportsSwitchingToNextInputMethod) {
+ final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
+ final int numItems = items.size();
+ for (int i = 0; i < numItems; i++) {
+ final ImeSubtypeListItem item = items.get(i);
+ if (item.mImi.supportsSwitchingToNextInputMethod()
+ == supportsSwitchingToNextInputMethod) {
+ result.add(item);
+ }
+ }
+ return result;
+ }
+
+ void dump(@NonNull Printer pw, @NonNull String prefix) {
+ pw.println(prefix + "mSwitchingAwareRotationList:");
+ mSwitchingAwareRotationList.dump(pw, prefix + " ");
+ pw.println(prefix + "mSwitchingUnawareRotationList:");
+ mSwitchingUnawareRotationList.dump(pw, prefix + " ");
+ if (Flags.imeSwitcherRevamp()) {
+ pw.println(prefix + "mRotationList:");
+ mRotationList.dump(pw, prefix + " ");
+ pw.println(prefix + "mHardwareRotationList:");
+ mHardwareRotationList.dump(pw, prefix + " ");
+ pw.println(prefix + "User action since last switch: " + mUserActionSinceSwitch);
+ }
+ }
+
+ InputMethodSubtypeSwitchingController() {
+ }
+
+ public void resetCircularListLocked(@NonNull Context context,
+ @NonNull InputMethodSettings settings) {
+ update(getSortedInputMethodAndSubtypeList(
+ false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
+ false /* forImeMenu */, context, settings),
+ getInputMethodAndSubtypeListForHardwareKeyboard(context, settings));
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 17f6561..a6f4c0e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -530,6 +530,12 @@
*/
private boolean mUseDifferentDelaysForBackgroundChain;
+ /**
+ * Core uids and apps without the internet permission will not have any firewall rules applied
+ * to them.
+ */
+ private boolean mNeverApplyRulesToCoreUids;
+
// See main javadoc for instructions on how to use these locks.
final Object mUidRulesFirstLock = new Object();
final Object mNetworkPoliciesSecondLock = new Object();
@@ -760,7 +766,8 @@
/** List of apps indexed by uid and whether they have the internet permission */
@GuardedBy("mUidRulesFirstLock")
- private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
+ @VisibleForTesting
+ final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
/**
* Map of uid -> UidStateCallbackInfo objects holding the data received from
@@ -1038,6 +1045,7 @@
mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain();
+ mNeverApplyRulesToCoreUids = Flags.neverApplyRulesToCoreUids();
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
@@ -4088,6 +4096,8 @@
+ mUseMeteredFirewallChains);
fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": "
+ mUseDifferentDelaysForBackgroundChain);
+ fout.println(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS + ": "
+ + mNeverApplyRulesToCoreUids);
fout.println();
fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -4878,6 +4888,12 @@
int[] idleUids = mUsageStats.getIdleUidsForUser(user.id);
for (int uid : idleUids) {
if (!mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid), false)) {
+ if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+ // This check is needed to keep mUidFirewallStandbyRules free of any
+ // such uids. Doing this keeps it in sync with the actual rules applied
+ // in the underlying connectivity stack.
+ continue;
+ }
// quick check: if this uid doesn't have INTERNET permission, it
// doesn't have network access anyway, so it is a waste to mess
// with it here.
@@ -5180,6 +5196,11 @@
@GuardedBy("mUidRulesFirstLock")
private boolean isUidValidForAllowlistRulesUL(int uid) {
+ return isUidValidForRulesUL(uid);
+ }
+
+ @GuardedBy("mUidRulesFirstLock")
+ private boolean isUidValidForRulesUL(int uid) {
return UserHandle.isApp(uid) && hasInternetPermissionUL(uid);
}
@@ -6194,41 +6215,33 @@
}
}
- private void addSdkSandboxUidsIfNeeded(SparseIntArray uidRules) {
- final int size = uidRules.size();
- final SparseIntArray sdkSandboxUids = new SparseIntArray();
- for (int index = 0; index < size; index++) {
- final int uid = uidRules.keyAt(index);
- final int rule = uidRules.valueAt(index);
- if (Process.isApplicationUid(uid)) {
- sdkSandboxUids.put(Process.toSdkSandboxUid(uid), rule);
- }
- }
-
- for (int index = 0; index < sdkSandboxUids.size(); index++) {
- final int uid = sdkSandboxUids.keyAt(index);
- final int rule = sdkSandboxUids.valueAt(index);
- uidRules.put(uid, rule);
- }
- }
-
/**
* Set uid rules on a particular firewall chain. This is going to synchronize the rules given
* here to netd. It will clean up dead rules and make sure the target chain only contains rules
* specified here.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
- addSdkSandboxUidsIfNeeded(uidRules);
try {
int size = uidRules.size();
- int[] uids = new int[size];
- int[] rules = new int[size];
+ final IntArray uids = new IntArray(size);
+ final IntArray rules = new IntArray(size);
for(int index = size - 1; index >= 0; --index) {
- uids[index] = uidRules.keyAt(index);
- rules[index] = uidRules.valueAt(index);
+ final int uid = uidRules.keyAt(index);
+ if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+ continue;
+ }
+ uids.add(uid);
+ rules.add(uidRules.valueAt(index));
+ if (Process.isApplicationUid(uid)) {
+ uids.add(Process.toSdkSandboxUid(uid));
+ rules.add(uidRules.valueAt(index));
+ }
}
- mNetworkManager.setFirewallUidRules(chain, uids, rules);
- mLogger.firewallRulesChanged(chain, uids, rules);
+ final int[] uidArray = uids.toArray();
+ final int[] ruleArray = rules.toArray();
+ mNetworkManager.setFirewallUidRules(chain, uidArray, ruleArray);
+ mLogger.firewallRulesChanged(chain, uidArray, ruleArray);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
@@ -6241,6 +6254,9 @@
*/
@GuardedBy("mUidRulesFirstLock")
private void setUidFirewallRuleUL(int chain, int uid, int rule) {
+ if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+ return;
+ }
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
"setUidFirewallRuleUL: " + chain + "/" + uid + "/" + rule);
@@ -6249,8 +6265,6 @@
if (chain == FIREWALL_CHAIN_STANDBY) {
mUidFirewallStandbyRules.put(uid, rule);
}
- // Note that we do not need keep a separate cache of uid rules for chains that we do
- // not call #setUidFirewallRulesUL for.
try {
mNetworkManager.setFirewallUidRule(chain, uid, rule);
@@ -6295,6 +6309,8 @@
* Resets all firewall rules associated with an UID.
*/
private void resetUidFirewallRules(int uid) {
+ // Resetting rules for uids with isUidValidForRulesUL = false should be OK as no rules
+ // should be previously set and the downstream code will skip no-op changes.
try {
mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid,
FIREWALL_RULE_DEFAULT);
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index 586baf0..7f04e66 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -27,3 +27,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "never_apply_rules_to_core_uids"
+ namespace: "backstage_power"
+ description: "Removes all rule bookkeeping and evaluation logic for core uids and uids without the internet permission"
+ bug: "356956588"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index a44e553..66e61c0 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,9 +16,6 @@
package com.android.server.notification;
-import static android.service.notification.Condition.STATE_TRUE;
-import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
-
import android.app.INotificationManager;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -322,20 +319,7 @@
final Condition c = conditions[i];
final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
r.info = info;
- if (android.app.Flags.modesUi()) {
- // if user turned on the mode, ignore the update unless the app also wants the
- // mode on. this will update the origin of the mode and let the owner turn it
- // off when the context ends
- if (r.condition != null && r.condition.source == ORIGIN_USER_IN_SYSTEMUI) {
- if (r.condition.state == STATE_TRUE && c.state == STATE_TRUE) {
- r.condition = c;
- }
- } else {
- r.condition = c;
- }
- } else {
- r.condition = c;
- }
+ r.condition = c;
}
}
final int N = conditions.length;
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index b4459cb..9818916 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -1519,7 +1519,14 @@
@Override
public void setLastNotificationUpdateTimeMs(NotificationRecord record,
long timestampMillis) {
- super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+ if (Flags.politeNotificationsAttnUpdate()) {
+ // Set last update per package/channel only for exempt notifications
+ if (isAvalancheExempted(record)) {
+ super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+ }
+ } else {
+ super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+ }
mLastNotificationTimestamp = timestampMillis;
mAppStrategy.setLastNotificationUpdateTimeMs(record, timestampMillis);
}
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 268d835..d495ef5 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -82,7 +82,7 @@
for (ZenRule automaticRule : config.automaticRules.values()) {
if (automaticRule.component != null) {
evaluateRule(automaticRule, current, trigger, processSubscriptions, false);
- updateSnoozing(automaticRule);
+ automaticRule.reconsiderConditionOverride();
}
}
@@ -187,13 +187,4 @@
+ rule.conditionId);
}
}
-
- private boolean updateSnoozing(ZenRule rule) {
- if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
- rule.snoozing = false;
- if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
- return true;
- }
- return false;
- }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8c280ed..0f50260 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -37,6 +37,8 @@
import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_APP;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.Preconditions.checkArgument;
@@ -643,10 +645,10 @@
if ((rule.userModifiedFields & AutomaticZenRule.FIELD_INTERRUPTION_FILTER) == 0) {
rule.zenMode = zenMode;
}
- rule.snoozing = false;
rule.condition = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_activated),
STATE_TRUE);
+ rule.resetConditionOverride();
setConfigLocked(newConfig, /* triggeringComponent= */ null, ORIGIN_APP,
"applyGlobalZenModeAsImplicitZenRule", callingUid);
@@ -867,8 +869,8 @@
ZenRule deletedRule = ruleToRemove.copy();
deletedRule.deletionInstant = Instant.now(mClock);
// If the rule is restored it shouldn't be active (or snoozed).
- deletedRule.snoozing = false;
deletedRule.condition = null;
+ deletedRule.resetConditionOverride();
// Overwrites a previously-deleted rule with the same conditionId, but that's okay.
config.deletedRules.put(deletedKey, deletedRule);
}
@@ -885,7 +887,12 @@
if (rule == null || !canManageAutomaticZenRule(rule)) {
return Condition.STATE_UNKNOWN;
}
- return rule.condition != null ? rule.condition.state : STATE_FALSE;
+ if (Flags.modesApi() && Flags.modesUi()) {
+ return rule.isAutomaticActive() ? STATE_TRUE : STATE_FALSE;
+ } else {
+ // Buggy, does not consider snoozing!
+ return rule.condition != null ? rule.condition.state : STATE_FALSE;
+ }
}
}
@@ -943,12 +950,43 @@
}
for (ZenRule rule : rules) {
- rule.condition = condition;
- updateSnoozing(rule);
+ applyConditionAndReconsiderOverride(rule, condition, origin);
setConfigLocked(config, rule.component, origin, "conditionChanged", callingUid);
}
}
+ private static void applyConditionAndReconsiderOverride(ZenRule rule, Condition condition,
+ int origin) {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ if (origin == ORIGIN_USER_IN_SYSTEMUI && condition != null
+ && condition.source == SOURCE_USER_ACTION) {
+ // Apply as override, instead of actual condition.
+ // If the new override is the reverse of a previous (still active) override, try
+ // removing the previous override, as long as the resulting state, based on the
+ // previous owner-provided condition, is the desired one (active or inactive).
+ // This allows the rule owner to resume controlling the rule after
+ // snoozing-unsnoozing or activating-stopping.
+ if (condition.state == STATE_TRUE) {
+ rule.resetConditionOverride();
+ if (!rule.isAutomaticActive()) {
+ rule.setConditionOverride(OVERRIDE_ACTIVATE);
+ }
+ } else if (condition.state == STATE_FALSE) {
+ rule.resetConditionOverride();
+ if (rule.isAutomaticActive()) {
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
+ }
+ }
+ } else {
+ rule.condition = condition;
+ rule.reconsiderConditionOverride();
+ }
+ } else {
+ rule.condition = condition;
+ rule.reconsiderConditionOverride();
+ }
+ }
+
private static List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id,
Condition condition) {
List<ZenRule> matchingRules = new ArrayList<>();
@@ -971,15 +1009,6 @@
return true;
}
- private boolean updateSnoozing(ZenRule rule) {
- if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
- rule.snoozing = false;
- if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
- return true;
- }
- return false;
- }
-
public int getCurrentInstanceCount(ComponentName cn) {
if (cn == null) {
return 0;
@@ -1181,7 +1210,7 @@
if (rule.enabled != azr.isEnabled()) {
rule.enabled = azr.isEnabled();
- rule.snoozing = false;
+ rule.resetConditionOverride();
modified = true;
}
if (!Objects.equals(rule.configurationActivity, azr.getConfigurationActivity())) {
@@ -1271,7 +1300,7 @@
return modified;
} else {
if (rule.enabled != azr.isEnabled()) {
- rule.snoozing = false;
+ rule.resetConditionOverride();
}
rule.name = azr.getName();
rule.condition = null;
@@ -1573,18 +1602,16 @@
// For API calls (different origin) keep old behavior of snoozing all rules.
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
- automaticRule.snoozing = true;
+ automaticRule.setConditionOverride(OVERRIDE_DEACTIVATE);
}
}
}
} else {
if (zenMode == Global.ZEN_MODE_OFF) {
newConfig.manualRule = null;
- // User deactivation of DND means just turning off the manual DND rule.
- // For API calls (different origin) keep old behavior of snoozing all rules.
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
- automaticRule.snoozing = true;
+ automaticRule.setConditionOverride(OVERRIDE_DEACTIVATE);
}
}
@@ -1626,13 +1653,11 @@
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
synchronized (mConfigLock) {
- if (mConfig.manualRule != null) {
+ if (mConfig.isManualActive()) {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
for (ZenRule rule : mConfig.automaticRules.values()) {
- if (rule.enabled && rule.condition != null
- && rule.condition.state == STATE_TRUE
- && !rule.snoozing) {
+ if (rule.isAutomaticActive()) {
rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
}
@@ -1695,8 +1720,8 @@
for (ZenRule automaticRule : config.automaticRules.values()) {
if (forRestore) {
// don't restore transient state from restored automatic rules
- automaticRule.snoozing = false;
automaticRule.condition = null;
+ automaticRule.resetConditionOverride();
automaticRule.creationTime = time;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 46585a5..6303ecd 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -80,6 +80,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.content.PackageMonitor;
import com.android.internal.content.om.OverlayConfig;
@@ -1180,6 +1181,7 @@
// intent, querying the PackageManagerService for the actual current
// state may lead to contradictions within OMS. Better then to lag
// behind until all pending intents have been processed.
+ @GuardedBy("itself")
private final ArrayMap<String, PackageStateUsers> mCache = new ArrayMap<>();
private final ArraySet<Integer> mInitializedUsers = new ArraySet<>();
@@ -1207,10 +1209,12 @@
}
final ArrayMap<String, PackageState> userPackages = new ArrayMap<>();
- for (int i = 0, n = mCache.size(); i < n; i++) {
- final PackageStateUsers pkg = mCache.valueAt(i);
- if (pkg.mInstalledUsers.contains(userId)) {
- userPackages.put(mCache.keyAt(i), pkg.mPackageState);
+ synchronized (mCache) {
+ for (int i = 0, n = mCache.size(); i < n; i++) {
+ final PackageStateUsers pkg = mCache.valueAt(i);
+ if (pkg.mInstalledUsers.contains(userId)) {
+ userPackages.put(mCache.keyAt(i), pkg.mPackageState);
+ }
}
}
return userPackages;
@@ -1220,7 +1224,11 @@
@Nullable
public PackageState getPackageStateForUser(@NonNull final String packageName,
final int userId) {
- final PackageStateUsers pkg = mCache.get(packageName);
+ final PackageStateUsers pkg;
+
+ synchronized (mCache) {
+ pkg = mCache.get(packageName);
+ }
if (pkg != null && pkg.mInstalledUsers.contains(userId)) {
return pkg.mPackageState;
}
@@ -1251,12 +1259,15 @@
@NonNull
private PackageState addPackageUser(@NonNull final PackageState pkg,
final int user) {
- PackageStateUsers pkgUsers = mCache.get(pkg.getPackageName());
- if (pkgUsers == null) {
- pkgUsers = new PackageStateUsers(pkg);
- mCache.put(pkg.getPackageName(), pkgUsers);
- } else {
- pkgUsers.mPackageState = pkg;
+ PackageStateUsers pkgUsers;
+ synchronized (mCache) {
+ pkgUsers = mCache.get(pkg.getPackageName());
+ if (pkgUsers == null) {
+ pkgUsers = new PackageStateUsers(pkg);
+ mCache.put(pkg.getPackageName(), pkgUsers);
+ } else {
+ pkgUsers.mPackageState = pkg;
+ }
}
pkgUsers.mInstalledUsers.add(user);
return pkgUsers.mPackageState;
@@ -1265,18 +1276,24 @@
@NonNull
private void removePackageUser(@NonNull final String packageName, final int user) {
- final PackageStateUsers pkgUsers = mCache.get(packageName);
- if (pkgUsers == null) {
- return;
+ // synchronize should include the call to the other removePackageUser() method so that
+ // the access and modification happen under the same lock.
+ synchronized (mCache) {
+ final PackageStateUsers pkgUsers = mCache.get(packageName);
+ if (pkgUsers == null) {
+ return;
+ }
+ removePackageUser(pkgUsers, user);
}
- removePackageUser(pkgUsers, user);
}
@NonNull
private void removePackageUser(@NonNull final PackageStateUsers pkg, final int user) {
pkg.mInstalledUsers.remove(user);
if (pkg.mInstalledUsers.isEmpty()) {
- mCache.remove(pkg.mPackageState.getPackageName());
+ synchronized (mCache) {
+ mCache.remove(pkg.mPackageState.getPackageName());
+ }
}
}
@@ -1386,8 +1403,10 @@
public void forgetAllPackageInfos(final int userId) {
// Iterate in reverse order since removing the package in all users will remove the
// package from the cache.
- for (int i = mCache.size() - 1; i >= 0; i--) {
- removePackageUser(mCache.valueAt(i), userId);
+ synchronized (mCache) {
+ for (int i = mCache.size() - 1; i >= 0; i--) {
+ removePackageUser(mCache.valueAt(i), userId);
+ }
}
}
@@ -1405,22 +1424,23 @@
public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
pw.println("AndroidPackage cache");
+ synchronized (mCache) {
+ if (!dumpState.isVerbose()) {
+ pw.println(TAB1 + mCache.size() + " package(s)");
+ return;
+ }
- if (!dumpState.isVerbose()) {
- pw.println(TAB1 + mCache.size() + " package(s)");
- return;
- }
+ if (mCache.size() == 0) {
+ pw.println(TAB1 + "<empty>");
+ return;
+ }
- if (mCache.size() == 0) {
- pw.println(TAB1 + "<empty>");
- return;
- }
-
- for (int i = 0, n = mCache.size(); i < n; i++) {
- final String packageName = mCache.keyAt(i);
- final PackageStateUsers pkg = mCache.valueAt(i);
- pw.print(TAB1 + packageName + ": " + pkg.mPackageState + " users=");
- pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
+ for (int i = 0, n = mCache.size(); i < n; i++) {
+ final String packageName = mCache.keyAt(i);
+ final PackageStateUsers pkg = mCache.valueAt(i);
+ pw.print(TAB1 + packageName + ": " + pkg.mPackageState + " users=");
+ pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 98e3e24..22b4d5d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -686,6 +686,9 @@
(installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
final boolean fullApp =
(installFlags & PackageManager.INSTALL_FULL_APP) != 0;
+ final boolean isPackageDeviceAdmin = mPm.isPackageDeviceAdmin(packageName, userId);
+ final boolean isProtectedPackage = mPm.mProtectedPackages != null
+ && mPm.mProtectedPackages.isPackageStateProtected(userId, packageName);
// writer
synchronized (mPm.mLock) {
@@ -694,7 +697,8 @@
if (pkgSetting == null || pkgSetting.getPkg() == null) {
return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
}
- if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp())) {
+ if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp()
+ || isPackageDeviceAdmin || isProtectedPackage)) {
return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
}
if (!snapshot.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 20859da..2124ff6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2034,6 +2034,10 @@
// CHECKSTYLE:ON IndentationCheck
t.traceEnd();
+ t.traceBegin("get system config");
+ SystemConfig systemConfig = injector.getSystemConfig();
+ t.traceEnd();
+
t.traceBegin("addSharedUsers");
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
@@ -2053,6 +2057,13 @@
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ final ArrayMap<String, Integer> oemDefinedUids = systemConfig.getOemDefinedUids();
+ final int numOemDefinedUids = oemDefinedUids.size();
+ for (int i = 0; i < numOemDefinedUids; i++) {
+ mSettings.addOemSharedUserLPw(oemDefinedUids.keyAt(i), oemDefinedUids.valueAt(i),
+ ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ }
+
t.traceEnd();
String separateProcesses = SystemProperties.get("debug.separate_processes");
@@ -2084,10 +2095,7 @@
mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);
- t.traceBegin("get system config");
- SystemConfig systemConfig = injector.getSystemConfig();
mAvailableFeatures = systemConfig.getAvailableFeatures();
- t.traceEnd();
mProtectedPackages = new ProtectedPackages(mContext);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 55280b4..4c9be21 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -977,6 +977,21 @@
return null;
}
+ SharedUserSetting addOemSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
+ if (!name.startsWith("android.uid")) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Failed to add oem defined shared user because of invalid name: " + name);
+ return null;
+ }
+ // OEM defined uids must be in the OEM reserved range
+ if (uid < 2900 || uid > 2999) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Failed to add oem defined shared user because of invalid uid: " + uid);
+ return null;
+ }
+ return addSharedUserLPw(name, uid, pkgFlags, pkgPrivateFlags);
+ }
+
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index c40608d..c95d88e 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -163,6 +163,22 @@
},
{
"name": "CtsUpdateOwnershipEnforcementTestCases"
+ },
+ {
+ "name": "CtsPackageInstallerCUJTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
}
],
"imports": [
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 829ee27..2b639fa 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -379,6 +379,10 @@
/** Count down latch to wait while boot user is not set.*/
private final CountDownLatch mBootUserLatch = new CountDownLatch(1);
+
+ /** Current boot phase. */
+ private @SystemService.BootPhase int mCurrentBootPhase;
+
/**
* Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
*/
@@ -968,6 +972,7 @@
@Override
public void onBootPhase(int phase) {
+ mUms.mCurrentBootPhase = phase;
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mUms.cleanupPartialUsers();
@@ -2105,6 +2110,10 @@
@Override
public void setUserAdmin(@UserIdInt int userId) {
checkManageUserAndAcrossUsersFullPermission("set user admin");
+ if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ checkAdminStatusChangeAllowed(userId);
+ }
+
mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_GRANT_ADMIN);
UserData user;
synchronized (mPackagesLock) {
@@ -2133,6 +2142,10 @@
@Override
public void revokeUserAdmin(@UserIdInt int userId) {
checkManageUserAndAcrossUsersFullPermission("revoke admin privileges");
+ if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ checkAdminStatusChangeAllowed(userId);
+ }
+
mUserJourneyLogger.logUserJourneyBegin(userId, USER_JOURNEY_REVOKE_ADMIN);
UserData user;
synchronized (mPackagesLock) {
@@ -4065,6 +4078,26 @@
}
}
+ /**
+ * Checks if changing the admin status of a target user is restricted
+ * due to the DISALLOW_GRANT_ADMIN restriction. If either the calling
+ * user or the target user has this restriction, a SecurityException
+ * is thrown.
+ *
+ * @param targetUser The user ID of the user whose admin status is being
+ * considered for change.
+ * @throws SecurityException if the admin status change is restricted due
+ * to the DISALLOW_GRANT_ADMIN restriction.
+ */
+ private void checkAdminStatusChangeAllowed(int targetUser) {
+ if (hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, UserHandle.getCallingUserId())
+ || hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, targetUser)) {
+ throw new SecurityException(
+ "Admin status change is restricted. The DISALLOW_GRANT_ADMIN "
+ + "restriction is applied either on the current or the target user.");
+ }
+ }
+
@GuardedBy({"mPackagesLock"})
private void writeBitmapLP(UserInfo info, Bitmap bitmap) {
try {
@@ -5443,6 +5476,13 @@
enforceUserRestriction(restriction, UserHandle.getCallingUserId(),
"Cannot add user");
+ if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+ if ((flags & UserInfo.FLAG_ADMIN) != 0) {
+ enforceUserRestriction(UserManager.DISALLOW_GRANT_ADMIN,
+ UserHandle.getCallingUserId(), "Cannot create ADMIN user");
+ }
+ }
+
return createUserInternalUnchecked(name, userType, flags, parentId,
/* preCreate= */ false, disallowedPackages, /* token= */ null);
}
@@ -6169,6 +6209,11 @@
Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
return false;
}
+ if (mCurrentBootPhase < SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ Slog.w(LOG_TAG, "Cannot remove user, removeUser is called too early during boot. "
+ + "ActivityManager is not ready yet.");
+ return false;
+ }
return removeUserWithProfilesUnchecked(userId);
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index b28da55b..1a2a196 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -360,7 +360,13 @@
IWakeLockCallback callback, int newFlags, String newTag, String newPackageName,
int newOwnerUid, int newOwnerPid, WorkSource newWorkSource, String newHistoryTag,
IWakeLockCallback newCallback) {
-
+ // Todo(b/359154665): We do this because the newWorkSource can potentially be updated
+ // before the request is processed on the notifier thread. This would generally happen is
+ // the Worksource's set method is called, which as of this comment happens only in
+ // PowerManager#setWorksource and WifiManager#WifiLock#setWorksource. Both these places
+ // need to be updated and the WorkSource#set should be deprecated to avoid falling into
+ // such traps
+ newWorkSource = (newWorkSource == null) ? null : new WorkSource(newWorkSource);
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
if (workSource != null && newWorkSource != null
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 8f99d28..1ca267e99 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -62,6 +62,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@@ -207,19 +208,28 @@
private final IBinder mService = new IPowerStatsService.Stub() {
@Override
- public void getSupportedPowerMonitors(ResultReceiver resultReceiver) {
+ public void getSupportedPowerMonitors(@NonNull ResultReceiver resultReceiver) {
+ if (Flags.verifyNonNullArguments()) {
+ Objects.requireNonNull(resultReceiver);
+ }
getHandler().post(() -> getSupportedPowerMonitorsImpl(resultReceiver));
}
@Override
- public void getPowerMonitorReadings(int[] powerMonitorIds, ResultReceiver resultReceiver) {
+ public void getPowerMonitorReadings(@NonNull int[] powerMonitorIds,
+ @NonNull ResultReceiver resultReceiver) {
+ if (Flags.verifyNonNullArguments()) {
+ Objects.requireNonNull(powerMonitorIds);
+ Objects.requireNonNull(resultReceiver);
+ }
int callingUid = Binder.getCallingUid();
getHandler().post(() ->
getPowerMonitorReadingsImpl(powerMonitorIds, resultReceiver, callingUid));
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+ @Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
if (mPowerStatsLogger == null) {
@@ -263,6 +273,11 @@
}
};
+ @VisibleForTesting
+ IPowerStatsService getIPowerStatsServiceForTest() {
+ return (IPowerStatsService) mService;
+ }
+
private class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
public Executor mExecutor = new HandlerExecutor(getHandler());
diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig
index 0a4a751..29ad7dc 100644
--- a/services/core/java/com/android/server/powerstats/flags.aconfig
+++ b/services/core/java/com/android/server/powerstats/flags.aconfig
@@ -10,4 +10,15 @@
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "verify_non_null_arguments"
+ namespace: "backstage_power"
+ description: "Verify arguments passed are non-null"
+ bug: "356731520"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a4a29a0..2faa68a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -20,6 +20,8 @@
import android.app.ITransientNotificationCallback;
import android.content.ComponentName;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
@@ -54,13 +56,12 @@
* Used by InputMethodManagerService to notify the IME status.
*
* @param displayId The display to which the IME is bound to.
- * @param vis Bit flags about the IME visibility.
- * (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE})
- * @param backDisposition Bit flags about the IME back disposition.
- * (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT})
+ * @param vis The IME visibility.
+ * @param backDisposition The IME back disposition.
* @param showImeSwitcher {@code true} when the IME switcher button should be shown.
*/
- void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
+ void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher);
/**
* See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c3601b3c..7d812ee 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -26,6 +26,10 @@
import static android.app.StatusBarManager.NavBarMode;
import static android.app.StatusBarManager.SessionFlags;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.os.UserHandle.getCallingUserId;
+import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
@@ -60,6 +64,8 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
import android.net.Uri;
@@ -113,6 +119,7 @@
import com.android.server.UiThread;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.notification.NotificationDelegate;
+import com.android.server.pm.UserManagerService;
import com.android.server.policy.GlobalActionsProvider;
import com.android.server.power.ShutdownCheckPoints;
import com.android.server.power.ShutdownThread;
@@ -145,9 +152,9 @@
/**
* In apps targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or higher, calling
- * {@link android.service.quicksettings.TileService#requestListeningState} will check that the
- * calling package (uid) and the package of the target {@link android.content.ComponentName}
- * match. It'll also make sure that the context used can take actions on behalf of the current
+ * {@link android.service.quicksettings.TileService#requestListeningState} will check that the
+ * calling package (uid) and the package of the target {@link android.content.ComponentName}
+ * match. It'll also make sure that the context used can take actions on behalf of the current
* user.
*/
@ChangeId
@@ -197,6 +204,9 @@
private IOverlayManager mOverlayManager;
+ private final boolean mVisibleBackgroundUsersEnabled;
+ private final UserManagerService mUserManager;
+
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
mBar.asBinder().unlinkToDeath(this,0);
@@ -297,6 +307,9 @@
mTileRequestTracker = new TileRequestTracker(mContext);
mSessionMonitor = new SessionMonitor(mContext);
+
+ mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();
+ mUserManager = UserManagerService.getInstance();
}
/**
@@ -534,8 +547,8 @@
}
@Override
- public void setImeWindowStatus(int displayId, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
StatusBarManagerService.this.setImeWindowStatus(displayId, vis, backDisposition,
showImeSwitcher);
}
@@ -911,6 +924,7 @@
@Override
public void expandNotificationsPanel() {
enforceExpandStatusBar();
+ enforceValidCallingUser();
if (isDisable2FlagSet(DISABLE2_NOTIFICATION_SHADE)) {
return;
@@ -926,6 +940,8 @@
@Override
public void collapsePanels() {
+ enforceValidCallingUser();
+
if (!checkCanCollapseStatusBar("collapsePanels")) {
return;
}
@@ -940,6 +956,8 @@
@Override
public void togglePanel() {
+ enforceValidCallingUser();
+
if (!checkCanCollapseStatusBar("togglePanel")) {
return;
}
@@ -959,6 +977,7 @@
@Override
public void expandSettingsPanel(String subPanel) {
enforceExpandStatusBar();
+ enforceValidCallingUser();
if (mBar != null) {
try {
@@ -973,6 +992,7 @@
addQsTileToFrontOrEnd(component, false);
} else {
enforceStatusBarOrShell();
+ enforceValidCallingUser();
if (mBar != null) {
try {
@@ -985,6 +1005,7 @@
private void addQsTileToFrontOrEnd(ComponentName tile, boolean end) {
enforceStatusBarOrShell();
+ enforceValidCallingUser();
if (mBar != null) {
try {
@@ -996,6 +1017,7 @@
public void remTile(ComponentName component) {
enforceStatusBarOrShell();
+ enforceValidCallingUser();
if (mBar != null) {
try {
@@ -1018,6 +1040,7 @@
public void clickTile(ComponentName component) {
enforceStatusBarOrShell();
+ enforceValidCallingUser();
if (mBar != null) {
try {
@@ -1029,6 +1052,8 @@
@Override
public void handleSystemKey(KeyEvent key) throws RemoteException {
+ enforceValidCallingUser();
+
if (!checkCanCollapseStatusBar("handleSystemKey")) {
return;
}
@@ -1053,6 +1078,8 @@
@Override
public void showPinningEnterExitToast(boolean entering) throws RemoteException {
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.showPinningEnterExitToast(entering);
@@ -1063,6 +1090,8 @@
@Override
public void showPinningEscapeToast() throws RemoteException {
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.showPinningEscapeToast();
@@ -1076,6 +1105,8 @@
int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
int userId, long operationId, String opPackageName, long requestId) {
enforceBiometricDialog();
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.showAuthenticationDialog(promptInfo, receiver, sensorIds, credentialAllowed,
@@ -1088,6 +1119,8 @@
@Override
public void onBiometricAuthenticated(@Modality int modality) {
enforceBiometricDialog();
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.onBiometricAuthenticated(modality);
@@ -1099,6 +1132,8 @@
@Override
public void onBiometricHelp(@Modality int modality, String message) {
enforceBiometricDialog();
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.onBiometricHelp(modality, message);
@@ -1110,6 +1145,8 @@
@Override
public void onBiometricError(int modality, int error, int vendorCode) {
enforceBiometricDialog();
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.onBiometricError(modality, error, vendorCode);
@@ -1121,6 +1158,8 @@
@Override
public void hideAuthenticationDialog(long requestId) {
enforceBiometricDialog();
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.hideAuthenticationDialog(requestId);
@@ -1132,6 +1171,8 @@
@Override
public void setBiometicContextListener(IBiometricContextListener listener) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
synchronized (mLock) {
mBiometricContextListener = listener;
}
@@ -1146,6 +1187,8 @@
@Override
public void setUdfpsRefreshRateCallback(IUdfpsRefreshRateRequestCallback callback) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.setUdfpsRefreshRateCallback(callback);
@@ -1156,6 +1199,8 @@
@Override
public void startTracing() {
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mBar.startTracing();
@@ -1167,6 +1212,8 @@
@Override
public void stopTracing() {
+ enforceValidCallingUser();
+
if (mBar != null) {
try {
mTracingEnabled = false;
@@ -1190,6 +1237,7 @@
@Override
public void disableForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
+ enforceValidCallingUser();
synchronized (mLock) {
disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
@@ -1293,6 +1341,7 @@
public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
String contentDescription) {
enforceStatusBar();
+ enforceValidCallingUser();
synchronized (mIcons) {
StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
@@ -1313,6 +1362,7 @@
@Override
public void setIconVisibility(String slot, boolean visibility) {
enforceStatusBar();
+ enforceValidCallingUser();
synchronized (mIcons) {
StatusBarIcon icon = mIcons.get(slot);
@@ -1336,6 +1386,7 @@
@Override
public void removeIcon(String slot) {
enforceStatusBar();
+ enforceValidCallingUser();
synchronized (mIcons) {
mIcons.remove(slot);
@@ -1351,9 +1402,10 @@
}
@Override
- public void setImeWindowStatus(int displayId, final int vis, final int backDisposition,
- final boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, @ImeWindowVisibility final int vis,
+ @BackDispositionMode final int backDisposition, final boolean showImeSwitcher) {
enforceStatusBar();
+ enforceValidCallingUser();
if (SPEW) {
Slog.d(TAG, "setImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
@@ -1418,8 +1470,10 @@
private String mPackageName = "none";
private int mDisabled1 = 0;
private int mDisabled2 = 0;
+ @ImeWindowVisibility
private int mImeWindowVis = 0;
- private int mImeBackDisposition = 0;
+ @BackDispositionMode
+ private int mImeBackDisposition = BACK_DISPOSITION_DEFAULT;
private boolean mShowImeSwitcher = false;
private LetterboxDetails[] mLetterboxDetails = new LetterboxDetails[0];
@@ -1462,7 +1516,8 @@
return mDisabled1 == disabled1 && mDisabled2 == disabled2;
}
- private void setImeWindowState(final int vis, final int backDisposition,
+ private void setImeWindowState(@ImeWindowVisibility final int vis,
+ @BackDispositionMode final int backDisposition,
final boolean showImeSwitcher) {
mImeWindowVis = vis;
mImeBackDisposition = backDisposition;
@@ -1544,6 +1599,7 @@
@Override
public RegisterStatusBarResult registerStatusBar(IStatusBar bar) {
enforceStatusBarService();
+ enforceValidCallingUser();
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;
@@ -1594,6 +1650,8 @@
@Override
public void onPanelRevealed(boolean clearNotificationEffects, int numItems) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onPanelRevealed(clearNotificationEffects, numItems);
@@ -1605,6 +1663,8 @@
@Override
public void clearNotificationEffects() throws RemoteException {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.clearEffects();
@@ -1616,6 +1676,8 @@
@Override
public void onPanelHidden() throws RemoteException {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onPanelHidden();
@@ -1630,6 +1692,8 @@
@Override
public void shutdown() {
enforceStatusBarService();
+ enforceValidCallingUser();
+
String reason = PowerManager.SHUTDOWN_USER_REQUESTED;
ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
final long identity = Binder.clearCallingIdentity();
@@ -1649,6 +1713,8 @@
@Override
public void reboot(boolean safeMode) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
String reason = safeMode
? PowerManager.REBOOT_SAFE_MODE
: PowerManager.SHUTDOWN_USER_REQUESTED;
@@ -1675,6 +1741,8 @@
@Override
public void restart() {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
@@ -1688,6 +1756,8 @@
@Override
public void onGlobalActionsShown() {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
if (mGlobalActionListener == null) return;
@@ -1700,6 +1770,8 @@
@Override
public void onGlobalActionsHidden() {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
if (mGlobalActionListener == null) return;
@@ -1711,6 +1783,8 @@
@Override
public void onNotificationClick(String key, NotificationVisibility nv) {
+ // enforceValidCallingUser is not required here as the NotificationManagerService
+ // will handle multi-user scenarios
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -1726,6 +1800,8 @@
public void onNotificationActionClick(
String key, int actionIndex, Notification.Action action, NotificationVisibility nv,
boolean generatedByAssistant) {
+ // enforceValidCallingUser is not required here as the NotificationManagerService
+ // will handle multi-user scenarios
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -1741,6 +1817,8 @@
@Override
public void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message, int userId) {
+ // enforceValidCallingUser is not required here as the NotificationManagerService
+ // will handle multi-user scenarios
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -1759,6 +1837,8 @@
@NotificationStats.DismissalSurface int dismissalSurface,
@NotificationStats.DismissalSentiment int dismissalSentiment,
NotificationVisibility nv) {
+ // enforceValidCallingUser is not required here as the NotificationManagerService
+ // will handle multi-user scenarios
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -1776,6 +1856,8 @@
NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys)
throws RemoteException {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationVisibilityChanged(
@@ -1789,6 +1871,8 @@
public void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded,
int location) throws RemoteException {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationExpansionChanged(
@@ -1801,6 +1885,8 @@
@Override
public void onNotificationDirectReplied(String key) throws RemoteException {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationDirectReplied(key);
@@ -1813,6 +1899,8 @@
public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
int smartActionCount, boolean generatedByAssistant, boolean editBeforeSending) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount,
@@ -1827,6 +1915,8 @@
String key, int replyIndex, CharSequence reply, int notificationLocation,
boolean modifiedBeforeSending) throws RemoteException {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
@@ -1839,6 +1929,8 @@
@Override
public void onNotificationSettingsViewed(String key) throws RemoteException {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationSettingsViewed(key);
@@ -1863,6 +1955,8 @@
@Override
public void onNotificationBubbleChanged(String key, boolean isBubble, int flags) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationBubbleChanged(key, isBubble, flags);
@@ -1874,6 +1968,8 @@
@Override
public void onBubbleMetadataFlagChanged(String key, int flags) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onBubbleMetadataFlagChanged(key, flags);
@@ -1885,6 +1981,8 @@
@Override
public void hideCurrentInputMethodForBubbles(int displayId) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long token = Binder.clearCallingIdentity();
try {
InputMethodManagerInternal.get().hideInputMethod(
@@ -1923,6 +2021,8 @@
@Override
public void onNotificationFeedbackReceived(String key, Bundle feedback) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
final long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationFeedbackReceived(key, feedback);
@@ -1942,6 +2042,8 @@
@Override
public void showInattentiveSleepWarning() {
enforceStatusBarService();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -1954,6 +2056,8 @@
@Override
public void dismissInattentiveSleepWarning(boolean animated) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -1966,6 +2070,8 @@
@Override
public void suppressAmbientDisplay(boolean suppress) {
enforceStatusBarService();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -2179,6 +2285,8 @@
@Override
public void cancelRequestAddTile(@NonNull String packageName) {
enforceStatusBar();
+ enforceValidCallingUser();
+
cancelRequestAddTileInternal(packageName);
}
@@ -2202,23 +2310,31 @@
@Override
public void onSessionStarted(@SessionFlags int sessionType, InstanceId instance) {
+ enforceValidCallingUser();
+
mSessionMonitor.onSessionStarted(sessionType, instance);
}
@Override
public void onSessionEnded(@SessionFlags int sessionType, InstanceId instance) {
+ enforceValidCallingUser();
+
mSessionMonitor.onSessionEnded(sessionType, instance);
}
@Override
public void registerSessionListener(@SessionFlags int sessionFlags,
ISessionListener listener) {
+ enforceValidCallingUser();
+
mSessionMonitor.registerSessionListener(sessionFlags, listener);
}
@Override
public void unregisterSessionListener(@SessionFlags int sessionFlags,
ISessionListener listener) {
+ enforceValidCallingUser();
+
mSessionMonitor.unregisterSessionListener(sessionFlags, listener);
}
@@ -2233,6 +2349,8 @@
*/
public void setNavBarMode(@NavBarMode int navBarMode) {
enforceStatusBar();
+ enforceValidCallingUser();
+
if (navBarMode != NAV_BAR_MODE_DEFAULT && navBarMode != NAV_BAR_MODE_KIDS) {
throw new IllegalArgumentException("Supplied navBarMode not supported: " + navBarMode);
}
@@ -2243,6 +2361,7 @@
throw new SecurityException("Calling user id: " + callingUserId
+ ", cannot call on behalf of current user id: " + mCurrentUserId + ".");
}
+
final long userIdentity = Binder.clearCallingIdentity();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -2316,6 +2435,8 @@
@Nullable IUndoMediaTransferCallback undoCallback
) {
enforceMediaContentControl();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -2340,6 +2461,8 @@
@Nullable Icon appIcon,
@Nullable CharSequence appName) {
enforceMediaContentControl();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -2367,6 +2490,8 @@
@NonNull INearbyMediaDevicesProvider provider
) {
enforceMediaContentControl();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -2393,6 +2518,8 @@
@NonNull INearbyMediaDevicesProvider provider
) {
enforceMediaContentControl();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -2407,6 +2534,8 @@
@Override
public void showRearDisplayDialog(int currentState) {
enforceControlDeviceStatePermission();
+ enforceValidCallingUser();
+
IStatusBar bar = mBar;
if (bar != null) {
try {
@@ -2579,4 +2708,32 @@
private static final Context getUiContext() {
return ActivityThread.currentActivityThread().getSystemUiContext();
}
+
+ /**
+ * This method validates whether the calling user is allowed to control the status bar
+ * on a device that enables visible background users.
+ * Only system or current user or the user that belongs to the same profile group as the
+ * current user is permitted to control the status bar.
+ */
+ private void enforceValidCallingUser() {
+ if (!mVisibleBackgroundUsersEnabled) {
+ return;
+ }
+
+ int callingUserId = getCallingUserId();
+ if (callingUserId == USER_SYSTEM || callingUserId == mCurrentUserId) {
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mUserManager.isSameProfileGroup(callingUserId, mCurrentUserId)) {
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ throw new SecurityException("User " + callingUserId
+ + " is not permitted to use this method");
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 46bd7af..fe0cf59 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -170,9 +170,11 @@
/** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
- int vibrationType = isRepeating()
- ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED
- : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE;
+ int vibrationType = mEffectToPlay.hasVendorEffects()
+ ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__VENDOR
+ : isRepeating()
+ ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED
+ : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE;
return new VibrationStats.StatsInfo(
callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), mStatus,
stats, completionUptimeMillis);
diff --git a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
index 8f36118..407f3d9 100644
--- a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
@@ -52,6 +52,7 @@
long vibratorOnResult = controller.on(effect, getVibration().id);
vibratorOnResult = Math.min(vibratorOnResult, VENDOR_EFFECT_MAX_DURATION_MS);
handleVibratorOnResult(vibratorOnResult);
+ getVibration().stats.reportPerformVendorEffect(vibratorOnResult);
return List.of(new CompleteEffectVibratorStep(conductor, startTime,
/* cancelled= */ false, controller, mPendingVibratorOffDeadline));
} finally {
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index f2f5eda..0d6778c 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,7 +16,6 @@
package com.android.server.vibrator;
-import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
import static android.os.VibrationAttributes.USAGE_ALARM;
import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
@@ -191,8 +190,6 @@
@GuardedBy("mLock")
private boolean mVibrateOn;
@GuardedBy("mLock")
- private boolean mKeyboardVibrationOn;
- @GuardedBy("mLock")
private int mRingerMode;
@GuardedBy("mLock")
private boolean mOnWirelessCharger;
@@ -532,14 +529,6 @@
return false;
}
- if (mVibrationConfig.isKeyboardVibrationSettingsSupported()) {
- int category = callerInfo.attrs.getCategory();
- if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
- // Keyboard touch has a different user setting.
- return mKeyboardVibrationOn;
- }
- }
-
// Apply individual user setting based on usage.
return getCurrentIntensity(usage) != Vibrator.VIBRATION_INTENSITY_OFF;
}
@@ -556,10 +545,11 @@
mVibrateInputDevices =
loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0, userHandle) > 0;
mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1, userHandle) > 0;
- mKeyboardVibrationOn = loadSystemSetting(
- Settings.System.KEYBOARD_VIBRATION_ENABLED, 1, userHandle) > 0;
- int keyboardIntensity = getDefaultIntensity(USAGE_IME_FEEDBACK);
+ boolean isKeyboardVibrationOn = loadSystemSetting(
+ Settings.System.KEYBOARD_VIBRATION_ENABLED, 1, userHandle) > 0;
+ int keyboardIntensity = toIntensity(isKeyboardVibrationOn,
+ getDefaultIntensity(USAGE_IME_FEEDBACK));
int alarmIntensity = toIntensity(
loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_ALARM));
@@ -654,7 +644,6 @@
return "VibrationSettings{"
+ "mVibratorConfig=" + mVibrationConfig
+ ", mVibrateOn=" + mVibrateOn
- + ", mKeyboardVibrationOn=" + mKeyboardVibrationOn
+ ", mVibrateInputDevices=" + mVibrateInputDevices
+ ", mBatterySaverMode=" + mBatterySaverMode
+ ", mRingerMode=" + ringerModeToString(mRingerMode)
@@ -671,7 +660,6 @@
pw.println("VibrationSettings:");
pw.increaseIndent();
pw.println("vibrateOn = " + mVibrateOn);
- pw.println("keyboardVibrationOn = " + mKeyboardVibrationOn);
pw.println("vibrateInputDevices = " + mVibrateInputDevices);
pw.println("batterySaverMode = " + mBatterySaverMode);
pw.println("ringerMode = " + ringerModeToString(mRingerMode));
@@ -698,8 +686,6 @@
void dump(ProtoOutputStream proto) {
synchronized (mLock) {
proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
- proto.write(VibratorManagerServiceDumpProto.KEYBOARD_VIBRATION_ON,
- mKeyboardVibrationOn);
proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
getCurrentIntensity(USAGE_ALARM));
@@ -774,6 +760,11 @@
return value;
}
+ @VibrationIntensity
+ private int toIntensity(boolean enabled, @VibrationIntensity int defaultValue) {
+ return enabled ? defaultValue : Vibrator.VIBRATION_INTENSITY_OFF;
+ }
+
private boolean loadBooleanSetting(String settingKey, int userHandle) {
return loadSystemSetting(settingKey, 0, userHandle) != 0;
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
index dd66809..8179d6a 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStats.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -79,6 +79,7 @@
private int mVibratorSetAmplitudeCount;
private int mVibratorSetExternalControlCount;
private int mVibratorPerformCount;
+ private int mVibratorPerformVendorCount;
private int mVibratorComposeCount;
private int mVibratorComposePwleCount;
@@ -239,6 +240,11 @@
}
}
+ /** Report a call to vibrator method to trigger a vendor vibration effect. */
+ void reportPerformVendorEffect(long halResult) {
+ mVibratorPerformVendorCount++;
+ }
+
/** Report a call to vibrator method to trigger a vibration as a composition of primitives. */
void reportComposePrimitives(long halResult, PrimitiveSegment[] primitives) {
mVibratorComposeCount++;
@@ -313,6 +319,7 @@
public final int halOnCount;
public final int halOffCount;
public final int halPerformCount;
+ public final int halPerformVendorCount;
public final int halSetAmplitudeCount;
public final int halSetExternalControlCount;
public final int halCompositionSize;
@@ -357,6 +364,7 @@
halOnCount = stats.mVibratorOnCount;
halOffCount = stats.mVibratorOffCount;
halPerformCount = stats.mVibratorPerformCount;
+ halPerformVendorCount = stats.mVibratorPerformVendorCount;
halSetAmplitudeCount = stats.mVibratorSetAmplitudeCount;
halSetExternalControlCount = stats.mVibratorSetExternalControlCount;
halCompositionSize = stats.mVibrationCompositionTotalSize;
@@ -390,7 +398,8 @@
halOnCount, halOffCount, halPerformCount, halSetAmplitudeCount,
halSetExternalControlCount, halSupportedCompositionPrimitivesUsed,
halSupportedEffectsUsed, halUnsupportedCompositionPrimitivesUsed,
- halUnsupportedEffectsUsed, halCompositionSize, halPwleSize, adaptiveScale);
+ halUnsupportedEffectsUsed, halCompositionSize, halPwleSize, adaptiveScale,
+ halPerformVendorCount);
}
private static int[] filteredKeys(SparseBooleanArray supportArray, boolean supported) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5c096ec..a76f1b6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1834,33 +1834,36 @@
}
}
- mLetterboxUiController.onMovedToDisplay(mDisplayContent.getDisplayId());
+ mAppCompatController.getAppCompatLetterboxPolicy()
+ .onMovedToDisplay(mDisplayContent.getDisplayId());
}
void layoutLetterboxIfNeeded(WindowState winHint) {
- mLetterboxUiController.layoutLetterboxIfNeeded(winHint);
+ mAppCompatController.getAppCompatLetterboxPolicy().start(winHint);
}
boolean hasWallpaperBackgroundForLetterbox() {
- return mLetterboxUiController.hasWallpaperBackgroundForLetterbox();
+ return mAppCompatController.getAppCompatLetterboxOverrides()
+ .hasWallpaperBackgroundForLetterbox();
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
- mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t, getPendingTransaction());
+ mAppCompatController.getAppCompatLetterboxPolicy()
+ .updateLetterboxSurfaceIfNeeded(winHint, t, getPendingTransaction());
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
- mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint);
+ mAppCompatController.getAppCompatLetterboxPolicy().updateLetterboxSurfaceIfNeeded(winHint);
}
/** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
Rect getLetterboxInsets() {
- return mLetterboxUiController.getLetterboxInsets();
+ return mAppCompatController.getAppCompatLetterboxPolicy().getLetterboxInsets();
}
/** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
void getLetterboxInnerBounds(Rect outBounds) {
- mLetterboxUiController.getLetterboxInnerBounds(outBounds);
+ mAppCompatController.getAppCompatLetterboxPolicy().getLetterboxInnerBounds(outBounds);
}
/**
@@ -1868,7 +1871,8 @@
* when the current activity is displayed.
*/
boolean isFullyTransparentBarAllowed(Rect rect) {
- return mLetterboxUiController.isFullyTransparentBarAllowed(rect);
+ return mAppCompatController.getAppCompatLetterboxPolicy()
+ .isFullyTransparentBarAllowed(rect);
}
private static class Token extends Binder {
@@ -4368,7 +4372,8 @@
mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
mTaskSupervisor.mStoppingActivities.remove(this);
- mLetterboxUiController.destroy();
+
+ mAppCompatController.getAppCompatLetterboxPolicy().stop();
mAppCompatController.getTransparentPolicy().stop();
// Defer removal of this activity when either a child is animating, or app transition is on
@@ -7688,7 +7693,7 @@
t.setLayer(mAnimationBoundsLayer, getLastLayer());
if (mNeedsLetterboxedAnimation) {
- final int cornerRadius = mLetterboxUiController
+ final int cornerRadius = mAppCompatController.getAppCompatLetterboxPolicy()
.getRoundedCornersRadius(findMainWindow());
final Rect letterboxInnerBounds = new Rect();
@@ -8574,7 +8579,7 @@
* Returns whether activity bounds are letterboxed.
*
* <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
- * LetterboxUiController#shouldShowLetterboxUi} for more context.
+ * AppCompatLetterboxOverrides#shouldShowLetterboxUi} for more context.
*/
boolean areBoundsLetterboxed() {
return getAppCompatState(/* ignoreVisibility= */ true)
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e4cae58..f23211b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3144,9 +3144,23 @@
null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
}
+ /**
+ * Requests assist data for a particular Task.
+ *
+ * <p>This is used by the system components to request assist data for a Task.
+ *
+ * @param receiver The receiver to send the assist data to.
+ * @param taskId The Task to request assist data for.
+ * @param callingPackageName The package name of the caller.
+ * @param callingAttributionTag The attribution tag of the caller.
+ * @param fetchStructure Whether to fetch the assist structure. Note that this is slow and
+ * should be avoided if possible.
+ * @return Whether the request was successful.
+ */
@Override
public boolean requestAssistDataForTask(IAssistDataReceiver receiver, int taskId,
- String callingPackageName, @Nullable String callingAttributionTag) {
+ String callingPackageName, @Nullable String callingAttributionTag,
+ boolean fetchStructure) {
mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"requestAssistDataForTask()");
final long callingId = Binder.clearCallingIdentity();
@@ -3171,7 +3185,7 @@
List<IBinder> topActivityToken = new ArrayList<>();
topActivityToken.add(tokens.getActivityToken());
requester.requestAssistData(topActivityToken, true /* fetchData */,
- false /* fetchScreenshot */, false /* fetchStructure */, true /* allowFetchData */,
+ false /* fetchScreenshot */, fetchStructure, true /* allowFetchData */,
false /* allowFetchScreenshot*/, true /* ignoreFocusCheck */,
Binder.getCallingUid(), callingPackageName, callingAttributionTag);
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index d38edfc..4290051 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -40,6 +40,8 @@
private final AppCompatOverrides mAppCompatOverrides;
@NonNull
private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
+ @NonNull
+ private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
AppCompatController(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord) {
@@ -57,6 +59,7 @@
mTransparentPolicy, mAppCompatOverrides);
mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord,
wmService.mAppCompatConfiguration);
+ mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord);
}
@NonNull
@@ -113,6 +116,11 @@
}
@NonNull
+ AppCompatLetterboxPolicy getAppCompatLetterboxPolicy() {
+ return mAppCompatLetterboxPolicy;
+ }
+
+ @NonNull
AppCompatFocusOverrides getAppCompatFocusOverrides() {
return mAppCompatOverrides.getAppCompatFocusOverrides();
}
@@ -127,4 +135,9 @@
return mAppCompatDeviceStateQuery;
}
+ @NonNull
+ AppCompatLetterboxOverrides getAppCompatLetterboxOverrides() {
+ return mAppCompatOverrides.getAppCompatLetterboxOverrides();
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java b/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java
new file mode 100644
index 0000000..24ed14c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java
@@ -0,0 +1,147 @@
+/*
+ * 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.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.graphics.Color;
+import android.util.Slog;
+import android.view.WindowManager;
+
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
+/**
+ * Encapsulates overrides and configuration related to the Letterboxing policy.
+ */
+class AppCompatLetterboxOverrides {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatLetterboxOverrides" : TAG_ATM;
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final AppCompatConfiguration mAppCompatConfiguration;
+
+ private boolean mShowWallpaperForLetterboxBackground;
+
+ AppCompatLetterboxOverrides(@NonNull ActivityRecord activityRecord,
+ @NonNull AppCompatConfiguration appCompatConfiguration) {
+ mActivityRecord = activityRecord;
+ mAppCompatConfiguration = appCompatConfiguration;
+ }
+
+ boolean shouldLetterboxHaveRoundedCorners() {
+ // TODO(b/214030873): remove once background is drawn for transparent activities
+ // Letterbox shouldn't have rounded corners if the activity is transparent
+ return mAppCompatConfiguration.isLetterboxActivityCornersRounded()
+ && mActivityRecord.fillsParent();
+ }
+
+ boolean isLetterboxEducationEnabled() {
+ return mAppCompatConfiguration.getIsEducationEnabled();
+ }
+
+ boolean hasWallpaperBackgroundForLetterbox() {
+ return mShowWallpaperForLetterboxBackground;
+ }
+
+ boolean checkWallpaperBackgroundForLetterbox(boolean wallpaperShouldBeShown) {
+ if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
+ mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
+ return true;
+ }
+ return false;
+ }
+
+ @NonNull
+ Color getLetterboxBackgroundColor() {
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (w == null || w.isLetterboxedForDisplayCutout()) {
+ return Color.valueOf(Color.BLACK);
+ }
+ final @LetterboxBackgroundType int letterboxBackgroundType =
+ mAppCompatConfiguration.getLetterboxBackgroundType();
+ final ActivityManager.TaskDescription taskDescription = mActivityRecord.taskDescription;
+ switch (letterboxBackgroundType) {
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColorFloating());
+ }
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColor());
+ }
+ break;
+ case LETTERBOX_BACKGROUND_WALLPAPER:
+ if (hasWallpaperBackgroundForLetterbox()) {
+ // Color is used for translucent scrim that dims wallpaper.
+ return mAppCompatConfiguration.getLetterboxBackgroundColor();
+ }
+ Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
+ + "blur is not supported by a device or not supported in the current "
+ + "window configuration or both alpha scrim and blur radius aren't "
+ + "provided so using solid color background");
+ break;
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return mAppCompatConfiguration.getLetterboxBackgroundColor();
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox background type: " + letterboxBackgroundType);
+ }
+ // If picked option configured incorrectly or not supported then default to a solid color
+ // background.
+ return mAppCompatConfiguration.getLetterboxBackgroundColor();
+ }
+
+ int getLetterboxActivityCornersRadius() {
+ return mAppCompatConfiguration.getLetterboxActivityCornersRadius();
+ }
+
+ boolean isLetterboxActivityCornersRounded() {
+ return mAppCompatConfiguration.isLetterboxActivityCornersRounded();
+ }
+
+ @LetterboxBackgroundType
+ int getLetterboxBackgroundType() {
+ return mAppCompatConfiguration.getLetterboxBackgroundType();
+ }
+
+ int getLetterboxWallpaperBlurRadiusPx() {
+ int blurRadius = mAppCompatConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx();
+ return Math.max(blurRadius, 0);
+ }
+
+ float getLetterboxWallpaperDarkScrimAlpha() {
+ float alpha = mAppCompatConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
+ // No scrim by default.
+ return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
+ }
+
+ boolean isLetterboxWallpaperBlurSupported() {
+ return mAppCompatConfiguration.mContext.getSystemService(WindowManager.class)
+ .isCrossWindowBlurEnabled();
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
new file mode 100644
index 0000000..d602c47
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -0,0 +1,372 @@
+/*
+ * 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.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 com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.LetterboxDetails;
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
+/**
+ * Encapsulates the logic for the Letterboxing policy.
+ */
+class AppCompatLetterboxPolicy {
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final LetterboxPolicyState mLetterboxPolicyState;
+ @NonNull
+ private final AppCompatRoundedCorners mAppCompatRoundedCorners;
+
+ private boolean mLastShouldShowLetterboxUi;
+
+ AppCompatLetterboxPolicy(@NonNull ActivityRecord activityRecord) {
+ mActivityRecord = activityRecord;
+ mLetterboxPolicyState = new LetterboxPolicyState();
+ // TODO (b/358334569) Improve cutout logic dependency on app compat.
+ mAppCompatRoundedCorners = new AppCompatRoundedCorners(mActivityRecord,
+ this::isLetterboxedNotForDisplayCutout);
+ }
+
+ /** Cleans up {@link Letterbox} if it exists.*/
+ void stop() {
+ mLetterboxPolicyState.stop();
+ }
+
+ /** @return {@value true} if the letterbox policy is running and the activity letterboxed. */
+ boolean isRunning() {
+ return mLetterboxPolicyState.isRunning();
+ }
+
+ void onMovedToDisplay(int displayId) {
+ mLetterboxPolicyState.onMovedToDisplay(displayId);
+ }
+
+ /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
+ @NonNull
+ Rect getLetterboxInsets() {
+ return mLetterboxPolicyState.getLetterboxInsets();
+ }
+
+ /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
+ void getLetterboxInnerBounds(@NonNull Rect outBounds) {
+ mLetterboxPolicyState.getLetterboxInnerBounds(outBounds);
+ }
+
+ @Nullable
+ LetterboxDetails getLetterboxDetails() {
+ return mLetterboxPolicyState.getLetterboxDetails();
+ }
+
+ /**
+ * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
+ * when the current activity is displayed.
+ */
+ boolean isFullyTransparentBarAllowed(@NonNull Rect rect) {
+ return mLetterboxPolicyState.isFullyTransparentBarAllowed(rect);
+ }
+
+ void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint,
+ @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction inputT) {
+ mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint, t, inputT);
+ }
+
+ void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint) {
+ mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint,
+ mActivityRecord.getSyncTransaction(), mActivityRecord.getPendingTransaction());
+ }
+
+ void start(@NonNull WindowState w) {
+ if (shouldNotLayoutLetterbox(w)) {
+ return;
+ }
+ mAppCompatRoundedCorners.updateRoundedCornersIfNeeded(w);
+ updateWallpaperForLetterbox(w);
+ if (shouldShowLetterboxUi(w)) {
+ mLetterboxPolicyState.layoutLetterboxIfNeeded(w);
+ } else {
+ mLetterboxPolicyState.hide();
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldShowLetterboxUi(@NonNull WindowState mainWindow) {
+ if (mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+ .getIsRelaunchingAfterRequestedOrientationChanged()) {
+ return mLastShouldShowLetterboxUi;
+ }
+
+ final boolean shouldShowLetterboxUi =
+ (mActivityRecord.isInLetterboxAnimation() || mActivityRecord.isVisible()
+ || mActivityRecord.isVisibleRequested())
+ && mainWindow.areAppWindowBoundsLetterboxed()
+ // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
+ // WindowContainer#showWallpaper because the later will return true when
+ // this activity is using blurred wallpaper for letterbox background.
+ && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
+
+ mLastShouldShowLetterboxUi = shouldShowLetterboxUi;
+
+ return shouldShowLetterboxUi;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) {
+ return mAppCompatRoundedCorners.getCropBoundsIfNeeded(mainWindow);
+ }
+
+ /**
+ * Returns rounded corners radius the letterboxed activity should have based on override in
+ * R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+ * Device corners can be different on the right and left sides, but we use the same radius
+ * for all corners for consistency and pick a minimal bottom one for consistency with a
+ * taskbar rounded corners.
+ *
+ * @param mainWindow The {@link WindowState} to consider for the rounded corners calculation.
+ */
+ int getRoundedCornersRadius(@NonNull final WindowState mainWindow) {
+ return mAppCompatRoundedCorners.getRoundedCornersRadius(mainWindow);
+ }
+
+ private void updateWallpaperForLetterbox(@NonNull WindowState mainWindow) {
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ final @LetterboxBackgroundType int letterboxBackgroundType =
+ letterboxOverrides.getLetterboxBackgroundType();
+ boolean wallpaperShouldBeShown =
+ letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
+ // Don't use wallpaper as a background if letterboxed for display cutout.
+ && isLetterboxedNotForDisplayCutout(mainWindow)
+ // Check that dark scrim alpha or blur radius are provided
+ && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() > 0
+ || letterboxOverrides.getLetterboxWallpaperDarkScrimAlpha() > 0)
+ // Check that blur is supported by a device if blur radius is provided.
+ && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() <= 0
+ || letterboxOverrides.isLetterboxWallpaperBlurSupported());
+ if (letterboxOverrides.checkWallpaperBackgroundForLetterbox(wallpaperShouldBeShown)) {
+ mActivityRecord.requestUpdateWallpaperIfNeeded();
+ }
+ }
+
+ private boolean isLetterboxedNotForDisplayCutout(@NonNull WindowState mainWindow) {
+ return shouldShowLetterboxUi(mainWindow)
+ && !mainWindow.isLetterboxedForDisplayCutout();
+ }
+
+ private static boolean shouldNotLayoutLetterbox(@Nullable WindowState w) {
+ if (w == null) {
+ return true;
+ }
+ final int type = w.mAttrs.type;
+ // Allow letterbox to be displayed early for base application or application starting
+ // windows even if it is not on the top z order to prevent flickering when the
+ // letterboxed window is brought to the top
+ return (type != TYPE_BASE_APPLICATION && type != TYPE_APPLICATION_STARTING)
+ || w.mAnimatingExit;
+ }
+
+ private class LetterboxPolicyState {
+
+ @Nullable
+ private Letterbox mLetterbox;
+
+ void layoutLetterboxIfNeeded(@NonNull WindowState w) {
+ if (!isRunning()) {
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ final AppCompatReachabilityPolicy reachabilityPolicy = mActivityRecord
+ .mAppCompatController.getAppCompatReachabilityPolicy();
+ mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
+ mActivityRecord.mWmService.mTransactionFactory,
+ reachabilityPolicy, letterboxOverrides,
+ this::getLetterboxParentSurface);
+ mLetterbox.attachInput(w);
+ mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+ .setLetterboxInnerBoundsSupplier(mLetterbox::getInnerFrame);
+ }
+ final Point letterboxPosition = new Point();
+ if (mActivityRecord.isInLetterboxAnimation()) {
+ // In this case we attach the letterbox to the task instead of the activity.
+ mActivityRecord.getTask().getPosition(letterboxPosition);
+ } else {
+ mActivityRecord.getPosition(letterboxPosition);
+ }
+
+ // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+ // priority because the activity is launched in a rotated environment. In multi-window
+ // mode, the taskFragment-level represents this for both split-screen
+ // and activity-embedding. In fullscreen-mode, the task container does
+ // (since the orientation letterbox is also applied to the task).
+ final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
+ final Rect spaceToFill = transformedBounds != null
+ ? transformedBounds
+ : mActivityRecord.inMultiWindowMode()
+ ? mActivityRecord.getTaskFragment().getBounds()
+ : mActivityRecord.getRootTask().getParent().getBounds();
+ // In case of translucent activities an option is to use the WindowState#getFrame() of
+ // the first opaque activity beneath. In some cases (e.g. an opaque activity is using
+ // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct
+ // information and in particular it might provide a value for a smaller area making
+ // the letterbox overlap with the translucent activity's frame.
+ // If we use WindowState#getFrame() for the translucent activity's letterbox inner
+ // frame, the letterbox will then be overlapped with the translucent activity's frame.
+ // Because the surface layer of letterbox is lower than an activity window, this
+ // won't crop the content, but it may affect other features that rely on values stored
+ // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher
+ // 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 TransparentPolicy transparentPolicy =
+ mActivityRecord.mAppCompatController.getTransparentPolicy();
+ final Rect innerFrame =
+ transparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame();
+ mLetterbox.layout(spaceToFill, innerFrame, letterboxPosition);
+ if (mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides()
+ .isDoubleTapEvent()) {
+ // We need to notify Shell that letterbox position has changed.
+ mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
+ }
+
+ /**
+ * @return {@code true} if the policy is running and so if the current activity is
+ * letterboxed.
+ */
+ boolean isRunning() {
+ return mLetterbox != null;
+ }
+
+ void onMovedToDisplay(int displayId) {
+ if (isRunning()) {
+ mLetterbox.onMovedToDisplay(displayId);
+ }
+ }
+
+ /** Cleans up {@link Letterbox} if it exists.*/
+ void stop() {
+ if (isRunning()) {
+ mLetterbox.destroy();
+ mLetterbox = null;
+ }
+ mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+ .setLetterboxInnerBoundsSupplier(null);
+ }
+
+ void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint,
+ @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction inputT) {
+ if (shouldNotLayoutLetterbox(winHint)) {
+ return;
+ }
+ start(winHint);
+ if (isRunning() && mLetterbox.needsApplySurfaceChanges()) {
+ mLetterbox.applySurfaceChanges(t, inputT);
+ }
+ }
+
+ void hide() {
+ if (isRunning()) {
+ mLetterbox.hide();
+ }
+ }
+
+ /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
+ @NonNull
+ Rect getLetterboxInsets() {
+ if (isRunning()) {
+ return mLetterbox.getInsets();
+ } else {
+ return new Rect();
+ }
+ }
+
+ /** Gets the inner bounds of letterbox. The bounds will be empty with no letterbox. */
+ void getLetterboxInnerBounds(@NonNull Rect outBounds) {
+ if (isRunning()) {
+ outBounds.set(mLetterbox.getInnerFrame());
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (w != null) {
+ AppCompatUtils.adjustBoundsForTaskbar(w, outBounds);
+ }
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+
+ /** Gets the outer bounds of letterbox. The bounds will be empty with no letterbox. */
+ private void getLetterboxOuterBounds(@NonNull Rect outBounds) {
+ if (isRunning()) {
+ outBounds.set(mLetterbox.getOuterFrame());
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+
+ /**
+ * @return {@code true} if bar shown within a given rectangle is allowed to be fully
+ * transparent when the current activity is displayed.
+ */
+ boolean isFullyTransparentBarAllowed(@NonNull Rect rect) {
+ return !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect);
+ }
+
+ @Nullable
+ LetterboxDetails getLetterboxDetails() {
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (!isRunning() || w == null || w.isLetterboxedForDisplayCutout()) {
+ return null;
+ }
+ final Rect letterboxInnerBounds = new Rect();
+ final Rect letterboxOuterBounds = new Rect();
+ getLetterboxInnerBounds(letterboxInnerBounds);
+ getLetterboxOuterBounds(letterboxOuterBounds);
+
+ if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) {
+ return null;
+ }
+
+ return new LetterboxDetails(
+ letterboxInnerBounds,
+ letterboxOuterBounds,
+ w.mAttrs.insetsFlags.appearance
+ );
+ }
+
+ @Nullable
+ private SurfaceControl getLetterboxParentSurface() {
+ if (mActivityRecord.isInLetterboxAnimation()) {
+ return mActivityRecord.getTask().getSurfaceControl();
+ }
+ return mActivityRecord.getSurfaceControl();
+ }
+
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index 80bbee3..2f03105 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -37,6 +37,8 @@
private final AppCompatResizeOverrides mAppCompatResizeOverrides;
@NonNull
private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
+ @NonNull
+ private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
AppCompatOverrides(@NonNull ActivityRecord activityRecord,
@NonNull AppCompatConfiguration appCompatConfiguration,
@@ -54,6 +56,8 @@
mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord,
appCompatConfiguration, optPropBuilder);
mAppCompatResizeOverrides = new AppCompatResizeOverrides(activityRecord, optPropBuilder);
+ mAppCompatLetterboxOverrides = new AppCompatLetterboxOverrides(activityRecord,
+ appCompatConfiguration);
}
@NonNull
@@ -85,4 +89,9 @@
AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
return mAppCompatReachabilityOverrides;
}
+
+ @NonNull
+ AppCompatLetterboxOverrides getAppCompatLetterboxOverrides() {
+ return mAppCompatLetterboxOverrides;
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
new file mode 100644
index 0000000..077ce00
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
@@ -0,0 +1,143 @@
+/*
+ * 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;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Predicate;
+
+/**
+ * Utility to unify rounded corners in app compat.
+ */
+class AppCompatRoundedCorners {
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final Predicate<WindowState> mIsLetterboxedNotForDisplayCutout;
+
+ AppCompatRoundedCorners(@NonNull ActivityRecord activityRecord,
+ @NonNull Predicate<WindowState> isLetterboxedNotForDisplayCutout) {
+ mActivityRecord = activityRecord;
+ mIsLetterboxedNotForDisplayCutout = isLetterboxedNotForDisplayCutout;
+ }
+
+ void updateRoundedCornersIfNeeded(@NonNull final WindowState mainWindow) {
+ final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
+ if (windowSurface == null || !windowSurface.isValid()) {
+ return;
+ }
+
+ // cropBounds must be non-null for the cornerRadius to be ever applied.
+ mActivityRecord.getSyncTransaction()
+ .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
+ .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
+ }
+
+ @VisibleForTesting
+ @Nullable
+ Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
+ // We don't want corner radius on the window.
+ // In the case the ActivityRecord requires a letterboxed animation we never want
+ // rounded corners on the window because rounded corners are applied at the
+ // animation-bounds surface level and rounded corners on the window would interfere
+ // with that leading to unexpected rounded corner positioning during the animation.
+ return null;
+ }
+
+ final Rect cropBounds = new Rect(mActivityRecord.getBounds());
+
+ // In case of translucent activities we check if the requested size is different from
+ // the size provided using inherited bounds. In that case we decide to not apply rounded
+ // 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.
+ final TransparentPolicy transparentPolicy = mActivityRecord.mAppCompatController
+ .getTransparentPolicy();
+ if (transparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
+ || cropBounds.height() != mainWindow.mRequestedHeight)) {
+ return null;
+ }
+
+ // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
+ // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
+ // are in screen coordinates
+ AppCompatUtils.adjustBoundsForTaskbar(mainWindow, cropBounds);
+
+ final float scale = mainWindow.mInvGlobalScale;
+ if (scale != 1f && scale > 0f) {
+ cropBounds.scale(scale);
+ }
+
+ // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
+ // control is in the top left corner of an app window so offsetting bounds
+ // accordingly.
+ cropBounds.offsetTo(0, 0);
+ return cropBounds;
+ }
+
+ /**
+ * Returns rounded corners radius the letterboxed activity should have based on override in
+ * R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+ * Device corners can be different on the right and left sides, but we use the same radius
+ * for all corners for consistency and pick a minimal bottom one for consistency with a
+ * taskbar rounded corners.
+ *
+ * @param mainWindow The {@link WindowState} to consider for rounded corners calculation.
+ */
+ int getRoundedCornersRadius(@NonNull final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow)) {
+ return 0;
+ }
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ final int radius;
+ if (letterboxOverrides.getLetterboxActivityCornersRadius() >= 0) {
+ radius = letterboxOverrides.getLetterboxActivityCornersRadius();
+ } else {
+ final InsetsState insetsState = mainWindow.getInsetsState();
+ radius = Math.min(
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+ }
+
+ final float scale = mainWindow.mInvGlobalScale;
+ return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
+ }
+
+ private static int getInsetsStateCornerRadius(@NonNull InsetsState insetsState,
+ @RoundedCorner.Position int position) {
+ final RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
+ return corner == null ? 0 : corner.getRadius();
+ }
+
+ private boolean requiresRoundedCorners(@NonNull final WindowState mainWindow) {
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ return mIsLetterboxedNotForDisplayCutout.test(mainWindow)
+ && letterboxOverrides.isLetterboxActivityCornersRounded();
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 0244d27..94ad61f 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -28,13 +28,16 @@
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.WindowInsets;
import java.util.function.BooleanSupplier;
/**
* Utilities for App Compat policies and overrides.
*/
-class AppCompatUtils {
+final class AppCompatUtils {
/**
* Lazy version of a {@link BooleanSupplier} which access an existing BooleanSupplier and
@@ -135,8 +138,8 @@
// Whether the direct top activity is eligible for letterbox education.
appCompatTaskInfo.setEligibleForLetterboxEducation(
isTopActivityResumed && top.isEligibleForLetterboxEducation());
- appCompatTaskInfo.setLetterboxEducationEnabled(top.mLetterboxUiController
- .isLetterboxEducationEnabled());
+ appCompatTaskInfo.setLetterboxEducationEnabled(top.mAppCompatController
+ .getAppCompatLetterboxOverrides().isLetterboxEducationEnabled());
final AppCompatAspectRatioOverrides aspectRatioOverrides =
top.mAppCompatController.getAppCompatAspectRatioOverrides();
@@ -212,6 +215,40 @@
return "UNKNOWN_REASON";
}
+ /**
+ * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
+ */
+ @Nullable
+ static InsetsSource getExpandedTaskbarOrNull(@NonNull final WindowState mainWindow) {
+ final InsetsState state = mainWindow.getInsetsState();
+ for (int i = state.sourceSize() - 1; i >= 0; i--) {
+ final InsetsSource source = state.sourceAt(i);
+ if (source.getType() == WindowInsets.Type.navigationBars()
+ && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)
+ && source.isVisible()) {
+ return source;
+ }
+ }
+ return null;
+ }
+
+ static void adjustBoundsForTaskbar(@NonNull final WindowState mainWindow,
+ @NonNull final Rect bounds) {
+ // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
+ // an insets frame is equal to a navigation bar which shouldn't affect position of
+ // rounded corners since apps are expected to handle navigation bar inset.
+ // This condition checks whether the taskbar is visible.
+ // Do not crop the taskbar inset if the window is in immersive mode - the user can
+ // swipe to show/hide the taskbar as an overlay.
+ // Adjust the bounds only in case there is an expanded taskbar,
+ // otherwise the rounded corners will be shown behind the navbar.
+ final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
+ if (expandedTaskbarOrNull != null) {
+ // Rounded corners should be displayed above the expanded taskbar.
+ bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
+ }
+ }
+
private static void clearAppCompatTaskInfo(@NonNull AppCompatTaskInfo info) {
info.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 06e665e..89e10d36 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -273,8 +273,9 @@
customAppTransition.mBackgroundColor);
}
}
- infoBuilder.setLetterboxColor(currentActivity.mLetterboxUiController
- .getLetterboxBackgroundColor().toArgb());
+ infoBuilder.setLetterboxColor(currentActivity.mAppCompatController
+ .getAppCompatLetterboxOverrides()
+ .getLetterboxBackgroundColor().toArgb());
removedWindowContainer = currentActivity;
prevTask = prevActivities.get(0).getTask();
backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
@@ -761,7 +762,7 @@
if (isMonitorForRemote()) {
mObserver.sendResult(null /* result */);
}
- if (isMonitorAnimationOrTransition()) {
+ if (isMonitorAnimationOrTransition() && canCancelAnimations()) {
clearBackAnimations(true /* cancel */);
}
cancelPendingAnimation();
@@ -2046,11 +2047,22 @@
}
}
+ /** If the open transition is playing, wait for transition to clear the animation */
+ private boolean canCancelAnimations() {
+ if (!Flags.migratePredictiveBackTransition()) {
+ return true;
+ }
+ return mAnimationHandler.mOpenAnimAdaptor == null
+ || mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition == null;
+ }
+
void startAnimation() {
if (!mBackAnimationInProgress) {
// gesture is already finished, do not start animation
if (mPendingAnimation != null) {
- clearBackAnimations(true /* cancel */);
+ if (canCancelAnimations()) {
+ clearBackAnimations(true /* cancel */);
+ }
mPendingAnimation = null;
}
return;
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 2755a80..9b142f28 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -109,10 +109,10 @@
}
@Override
- public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+ public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
@NonNull String cameraId) {
if (!isTreatmentEnabledForActivity(cameraActivity)) {
- return false;
+ return;
}
final int existingCameraCompatMode = cameraActivity.mAppCompatController
.getAppCompatCameraOverrides()
@@ -124,11 +124,9 @@
cameraActivity.mAppCompatController.getAppCompatCameraOverrides()
.setFreeformCameraCompatMode(newCameraCompatMode);
forceUpdateActivityAndTask(cameraActivity);
- return true;
} else {
mIsCameraCompatTreatmentPending = false;
}
- return false;
}
@Override
diff --git a/services/core/java/com/android/server/wm/CameraStateMonitor.java b/services/core/java/com/android/server/wm/CameraStateMonitor.java
index 068fc00..63c90ff 100644
--- a/services/core/java/com/android/server/wm/CameraStateMonitor.java
+++ b/services/core/java/com/android/server/wm/CameraStateMonitor.java
@@ -73,16 +73,6 @@
private final ArrayList<CameraCompatStateListener> mCameraStateListeners = new ArrayList<>();
- /**
- * {@link CameraCompatStateListener} which returned {@code true} on the last {@link
- * CameraCompatStateListener#onCameraOpened(ActivityRecord, String)}, if any.
- *
- * <p>This allows the {@link CameraStateMonitor} to notify a particular listener when camera
- * closes, so they can revert any changes.
- */
- @Nullable
- private CameraCompatStateListener mCurrentListenerForCameraActivity;
-
private final CameraManager.AvailabilityCallback mAvailabilityCallback =
new CameraManager.AvailabilityCallback() {
@Override
@@ -167,12 +157,7 @@
@NonNull String cameraId) {
for (int i = 0; i < mCameraStateListeners.size(); i++) {
CameraCompatStateListener listener = mCameraStateListeners.get(i);
- boolean activeCameraTreatment = listener.onCameraOpened(
- cameraActivity, cameraId);
- if (activeCameraTreatment) {
- mCurrentListenerForCameraActivity = listener;
- break;
- }
+ listener.onCameraOpened(cameraActivity, cameraId);
}
}
@@ -226,19 +211,30 @@
return;
}
- if (mCurrentListenerForCameraActivity != null) {
- boolean closeSuccessful =
- mCurrentListenerForCameraActivity.onCameraClosed(cameraId);
- if (closeSuccessful) {
- mCameraIdPackageBiMapping.removeCameraId(cameraId);
- mCurrentListenerForCameraActivity = null;
- } else {
- rescheduleRemoveCameraActivity(cameraId);
- }
+ final boolean closeSuccessfulForAllListeners = notifyListenersCameraClosed(cameraId);
+ if (closeSuccessfulForAllListeners) {
+ // Finish cleaning up.
+ mCameraIdPackageBiMapping.removeCameraId(cameraId);
+ } else {
+ // Not ready to process closure yet - the camera activity might be refreshing.
+ // Try again later.
+ rescheduleRemoveCameraActivity(cameraId);
}
}
}
+ /**
+ * @return {@code false} if any listeners have reported issues processing the close.
+ */
+ private boolean notifyListenersCameraClosed(@NonNull String cameraId) {
+ boolean closeSuccessfulForAllListeners = true;
+ for (int i = 0; i < mCameraStateListeners.size(); i++) {
+ closeSuccessfulForAllListeners &= mCameraStateListeners.get(i).onCameraClosed(cameraId);
+ }
+
+ return closeSuccessfulForAllListeners;
+ }
+
// TODO(b/335165310): verify that this works in multi instance and permission dialogs.
/**
* Finds a visible activity with the given package name.
@@ -286,11 +282,9 @@
interface CameraCompatStateListener {
/**
* Notifies the compat listener that an activity has opened camera.
- *
- * @return true if the treatment has been applied.
*/
// TODO(b/336474959): try to decouple `cameraId` from the listeners.
- boolean onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId);
+ void onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId);
/**
* Notifies the compat listener that camera is closed.
*
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c3339cd..745b792 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1596,7 +1596,7 @@
final ActivityRecord currentActivity = win.getActivityRecord();
if (currentActivity != null) {
final LetterboxDetails currentLetterboxDetails = currentActivity
- .mLetterboxUiController.getLetterboxDetails();
+ .mAppCompatController.getAppCompatLetterboxPolicy().getLetterboxDetails();
if (currentLetterboxDetails != null) {
mLetterboxDetails.add(currentLetterboxDetails);
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index e50a089..762180b 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -298,7 +298,7 @@
}
@Override
- public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+ public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
@NonNull String cameraId) {
mCameraTask = cameraActivity.getTask();
// Checking whether an activity in fullscreen rather than the task as this camera
@@ -306,7 +306,7 @@
if (cameraActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
recomputeConfigurationForCameraCompatIfNeeded(cameraActivity);
mDisplayContent.updateOrientation();
- return true;
+ return;
}
// Checking that the whole app is in multi-window mode as we shouldn't show toast
// for the activity embedding case.
@@ -320,7 +320,6 @@
(String) packageManager.getApplicationLabel(
packageManager.getApplicationInfo(cameraActivity.packageName,
/* flags */ 0)));
- return true;
} catch (PackageManager.NameNotFoundException e) {
ProtoLog.e(WM_DEBUG_ORIENTATION,
"DisplayRotationCompatPolicy: Multi-window toast not shown as "
@@ -328,7 +327,6 @@
cameraActivity.packageName);
}
}
- return false;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 7a95c2d..2f0ee17 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -129,21 +129,33 @@
@WindowConfiguration.WindowingMode
private int getWindowingModeLocked(@NonNull SettingsProvider.SettingsEntry settings,
@NonNull DisplayContent dc) {
- int windowingMode = settings.mWindowingMode;
+ final int windowingModeFromDisplaySettings = settings.mWindowingMode;
// This display used to be in freeform, but we don't support freeform anymore, so fall
// back to fullscreen.
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
+ if (windowingModeFromDisplaySettings == WindowConfiguration.WINDOWING_MODE_FREEFORM
&& !mService.mAtmService.mSupportsFreeformWindowManagement) {
return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
}
- // No record is present so use default windowing mode policy.
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement
- && (mService.mIsPc || dc.forceDesktopMode())
- ? WindowConfiguration.WINDOWING_MODE_FREEFORM
- : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ if (windowingModeFromDisplaySettings != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ return windowingModeFromDisplaySettings;
}
- return windowingMode;
+ // No record is present so use default windowing mode policy.
+ final boolean forceFreeForm = mService.mAtmService.mSupportsFreeformWindowManagement
+ && (mService.mIsPc || dc.forceDesktopMode());
+ if (forceFreeForm) {
+ return WindowConfiguration.WINDOWING_MODE_FREEFORM;
+ }
+ final int currentWindowingMode = dc.getDefaultTaskDisplayArea().getWindowingMode();
+ if (currentWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+ // No record preset in settings + no mode set via the display area policy.
+ // Move to fullscreen as a fallback.
+ return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ }
+ if (currentWindowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
+ // Freeform was enabled before but disabled now, the TDA should now move to fullscreen.
+ return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ }
+ return currentWindowingMode;
}
@WindowConfiguration.WindowingMode
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e18ca85..5d8a96c 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -51,6 +51,7 @@
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
@@ -619,7 +620,8 @@
}
state.mKeyguardGoingAway = false;
state.writeEventLog("goingAwayTimeout");
- mWindowManager.mPolicy.startKeyguardExitAnimation(0);
+ mWindowManager.mPolicy.startKeyguardExitAnimation(
+ SystemClock.uptimeMillis() - GOING_AWAY_TIMEOUT_MS);
}
};
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3fc5eaf..252590e 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -38,9 +38,6 @@
import com.android.server.UiThread;
-import java.util.function.BooleanSupplier;
-import java.util.function.DoubleSupplier;
-import java.util.function.IntSupplier;
import java.util.function.Supplier;
/**
@@ -54,12 +51,6 @@
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
- private final BooleanSupplier mAreCornersRounded;
- private final Supplier<Color> mColorSupplier;
- // Parameters for "blurred wallpaper" letterbox background.
- private final BooleanSupplier mHasWallpaperBackgroundSupplier;
- private final IntSupplier mBlurRadiusSupplier;
- private final DoubleSupplier mDarkScrimAlphaSupplier;
private final Supplier<SurfaceControl> mParentSurfaceSupplier;
private final Rect mOuter = new Rect();
@@ -77,6 +68,8 @@
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
@NonNull
private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
+ @NonNull
+ private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
/**
* Constructs a Letterbox.
@@ -85,24 +78,14 @@
*/
public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- BooleanSupplier areCornersRounded,
- Supplier<Color> colorSupplier,
- BooleanSupplier hasWallpaperBackgroundSupplier,
- IntSupplier blurRadiusSupplier,
- DoubleSupplier darkScrimAlphaSupplier,
@NonNull AppCompatReachabilityPolicy appCompatReachabilityPolicy,
+ @NonNull AppCompatLetterboxOverrides appCompatLetterboxOverrides,
Supplier<SurfaceControl> parentSurface) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
- mAreCornersRounded = areCornersRounded;
- mColorSupplier = colorSupplier;
- mHasWallpaperBackgroundSupplier = hasWallpaperBackgroundSupplier;
- mBlurRadiusSupplier = blurRadiusSupplier;
- mDarkScrimAlphaSupplier = darkScrimAlphaSupplier;
mAppCompatReachabilityPolicy = appCompatReachabilityPolicy;
+ mAppCompatLetterboxOverrides = appCompatLetterboxOverrides;
mParentSurfaceSupplier = parentSurface;
- // TODO Remove after Letterbox refactoring.
- mAppCompatReachabilityPolicy.setLetterboxInnerBoundsSupplier(this::getInnerFrame);
}
/**
@@ -252,7 +235,8 @@
* Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}.
*/
private boolean useFullWindowSurface() {
- return mAreCornersRounded.getAsBoolean() || mHasWallpaperBackgroundSupplier.getAsBoolean();
+ return mAppCompatLetterboxOverrides.shouldLetterboxHaveRoundedCorners()
+ || mAppCompatLetterboxOverrides.hasWallpaperBackgroundForLetterbox();
}
private final class TapEventReceiver extends InputEventReceiver {
@@ -431,7 +415,7 @@
createSurface(t);
}
- mColor = mColorSupplier.get();
+ mColor = mAppCompatLetterboxOverrides.getLetterboxBackgroundColor();
mParentSurface = mParentSurfaceSupplier.get();
t.setColor(mSurface, getRgbColorArray());
t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
@@ -439,7 +423,8 @@
mSurfaceFrameRelative.height());
t.reparent(mSurface, mParentSurface);
- mHasWallpaperBackground = mHasWallpaperBackgroundSupplier.getAsBoolean();
+ mHasWallpaperBackground = mAppCompatLetterboxOverrides
+ .hasWallpaperBackgroundForLetterbox();
updateAlphaAndBlur(t);
t.show(mSurface);
@@ -460,17 +445,19 @@
t.setBackgroundBlurRadius(mSurface, 0);
return;
}
- final float alpha = (float) mDarkScrimAlphaSupplier.getAsDouble();
+ final float alpha = mAppCompatLetterboxOverrides.getLetterboxWallpaperDarkScrimAlpha();
t.setAlpha(mSurface, alpha);
// Translucent dark scrim can be shown without blur.
- if (mBlurRadiusSupplier.getAsInt() <= 0) {
+ final int blurRadiusPx = mAppCompatLetterboxOverrides
+ .getLetterboxWallpaperBlurRadiusPx();
+ if (blurRadiusPx <= 0) {
// Removing pre-exesting blur
t.setBackgroundBlurRadius(mSurface, 0);
return;
}
- t.setBackgroundBlurRadius(mSurface, mBlurRadiusSupplier.getAsInt());
+ t.setBackgroundBlurRadius(mSurface, blurRadiusPx);
}
private float[] getRgbColorArray() {
@@ -487,8 +474,9 @@
// and mParentSurface may never be updated in applySurfaceChanges but this
// doesn't mean that update is needed.
|| !mSurfaceFrameRelative.isEmpty()
- && (mHasWallpaperBackgroundSupplier.getAsBoolean() != mHasWallpaperBackground
- || !mColorSupplier.get().equals(mColor)
+ && (mAppCompatLetterboxOverrides.hasWallpaperBackgroundForLetterbox()
+ != mHasWallpaperBackground
+ || !mAppCompatLetterboxOverrides.getLetterboxBackgroundColor().equals(mColor)
|| mParentSurfaceSupplier.get() != mParentSurface);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 4740fc4..0e8291e 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -16,36 +16,10 @@
package com.android.server.wm;
-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 com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager.TaskDescription;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Slog;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.RoundedCorner;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.LetterboxDetails;
-import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
import java.io.PrintWriter;
@@ -54,10 +28,6 @@
// SizeCompatTests and LetterboxTests but not all.
final class LetterboxUiController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
-
- private final Point mTmpPoint = new Point();
-
private final AppCompatConfiguration mAppCompatConfiguration;
private final ActivityRecord mActivityRecord;
@@ -66,18 +36,9 @@
@NonNull
private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
@NonNull
- private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
+ private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
@NonNull
- private final TransparentPolicy mTransparentPolicy;
- @NonNull
- private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
-
- private boolean mShowWallpaperForLetterboxBackground;
-
- @Nullable
- private Letterbox mLetterbox;
-
- private boolean mLastShouldShowLetterboxUi;
+ private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
mAppCompatConfiguration = wmService.mAppCompatConfiguration;
@@ -88,402 +49,10 @@
// TODO(b/356385137): Remove these we added to make dependencies temporarily explicit.
mAppCompatReachabilityOverrides = mActivityRecord.mAppCompatController
.getAppCompatReachabilityOverrides();
- mAppCompatReachabilityPolicy = mActivityRecord.mAppCompatController
- .getAppCompatReachabilityPolicy();
- mTransparentPolicy = mActivityRecord.mAppCompatController.getTransparentPolicy();
- mAppCompatOrientationOverrides = mActivityRecord.mAppCompatController
- .getAppCompatOrientationOverrides();
-
- }
-
- /** Cleans up {@link Letterbox} if it exists.*/
- void destroy() {
- if (mLetterbox != null) {
- mLetterbox.destroy();
- mLetterbox = null;
- // TODO Remove after Letterbox refactoring.
- mAppCompatReachabilityPolicy.setLetterboxInnerBoundsSupplier(null);
- }
- }
-
- void onMovedToDisplay(int displayId) {
- if (mLetterbox != null) {
- mLetterbox.onMovedToDisplay(displayId);
- }
- }
-
- boolean hasWallpaperBackgroundForLetterbox() {
- return mShowWallpaperForLetterboxBackground;
- }
-
- /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
- Rect getLetterboxInsets() {
- if (mLetterbox != null) {
- return mLetterbox.getInsets();
- } else {
- return new Rect();
- }
- }
-
- /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
- void getLetterboxInnerBounds(Rect outBounds) {
- if (mLetterbox != null) {
- outBounds.set(mLetterbox.getInnerFrame());
- final WindowState w = mActivityRecord.findMainWindow();
- if (w != null) {
- adjustBoundsForTaskbar(w, outBounds);
- }
- } else {
- outBounds.setEmpty();
- }
- }
-
- /** Gets the outer bounds of letterbox. The bounds will be empty if there is no letterbox. */
- private void getLetterboxOuterBounds(Rect outBounds) {
- if (mLetterbox != null) {
- outBounds.set(mLetterbox.getOuterFrame());
- } else {
- outBounds.setEmpty();
- }
- }
-
- /**
- * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
- * when the current activity is displayed.
- */
- boolean isFullyTransparentBarAllowed(Rect rect) {
- return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
- }
-
- void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
- updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction(),
- mActivityRecord.getPendingTransaction());
- }
-
- void updateLetterboxSurfaceIfNeeded(WindowState winHint, @NonNull Transaction t,
- @NonNull Transaction inputT) {
- if (shouldNotLayoutLetterbox(winHint)) {
- return;
- }
- layoutLetterboxIfNeeded(winHint);
- if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(t, inputT);
- }
- }
-
- void layoutLetterboxIfNeeded(WindowState w) {
- if (shouldNotLayoutLetterbox(w)) {
- return;
- }
- updateRoundedCornersIfNeeded(w);
- updateWallpaperForLetterbox(w);
- if (shouldShowLetterboxUi(w)) {
- if (mLetterbox == null) {
- mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
- mActivityRecord.mWmService.mTransactionFactory,
- this::shouldLetterboxHaveRoundedCorners,
- this::getLetterboxBackgroundColor,
- this::hasWallpaperBackgroundForLetterbox,
- this::getLetterboxWallpaperBlurRadiusPx,
- this::getLetterboxWallpaperDarkScrimAlpha,
- mAppCompatReachabilityPolicy,
- this::getLetterboxParentSurface);
- mLetterbox.attachInput(w);
- }
-
- if (mActivityRecord.isInLetterboxAnimation()) {
- // In this case we attach the letterbox to the task instead of the activity.
- mActivityRecord.getTask().getPosition(mTmpPoint);
- } else {
- mActivityRecord.getPosition(mTmpPoint);
- }
-
- // Get the bounds of the "space-to-fill". The transformed bounds have the highest
- // priority because the activity is launched in a rotated environment. In multi-window
- // mode, the taskFragment-level represents this for both split-screen
- // and activity-embedding. In fullscreen-mode, the task container does
- // (since the orientation letterbox is also applied to the task).
- final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
- final Rect spaceToFill = transformedBounds != null
- ? transformedBounds
- : mActivityRecord.inMultiWindowMode()
- ? mActivityRecord.getTaskFragment().getBounds()
- : mActivityRecord.getRootTask().getParent().getBounds();
- // In case of translucent activities an option is to use the WindowState#getFrame() of
- // the first opaque activity beneath. In some cases (e.g. an opaque activity is using
- // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct
- // information and in particular it might provide a value for a smaller area making
- // the letterbox overlap with the translucent activity's frame.
- // If we use WindowState#getFrame() for the translucent activity's letterbox inner
- // frame, the letterbox will then be overlapped with the translucent activity's frame.
- // Because the surface layer of letterbox is lower than an activity window, this
- // won't crop the content, but it may affect other features that rely on values stored
- // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher
- // 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 =
- mTransparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame();
- mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
- if (mAppCompatReachabilityOverrides.isDoubleTapEvent()) {
- // We need to notify Shell that letterbox position has changed.
- mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
- }
- } else if (mLetterbox != null) {
- mLetterbox.hide();
- }
- }
-
- SurfaceControl getLetterboxParentSurface() {
- if (mActivityRecord.isInLetterboxAnimation()) {
- return mActivityRecord.getTask().getSurfaceControl();
- }
- return mActivityRecord.getSurfaceControl();
- }
-
- private static boolean shouldNotLayoutLetterbox(WindowState w) {
- if (w == null) {
- return true;
- }
- final int type = w.mAttrs.type;
- // Allow letterbox to be displayed early for base application or application starting
- // windows even if it is not on the top z order to prevent flickering when the
- // letterboxed window is brought to the top
- return (type != TYPE_BASE_APPLICATION && type != TYPE_APPLICATION_STARTING)
- || w.mAnimatingExit;
- }
-
- private boolean shouldLetterboxHaveRoundedCorners() {
- // TODO(b/214030873): remove once background is drawn for transparent activities
- // Letterbox shouldn't have rounded corners if the activity is transparent
- return mAppCompatConfiguration.isLetterboxActivityCornersRounded()
- && mActivityRecord.fillsParent();
- }
-
- boolean isLetterboxEducationEnabled() {
- return mAppCompatConfiguration.getIsEducationEnabled();
- }
-
- @VisibleForTesting
- boolean shouldShowLetterboxUi(WindowState mainWindow) {
- if (mAppCompatOrientationOverrides.getIsRelaunchingAfterRequestedOrientationChanged()) {
- return mLastShouldShowLetterboxUi;
- }
-
- final boolean shouldShowLetterboxUi =
- (mActivityRecord.isInLetterboxAnimation() || mActivityRecord.isVisible()
- || mActivityRecord.isVisibleRequested())
- && mainWindow.areAppWindowBoundsLetterboxed()
- // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
- // WindowContainer#showWallpaper because the later will return true when this
- // activity is using blurred wallpaper for letterbox background.
- && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
-
- mLastShouldShowLetterboxUi = shouldShowLetterboxUi;
-
- return shouldShowLetterboxUi;
- }
-
- Color getLetterboxBackgroundColor() {
- final WindowState w = mActivityRecord.findMainWindow();
- if (w == null || w.isLetterboxedForDisplayCutout()) {
- return Color.valueOf(Color.BLACK);
- }
- @LetterboxBackgroundType int letterboxBackgroundType =
- mAppCompatConfiguration.getLetterboxBackgroundType();
- TaskDescription taskDescription = mActivityRecord.taskDescription;
- switch (letterboxBackgroundType) {
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
- if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
- return Color.valueOf(taskDescription.getBackgroundColorFloating());
- }
- break;
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
- if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
- return Color.valueOf(taskDescription.getBackgroundColor());
- }
- break;
- case LETTERBOX_BACKGROUND_WALLPAPER:
- if (hasWallpaperBackgroundForLetterbox()) {
- // Color is used for translucent scrim that dims wallpaper.
- return mAppCompatConfiguration.getLetterboxBackgroundColor();
- }
- Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
- + "blur is not supported by a device or not supported in the current "
- + "window configuration or both alpha scrim and blur radius aren't "
- + "provided so using solid color background");
- break;
- case LETTERBOX_BACKGROUND_SOLID_COLOR:
- return mAppCompatConfiguration.getLetterboxBackgroundColor();
- default:
- throw new AssertionError(
- "Unexpected letterbox background type: " + letterboxBackgroundType);
- }
- // If picked option configured incorrectly or not supported then default to a solid color
- // background.
- return mAppCompatConfiguration.getLetterboxBackgroundColor();
- }
-
- private void updateRoundedCornersIfNeeded(final WindowState mainWindow) {
- final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
- if (windowSurface == null || !windowSurface.isValid()) {
- return;
- }
-
- // cropBounds must be non-null for the cornerRadius to be ever applied.
- mActivityRecord.getSyncTransaction()
- .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
- .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
- }
-
- @VisibleForTesting
- @Nullable
- Rect getCropBoundsIfNeeded(final WindowState mainWindow) {
- if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
- // We don't want corner radius on the window.
- // In the case the ActivityRecord requires a letterboxed animation we never want
- // rounded corners on the window because rounded corners are applied at the
- // animation-bounds surface level and rounded corners on the window would interfere
- // with that leading to unexpected rounded corner positioning during the animation.
- return null;
- }
-
- final Rect cropBounds = new Rect(mActivityRecord.getBounds());
-
- // In case of translucent activities we check if the requested size is different from
- // the size provided using inherited bounds. In that case we decide to not apply rounded
- // 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 (mTransparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
- || cropBounds.height() != mainWindow.mRequestedHeight)) {
- return null;
- }
-
- // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
- // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
- // are in screen coordinates
- adjustBoundsForTaskbar(mainWindow, cropBounds);
-
- final float scale = mainWindow.mInvGlobalScale;
- if (scale != 1f && scale > 0f) {
- cropBounds.scale(scale);
- }
-
- // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
- // control is in the top left corner of an app window so offsetting bounds
- // accordingly.
- cropBounds.offsetTo(0, 0);
- return cropBounds;
- }
-
- private boolean requiresRoundedCorners(final WindowState mainWindow) {
- return isLetterboxedNotForDisplayCutout(mainWindow)
- && mAppCompatConfiguration.isLetterboxActivityCornersRounded();
- }
-
- // Returns rounded corners radius the letterboxed activity should have based on override in
- // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
- // Device corners can be different on the right and left sides, but we use the same radius
- // for all corners for consistency and pick a minimal bottom one for consistency with a
- // taskbar rounded corners.
- int getRoundedCornersRadius(final WindowState mainWindow) {
- if (!requiresRoundedCorners(mainWindow)) {
- return 0;
- }
-
- final int radius;
- if (mAppCompatConfiguration.getLetterboxActivityCornersRadius() >= 0) {
- radius = mAppCompatConfiguration.getLetterboxActivityCornersRadius();
- } else {
- final InsetsState insetsState = mainWindow.getInsetsState();
- radius = Math.min(
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
- }
-
- final float scale = mainWindow.mInvGlobalScale;
- return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
- }
-
- /**
- * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
- */
- @VisibleForTesting
- @Nullable
- InsetsSource getExpandedTaskbarOrNull(final WindowState mainWindow) {
- final InsetsState state = mainWindow.getInsetsState();
- for (int i = state.sourceSize() - 1; i >= 0; i--) {
- final InsetsSource source = state.sourceAt(i);
- if (source.getType() == WindowInsets.Type.navigationBars()
- && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)
- && source.isVisible()) {
- return source;
- }
- }
- return null;
- }
-
- private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) {
- // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
- // an insets frame is equal to a navigation bar which shouldn't affect position of
- // rounded corners since apps are expected to handle navigation bar inset.
- // This condition checks whether the taskbar is visible.
- // Do not crop the taskbar inset if the window is in immersive mode - the user can
- // swipe to show/hide the taskbar as an overlay.
- // Adjust the bounds only in case there is an expanded taskbar,
- // otherwise the rounded corners will be shown behind the navbar.
- final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
- if (expandedTaskbarOrNull != null) {
- // Rounded corners should be displayed above the expanded taskbar.
- bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
- }
- }
-
- private int getInsetsStateCornerRadius(
- InsetsState insetsState, @RoundedCorner.Position int position) {
- RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
- return corner == null ? 0 : corner.getRadius();
- }
-
- private boolean isLetterboxedNotForDisplayCutout(WindowState mainWindow) {
- return shouldShowLetterboxUi(mainWindow)
- && !mainWindow.isLetterboxedForDisplayCutout();
- }
-
- private void updateWallpaperForLetterbox(WindowState mainWindow) {
- @LetterboxBackgroundType int letterboxBackgroundType =
- mAppCompatConfiguration.getLetterboxBackgroundType();
- boolean wallpaperShouldBeShown =
- letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
- // Don't use wallpaper as a background if letterboxed for display cutout.
- && isLetterboxedNotForDisplayCutout(mainWindow)
- // Check that dark scrim alpha or blur radius are provided
- && (getLetterboxWallpaperBlurRadiusPx() > 0
- || getLetterboxWallpaperDarkScrimAlpha() > 0)
- // Check that blur is supported by a device if blur radius is provided.
- && (getLetterboxWallpaperBlurRadiusPx() <= 0
- || isLetterboxWallpaperBlurSupported());
- if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
- mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
- mActivityRecord.requestUpdateWallpaperIfNeeded();
- }
- }
-
- private int getLetterboxWallpaperBlurRadiusPx() {
- int blurRadius = mAppCompatConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx();
- return Math.max(blurRadius, 0);
- }
-
- private float getLetterboxWallpaperDarkScrimAlpha() {
- float alpha = mAppCompatConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
- // No scrim by default.
- return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
- }
-
- private boolean isLetterboxWallpaperBlurSupported() {
- return mAppCompatConfiguration.mContext.getSystemService(WindowManager.class)
- .isCrossWindowBlurEnabled();
+ mAppCompatLetterboxPolicy = mActivityRecord.mAppCompatController
+ .getAppCompatLetterboxPolicy();
+ mAppCompatLetterboxOverrides = mActivityRecord.mAppCompatController
+ .getAppCompatLetterboxOverrides();
}
void dump(PrintWriter pw, String prefix) {
@@ -506,7 +75,7 @@
pw.println(prefix + " activityAspectRatio="
+ AppCompatUtils.computeAspectRatio(mActivityRecord.getBounds()));
- boolean shouldShowLetterboxUi = shouldShowLetterboxUi(mainWin);
+ boolean shouldShowLetterboxUi = mAppCompatLetterboxPolicy.shouldShowLetterboxUi(mainWin);
pw.println(prefix + "shouldShowLetterboxUi=" + shouldShowLetterboxUi);
if (!shouldShowLetterboxUi) {
@@ -517,20 +86,20 @@
pw.println(prefix + " isHorizontalThinLetterboxed="
+ mAppCompatReachabilityOverrides.isHorizontalThinLetterboxed());
pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString(
- getLetterboxBackgroundColor().toArgb()));
+ mAppCompatLetterboxOverrides.getLetterboxBackgroundColor().toArgb()));
pw.println(prefix + " letterboxBackgroundType="
+ letterboxBackgroundTypeToString(
mAppCompatConfiguration.getLetterboxBackgroundType()));
pw.println(prefix + " letterboxCornerRadius="
- + getRoundedCornersRadius(mainWin));
+ + mAppCompatLetterboxPolicy.getRoundedCornersRadius(mainWin));
if (mAppCompatConfiguration.getLetterboxBackgroundType()
== LETTERBOX_BACKGROUND_WALLPAPER) {
pw.println(prefix + " isLetterboxWallpaperBlurSupported="
- + isLetterboxWallpaperBlurSupported());
+ + mAppCompatLetterboxOverrides.isLetterboxWallpaperBlurSupported());
pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha="
- + getLetterboxWallpaperDarkScrimAlpha());
+ + mAppCompatLetterboxOverrides.getLetterboxWallpaperDarkScrimAlpha());
pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
- + getLetterboxWallpaperBlurRadiusPx());
+ + mAppCompatLetterboxOverrides.getLetterboxWallpaperBlurRadiusPx());
}
final AppCompatReachabilityOverrides reachabilityOverrides = mActivityRecord
.mAppCompatController.getAppCompatReachabilityOverrides();
@@ -560,26 +129,4 @@
+ mAppCompatConfiguration
.getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
}
-
- @Nullable
- LetterboxDetails getLetterboxDetails() {
- final WindowState w = mActivityRecord.findMainWindow();
- if (mLetterbox == null || w == null || w.isLetterboxedForDisplayCutout()) {
- return null;
- }
- Rect letterboxInnerBounds = new Rect();
- Rect letterboxOuterBounds = new Rect();
- getLetterboxInnerBounds(letterboxInnerBounds);
- getLetterboxOuterBounds(letterboxOuterBounds);
-
- if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) {
- return null;
- }
-
- return new LetterboxDetails(
- letterboxInnerBounds,
- letterboxOuterBounds,
- w.mAttrs.insetsFlags.appearance
- );
- }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d3df5fd..12e91ad 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3507,7 +3507,7 @@
| StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS);
if ((info.startingWindowTypeParameter
& StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) {
- final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION);
+ final WindowState topMainWin = getTopFullscreenMainWindow();
if (topMainWin != null) {
info.mainWindowLayoutParams = topMainWin.getAttrs();
info.requestedVisibleTypes = topMainWin.getRequestedVisibleTypes();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index ed0dc3b..2ee157f 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -478,6 +478,10 @@
mTaskFragmentOrganizerProcessName = processName;
}
+ void onTaskFragmentOrganizerRestarted(@NonNull ITaskFragmentOrganizer organizer) {
+ mTaskFragmentOrganizer = organizer;
+ }
+
void onTaskFragmentOrganizerRemoved() {
mTaskFragmentOrganizer = null;
}
@@ -2960,7 +2964,13 @@
}
boolean shouldRemoveSelfOnLastChildRemoval() {
- return !mCreatedByOrganizer || mIsRemovalRequested;
+ if (!mCreatedByOrganizer || mIsRemovalRequested) {
+ return true;
+ }
+ // The TaskFragmentOrganizer may be killed while the embedded TaskFragments remains in WM
+ // core. The embedded TaskFragment should still be removed when the child activities are
+ // all gone (like package force-stopped).
+ return mIsEmbedded && mTaskFragmentOrganizer == null;
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 561ff7d..e4a3176 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -86,6 +86,12 @@
private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
new ArrayMap<>();
/**
+ * The cached {@link TaskFragmentOrganizerState} for the {@link ITaskFragmentOrganizer} that
+ * have no running process.
+ */
+ private final ArrayList<TaskFragmentOrganizerState> mCachedTaskFragmentOrganizerStates =
+ new ArrayList<>();
+ /**
* Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
*/
private final ArrayMap<IBinder, List<PendingTaskFragmentEvent>> mPendingTaskFragmentEvents =
@@ -105,11 +111,16 @@
* {@link TaskFragment TaskFragments}.
*/
private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
+ @NonNull
private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
- private final IApplicationThread mAppThread;
- private final ITaskFragmentOrganizer mOrganizer;
- private final int mOrganizerPid;
private final int mOrganizerUid;
+ @NonNull
+ private IApplicationThread mAppThread;
+ @NonNull
+ private ITaskFragmentOrganizer mOrganizer;
+ private int mOrganizerPid;
+ @Nullable
+ private Bundle mSavedState;
/**
* Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
@@ -182,6 +193,24 @@
}
}
+ void restore(@NonNull ITaskFragmentOrganizer organizer, int pid) {
+ mOrganizer = organizer;
+ mOrganizerPid = pid;
+ mAppThread = getAppThread(pid, mOrganizerUid);
+ for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
+ mOrganizedTaskFragments.get(i).onTaskFragmentOrganizerRestarted(organizer);
+ }
+ try {
+ mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "TaskFragmentOrganizer failed to register death recipient");
+ }
+ }
+
+ boolean hasSavedState() {
+ return mSavedState != null && !mSavedState.isEmpty();
+ }
+
/**
* @return {@code true} if taskFragment is organized and not sent the appeared event before.
*/
@@ -226,12 +255,18 @@
containsNonEmbeddedActivity ? null : task,
null /* remoteTransition */, null /* displayChange */);
}
+
// Defer to avoid unnecessary layout when there are multiple TaskFragments removal.
mAtmService.deferWindowLayout();
try {
- while (!mOrganizedTaskFragments.isEmpty()) {
- final TaskFragment taskFragment = mOrganizedTaskFragments.remove(0);
- taskFragment.removeImmediately();
+ // Removes the TaskFragments if no saved state or is empty.
+ final boolean haveSavedState = hasSavedState();
+ for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
+ final TaskFragment taskFragment = mOrganizedTaskFragments.get(i);
+ if (!haveSavedState || !taskFragment.hasChild()) {
+ mOrganizedTaskFragments.remove(i);
+ taskFragment.removeImmediately();
+ }
}
} finally {
mAtmService.continueWindowLayout();
@@ -407,8 +442,7 @@
change.setTaskFragmentToken(lastParentTfToken);
}
// Only pass the activity token to the client if it belongs to the same process.
- if (Flags.fixPipRestoreToOverlay() && nextFillTaskActivity != null
- && nextFillTaskActivity.getPid() == mOrganizerPid) {
+ if (nextFillTaskActivity != null && nextFillTaskActivity.getPid() == mOrganizerPid) {
change.setOtherActivityToken(nextFillTaskActivity.token);
}
return change;
@@ -479,15 +513,15 @@
}
@Override
- public void registerOrganizer(
- @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
- registerOrganizerInternal(
- organizer,
- Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer);
+ public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer,
+ boolean isSystemOrganizer, @NonNull Bundle outSavedState) {
+ registerOrganizerInternal(organizer,
+ Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer, outSavedState);
}
private void registerOrganizerInternal(
- @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
+ @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer,
+ @NonNull Bundle outSavedState) {
if (isSystemOrganizer) {
enforceTaskPermission("registerSystemOrganizer()");
}
@@ -497,16 +531,49 @@
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Register task fragment organizer=%s uid=%d pid=%d",
organizer.asBinder(), uid, pid);
+
if (isOrganizerRegistered(organizer)) {
throw new IllegalStateException(
"Replacing existing organizer currently unsupported");
}
+
+ if (restoreFromCachedStateIfPossible(organizer, pid, uid, outSavedState)) {
+ return;
+ }
+
mTaskFragmentOrganizerState.put(organizer.asBinder(),
new TaskFragmentOrganizerState(organizer, pid, uid, isSystemOrganizer));
mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
}
}
+ private boolean restoreFromCachedStateIfPossible(@NonNull ITaskFragmentOrganizer organizer,
+ int pid, int uid, @NonNull Bundle outSavedState) {
+ if (!Flags.aeBackStackRestore()) {
+ return false;
+ }
+
+ TaskFragmentOrganizerState cachedState = null;
+ int i = mCachedTaskFragmentOrganizerStates.size() - 1;
+ for (; i >= 0; i--) {
+ final TaskFragmentOrganizerState state = mCachedTaskFragmentOrganizerStates.get(i);
+ if (state.mOrganizerUid == uid) {
+ cachedState = state;
+ break;
+ }
+ }
+ if (cachedState == null) {
+ return false;
+ }
+
+ mCachedTaskFragmentOrganizerStates.remove(cachedState);
+ outSavedState.putAll(cachedState.mSavedState);
+ cachedState.restore(organizer, pid);
+ mTaskFragmentOrganizerState.put(organizer.asBinder(), cachedState);
+ mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
+ return true;
+ }
+
@Override
public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final int pid = Binder.getCallingPid();
@@ -525,6 +592,23 @@
}
@Override
+ public void setSavedState(@NonNull ITaskFragmentOrganizer organizer, @Nullable Bundle state) {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set state for organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ final TaskFragmentOrganizerState organizerState = mTaskFragmentOrganizerState.get(
+ organizer.asBinder());
+ if (organizerState == null) {
+ throw new IllegalStateException("The organizer hasn't been registered.");
+ }
+
+ organizerState.mSavedState = state;
+ }
+ }
+
+ @Override
public void onTransactionHandled(@NonNull IBinder transactionToken,
@NonNull WindowContainerTransaction wct,
@WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
@@ -769,9 +853,11 @@
// Remove any pending event of this organizer first because state.dispose() may trigger
// event dispatch as result of surface placement.
mPendingTaskFragmentEvents.remove(organizer.asBinder());
- // remove all of the children of the organized TaskFragment
state.dispose(reason);
mTaskFragmentOrganizerState.remove(organizer.asBinder());
+ if (state.hasSavedState()) {
+ mCachedTaskFragmentOrganizerStates.add(state);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 2c5beda..9ee1fbe 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -684,7 +684,8 @@
removalInfo.playRevealAnimation = false;
} else if (removalInfo.playRevealAnimation && playShiftUpAnimation) {
removalInfo.roundedCornerRadius =
- topActivity.mLetterboxUiController.getRoundedCornersRadius(mainWindow);
+ topActivity.mAppCompatController.getAppCompatLetterboxPolicy()
+ .getRoundedCornersRadius(mainWindow);
removalInfo.windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
removalInfo.mainFrame = new Rect(mainWindow.getFrame());
removalInfo.mainFrame.offsetTo(mainWindow.mSurfacePosition.x,
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6ea1f3a..db95d96 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -177,8 +177,8 @@
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
mFindResults.setWallpaperTarget(w);
return true;
- } else if (hasWallpaper && w.isOnScreen()
- && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
+ } else if (hasWallpaper
+ && (w.mActivityRecord != null ? w.isOnScreen() : w.isReadyForDisplay())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
mFindResults.setWallpaperTarget(w);
mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 34b9913..2c58c61 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -97,10 +97,10 @@
/**
* @return If a window animation has outsets applied to it.
- * @see Animation#hasExtension()
+ * @see Animation#getExtensionEdges()
*/
public boolean hasExtension() {
- return mAnimation.hasExtension();
+ return mAnimation.getExtensionEdges() != 0;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 092a751..06d8c37 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -283,24 +283,28 @@
}
private int runDisplayDensity(PrintWriter pw) throws RemoteException {
- String densityStr = getNextArg();
+ String densityStr = null;
String arg = getNextArg();
int density;
int displayId = Display.DEFAULT_DISPLAY;
- if ("-d".equals(densityStr) && arg != null) {
+ if (!"-d".equals(arg) && !"-u".equals(arg)) {
+ densityStr = arg;
+ arg = getNextArg();
+ }
+ if ("-d".equals(arg)) {
+ arg = getNextArg();
try {
displayId = Integer.parseInt(arg);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: bad number " + e);
}
- densityStr = getNextArg();
- } else if ("-u".equals(densityStr) && arg != null) {
+ } else if ("-u".equals(arg)) {
+ arg = getNextArg();
displayId = mInterface.getDisplayIdByUniqueId(arg);
if (displayId == Display.INVALID_DISPLAY) {
getErrPrintWriter().println("Error: the uniqueId is invalid ");
return -1;
}
- densityStr = getNextArg();
}
if (densityStr == null) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 58c48ad..c6d0b72 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1194,7 +1194,13 @@
}
}
}
- effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+ if (wc.asTask() != null) {
+ effects |= sanitizeAndApplyHierarchyOpForTask(wc.asTask(), hop);
+ } else if (wc.asDisplayArea() != null) {
+ effects |= sanitizeAndApplyHierarchyOpForDisplayArea(wc.asDisplayArea(), hop);
+ } else {
+ throw new IllegalArgumentException("Invalid container in hierarchy op");
+ }
break;
}
case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: {
@@ -1850,12 +1856,22 @@
return starterResult[0];
}
- private int sanitizeAndApplyHierarchyOp(WindowContainer container,
- WindowContainerTransaction.HierarchyOp hop) {
- final Task task = container.asTask();
- if (task == null) {
- throw new IllegalArgumentException("Invalid container in hierarchy op");
+ private int sanitizeAndApplyHierarchyOpForDisplayArea(@NonNull DisplayArea displayArea,
+ @NonNull WindowContainerTransaction.HierarchyOp hop) {
+ if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) {
+ throw new UnsupportedOperationException("DisplayArea only supports reordering");
}
+ if (displayArea.getParent() == null) {
+ return TRANSACT_EFFECTS_NONE;
+ }
+ displayArea.getParent().positionChildAt(
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ displayArea, hop.includingParents());
+ return TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
+ private int sanitizeAndApplyHierarchyOpForTask(@NonNull Task task,
+ @NonNull WindowContainerTransaction.HierarchyOp hop) {
final DisplayContent dc = task.getDisplayContent();
if (dc == null) {
Slog.w(TAG, "Container is no longer attached: " + task);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ec2fd3f..b6e8977 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2385,6 +2385,8 @@
mWmService.mDisplayManagerInternal.onPresentation(dc.getDisplay().getDisplayId(),
/*isShown=*/ false);
}
+ // Check if window provides non decor insets before clearing its provided insets.
+ final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();
dc.getDisplayPolicy().removeWindowLw(this);
@@ -2395,6 +2397,18 @@
mWmService.postWindowRemoveCleanupLocked(this);
consumeInsetsChange();
+
+ // Update the orientation when removing a window affecting the display orientation.
+ // Also recompute configuration if it provides screen decor insets.
+ boolean needToSendNewConfiguration = dc.getLastOrientationSource() == this
+ && dc.updateOrientation();
+ if (windowProvidesDisplayDecorInsets) {
+ needToSendNewConfiguration |= dc.getDisplayPolicy().updateDecorInsetsInfo();
+ }
+
+ if (needToSendNewConfiguration) {
+ dc.sendNewConfiguration();
+ }
}
@Override
@@ -2447,16 +2461,10 @@
mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION),
mWmService.mDisplayFrozen, Debug.getCallers(6));
- // Visibility of the removed window. Will be used later to update orientation later on.
- boolean wasVisible = false;
-
// First, see if we need to run an animation. If we do, we have to hold off on removing the
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
if (mHasSurface && mToken.okToAnimate()) {
- // If we are not currently running the exit animation, we need to see about starting one
- wasVisible = isVisible();
-
// Remove immediately if there is display transition because the animation is
// usually unnoticeable (e.g. covered by rotation animation) and the animation
// bounds could be inconsistent, such as depending on when the window applies
@@ -2466,7 +2474,9 @@
// look weird if its orientation is changed.
&& !inRelaunchingActivity();
- if (wasVisible && isDisplayed()) {
+ // If we are not currently running the exit animation,
+ // we need to see about starting one
+ if (isVisible() && isDisplayed()) {
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
// Try starting an animation.
@@ -2515,21 +2525,7 @@
}
}
- // Check if window provides non decor insets before clearing its provided insets.
- final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();
-
removeImmediately();
- // Removing a visible window may affect the display orientation so just update it if
- // needed. Also recompute configuration if it provides screen decor insets.
- boolean needToSendNewConfiguration = wasVisible && displayContent.updateOrientation();
- if (windowProvidesDisplayDecorInsets) {
- needToSendNewConfiguration |=
- displayContent.getDisplayPolicy().updateDecorInsetsInfo();
- }
-
- if (needToSendNewConfiguration) {
- displayContent.sendNewConfiguration();
- }
mWmService.updateFocusedWindowLocked(isFocused()
? UPDATE_FOCUS_REMOVING_FOCUS
: UPDATE_FOCUS_NORMAL,
@@ -3754,7 +3750,7 @@
// If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
// since it will be destroyed anyway. This also prevents the client from receiving
// windowing mode change before it is destroyed.
- if (inRelaunchingActivity()) {
+ if (inRelaunchingActivity() && mAttrs.type != TYPE_APPLICATION_STARTING) {
return;
}
// If this is an activity or wallpaper and is invisible or going invisible, don't report
@@ -3975,7 +3971,7 @@
* Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
*
* <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
- * LetterboxUiController#shouldShowLetterboxUi} for more context.
+ * AppCompatLetterboxOverrides#shouldShowLetterboxUi} for more context.
*/
boolean areAppWindowBoundsLetterboxed() {
return mActivityRecord != null && !isStartingWindowAssociatedToTask()
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index f1e94de..a5085fc 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -211,7 +211,6 @@
"android.system.suspend-V1-ndk",
"server_configurable_flags",
"service.incremental",
- "android.companion.virtualdevice.flags-aconfig-cc",
],
static_libs: [
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 9ec5ae5..6f72760 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -19,273 +19,22 @@
#include <android-base/unique_fd.h>
#include <android/input.h>
#include <android/keycodes.h>
-#include <android_companion_virtualdevice_flags.h>
-#include <errno.h>
-#include <fcntl.h>
#include <input/Input.h>
#include <input/VirtualInputDevice.h>
-#include <linux/uinput.h>
-#include <math.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <utils/Log.h>
-#include <map>
-#include <set>
#include <string>
using android::base::unique_fd;
namespace android {
-namespace vd_flags = android::companion::virtualdevice::flags;
-
static constexpr jlong INVALID_PTR = 0;
-enum class DeviceType {
- KEYBOARD,
- MOUSE,
- TOUCHSCREEN,
- DPAD,
- STYLUS,
- ROTARY_ENCODER,
-};
-
-static unique_fd invalidFd() {
- return unique_fd(-1);
-}
-
-/** Creates a new uinput device and assigns a file descriptor. */
-static unique_fd openUinput(const char* readableName, jint vendorId, jint productId,
- const char* phys, DeviceType deviceType, jint screenHeight,
- jint screenWidth) {
- unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
- if (fd < 0) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
-
- ioctl(fd, UI_SET_PHYS, phys);
-
- ioctl(fd, UI_SET_EVBIT, EV_KEY);
- ioctl(fd, UI_SET_EVBIT, EV_SYN);
- switch (deviceType) {
- case DeviceType::DPAD:
- for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
- ioctl(fd, UI_SET_KEYBIT, keyCode);
- }
- break;
- case DeviceType::KEYBOARD:
- for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
- ioctl(fd, UI_SET_KEYBIT, keyCode);
- }
- break;
- case DeviceType::MOUSE:
- ioctl(fd, UI_SET_EVBIT, EV_REL);
- ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
- ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
- ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
- ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
- ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
- ioctl(fd, UI_SET_RELBIT, REL_X);
- ioctl(fd, UI_SET_RELBIT, REL_Y);
- ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
- ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
- if (vd_flags::high_resolution_scroll()) {
- ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
- ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
- }
- break;
- case DeviceType::TOUCHSCREEN:
- ioctl(fd, UI_SET_EVBIT, EV_ABS);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
- ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
- break;
- case DeviceType::STYLUS:
- ioctl(fd, UI_SET_EVBIT, EV_ABS);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
- ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
- ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
- ioctl(fd, UI_SET_ABSBIT, ABS_X);
- ioctl(fd, UI_SET_ABSBIT, ABS_Y);
- ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
- ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
- 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);
- if (vd_flags::high_resolution_scroll()) {
- ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
- }
- break;
- default:
- ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
- return invalidFd();
- }
-
- int version;
- if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
- uinput_setup setup;
- memset(&setup, 0, sizeof(setup));
- strlcpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
- setup.id.version = 1;
- setup.id.bustype = BUS_VIRTUAL;
- setup.id.vendor = vendorId;
- setup.id.product = productId;
- if (deviceType == DeviceType::TOUCHSCREEN) {
- uinput_abs_setup xAbsSetup;
- xAbsSetup.code = ABS_MT_POSITION_X;
- xAbsSetup.absinfo.maximum = screenWidth - 1;
- xAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup yAbsSetup;
- yAbsSetup.code = ABS_MT_POSITION_Y;
- yAbsSetup.absinfo.maximum = screenHeight - 1;
- yAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup majorAbsSetup;
- majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
- majorAbsSetup.absinfo.maximum = screenWidth - 1;
- majorAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup pressureAbsSetup;
- pressureAbsSetup.code = ABS_MT_PRESSURE;
- pressureAbsSetup.absinfo.maximum = 255;
- pressureAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup slotAbsSetup;
- slotAbsSetup.code = ABS_MT_SLOT;
- slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
- slotAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup trackingIdAbsSetup;
- trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
- trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
- trackingIdAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
- return invalidFd();
- }
- } else if (deviceType == DeviceType::STYLUS) {
- uinput_abs_setup xAbsSetup;
- xAbsSetup.code = ABS_X;
- xAbsSetup.absinfo.maximum = screenWidth - 1;
- xAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup yAbsSetup;
- yAbsSetup.code = ABS_Y;
- yAbsSetup.absinfo.maximum = screenHeight - 1;
- yAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup tiltXAbsSetup;
- tiltXAbsSetup.code = ABS_TILT_X;
- tiltXAbsSetup.absinfo.maximum = 90;
- tiltXAbsSetup.absinfo.minimum = -90;
- if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup tiltYAbsSetup;
- tiltYAbsSetup.code = ABS_TILT_Y;
- tiltYAbsSetup.absinfo.maximum = 90;
- tiltYAbsSetup.absinfo.minimum = -90;
- if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup pressureAbsSetup;
- pressureAbsSetup.code = ABS_PRESSURE;
- pressureAbsSetup.absinfo.maximum = 255;
- pressureAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
- return invalidFd();
- }
- }
- if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
- } else {
- // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
- ALOGI("Falling back to version %d manual setup", version);
- uinput_user_dev fallback;
- memset(&fallback, 0, sizeof(fallback));
- strlcpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
- fallback.id.version = 1;
- fallback.id.bustype = BUS_VIRTUAL;
- fallback.id.vendor = vendorId;
- fallback.id.product = productId;
- if (deviceType == DeviceType::TOUCHSCREEN) {
- fallback.absmin[ABS_MT_POSITION_X] = 0;
- fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
- fallback.absmin[ABS_MT_POSITION_Y] = 0;
- fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
- fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
- fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
- fallback.absmin[ABS_MT_PRESSURE] = 0;
- fallback.absmax[ABS_MT_PRESSURE] = 255;
- } else if (deviceType == DeviceType::STYLUS) {
- fallback.absmin[ABS_X] = 0;
- fallback.absmax[ABS_X] = screenWidth - 1;
- fallback.absmin[ABS_Y] = 0;
- fallback.absmax[ABS_Y] = screenHeight - 1;
- fallback.absmin[ABS_TILT_X] = -90;
- fallback.absmax[ABS_TILT_X] = 90;
- fallback.absmin[ABS_TILT_Y] = -90;
- fallback.absmax[ABS_TILT_Y] = 90;
- fallback.absmin[ABS_PRESSURE] = 0;
- fallback.absmax[ABS_PRESSURE] = 255;
- }
- if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
- }
-
- if (ioctl(fd, UI_DEV_CREATE) != 0) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
-
- return fd;
-}
-
static unique_fd openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
- jstring phys, DeviceType deviceType, int screenHeight,
- int screenWidth) {
+ jstring phys, DeviceType deviceType, jint screenHeight,
+ jint screenWidth) {
ScopedUtfChars readableName(env, name);
ScopedUtfChars readablePhys(env, phys);
return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index f86d307..19a942c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -129,9 +129,8 @@
*/
static PolicyDefinition<Integer> PERMISSION_GRANT(
@NonNull String packageName, @NonNull String permissionName) {
- if (packageName == null || permissionName == null) {
- return GENERIC_PERMISSION_GRANT;
- }
+ Objects.requireNonNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(permissionName, "permissionName must not be null");
return GENERIC_PERMISSION_GRANT.createPolicyDefinition(
new PackagePermissionPolicyKey(
DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
@@ -190,10 +189,8 @@
* {@link #GENERIC_PERSISTENT_PREFERRED_ACTIVITY}.
*/
static PolicyDefinition<ComponentName> PERSISTENT_PREFERRED_ACTIVITY(
- IntentFilter intentFilter) {
- if (intentFilter == null) {
- return GENERIC_PERSISTENT_PREFERRED_ACTIVITY;
- }
+ @NonNull IntentFilter intentFilter) {
+ Objects.requireNonNull(intentFilter, "intentFilter must not be null");
return GENERIC_PERSISTENT_PREFERRED_ACTIVITY.createPolicyDefinition(
new IntentFilterPolicyKey(
DevicePolicyIdentifiers.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
@@ -216,11 +213,8 @@
* Passing in {@code null} for {@code packageName} will return
* {@link #GENERIC_PACKAGE_UNINSTALL_BLOCKED}.
*/
- static PolicyDefinition<Boolean> PACKAGE_UNINSTALL_BLOCKED(
- String packageName) {
- if (packageName == null) {
- return GENERIC_PACKAGE_UNINSTALL_BLOCKED;
- }
+ static PolicyDefinition<Boolean> PACKAGE_UNINSTALL_BLOCKED(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
return GENERIC_PACKAGE_UNINSTALL_BLOCKED.createPolicyDefinition(
new PackagePolicyKey(
DevicePolicyIdentifiers.PACKAGE_UNINSTALL_BLOCKED_POLICY, packageName));
@@ -247,10 +241,8 @@
* Passing in {@code null} for {@code packageName} will return
* {@link #GENERIC_APPLICATION_RESTRICTIONS}.
*/
- static PolicyDefinition<Bundle> APPLICATION_RESTRICTIONS(String packageName) {
- if (packageName == null) {
- return GENERIC_APPLICATION_RESTRICTIONS;
- }
+ static PolicyDefinition<Bundle> APPLICATION_RESTRICTIONS(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
return GENERIC_APPLICATION_RESTRICTIONS.createPolicyDefinition(
new PackagePolicyKey(
DevicePolicyIdentifiers.APPLICATION_RESTRICTIONS_POLICY, packageName));
@@ -293,10 +285,8 @@
* Passing in {@code null} for {@code packageName} will return
* {@link #GENERIC_APPLICATION_HIDDEN}.
*/
- static PolicyDefinition<Boolean> APPLICATION_HIDDEN(String packageName) {
- if (packageName == null) {
- return GENERIC_APPLICATION_HIDDEN;
- }
+ static PolicyDefinition<Boolean> APPLICATION_HIDDEN(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
return GENERIC_APPLICATION_HIDDEN.createPolicyDefinition(
new PackagePolicyKey(
DevicePolicyIdentifiers.APPLICATION_HIDDEN_POLICY, packageName));
@@ -319,10 +309,8 @@
* Passing in {@code null} for {@code accountType} will return
* {@link #GENERIC_ACCOUNT_MANAGEMENT_DISABLED}.
*/
- static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(String accountType) {
- if (accountType == null) {
- return GENERIC_ACCOUNT_MANAGEMENT_DISABLED;
- }
+ static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(@NonNull String accountType) {
+ Objects.requireNonNull(accountType, "accountType must not be null");
return GENERIC_ACCOUNT_MANAGEMENT_DISABLED.createPolicyDefinition(
new AccountTypePolicyKey(
DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY, accountType));
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9e8811f..09c54cb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
@@ -105,6 +106,7 @@
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.policy.AttributeCache;
+import com.android.internal.protolog.ProtoLogService;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
@@ -119,6 +121,7 @@
import com.android.server.ambientcontext.AmbientContextManagerService;
import com.android.server.app.GameManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.appfunctions.AppFunctionManagerService;
import com.android.server.apphibernation.AppHibernationService;
import com.android.server.appop.AppOpMigrationHelper;
import com.android.server.appop.AppOpMigrationHelperImpl;
@@ -1087,6 +1090,13 @@
SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
t.traceEnd();
+ // Orchestrates some ProtoLogging functionality.
+ if (android.tracing.Flags.clientSideProtoLogging()) {
+ t.traceBegin("StartProtoLogService");
+ ServiceManager.addService(Context.PROTOLOG_SERVICE, new ProtoLogService());
+ t.traceEnd();
+ }
+
// Platform compat service is used by ActivityManagerService, PackageManagerService, and
// possibly others in the future. b/135010838.
t.traceBegin("PlatformCompat");
@@ -1719,6 +1729,12 @@
mSystemServiceManager.startService(LogcatManagerService.class);
t.traceEnd();
+ t.traceBegin("StartAppFunctionManager");
+ if (enableAppFunctionManager()) {
+ mSystemServiceManager.startService(AppFunctionManagerService.class);
+ }
+ t.traceEnd();
+
} catch (Throwable e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service");
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 aee7242..770451c 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -44,7 +44,6 @@
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.Rule;
@@ -178,19 +177,20 @@
return items;
}
- private void assertNextInputMethod(@NonNull ControllerImpl controller, boolean onlyCurrentIme,
- @NonNull ImeSubtypeListItem currentItem, @Nullable ImeSubtypeListItem nextItem) {
+ private void assertNextInputMethod(@NonNull InputMethodSubtypeSwitchingController controller,
+ boolean onlyCurrentIme, @NonNull ImeSubtypeListItem currentItem,
+ @Nullable ImeSubtypeListItem nextItem) {
InputMethodSubtype subtype = null;
if (currentItem.mSubtypeName != null) {
subtype = createTestSubtype(currentItem.mSubtypeName.toString());
}
- final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
+ final ImeSubtypeListItem nextIme = controller.getNextInputMethodLocked(onlyCurrentIme,
currentItem.mImi, subtype, MODE_STATIC, true /* forward */);
assertEquals(nextItem, nextIme);
}
- private void assertRotationOrder(@NonNull ControllerImpl controller, boolean onlyCurrentIme,
- ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
+ private void assertRotationOrder(@NonNull InputMethodSubtypeSwitchingController controller,
+ boolean onlyCurrentIme, ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
final int numItems = expectedRotationOrderOfImeSubtypeList.length;
for (int i = 0; i < numItems; i++) {
final int nextIndex = (i + 1) % numItems;
@@ -200,7 +200,7 @@
}
}
- private boolean onUserAction(@NonNull ControllerImpl controller,
+ private boolean onUserAction(@NonNull InputMethodSubtypeSwitchingController controller,
@NonNull ImeSubtypeListItem subtypeListItem) {
InputMethodSubtype subtype = null;
if (subtypeListItem.mSubtypeName != null) {
@@ -228,8 +228,8 @@
final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(6);
final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(7);
- final ControllerImpl controller = ControllerImpl.createFrom(
- null /* currentInstance */, enabledItems, new ArrayList<>());
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(enabledItems, new ArrayList<>());
// switching-aware loop
assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -286,8 +286,8 @@
final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(6);
final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(7);
- final ControllerImpl controller = ControllerImpl.createFrom(
- null /* currentInstance */, enabledItems, new ArrayList<>());
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(enabledItems, new ArrayList<>());
// === switching-aware loop ===
assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -336,11 +336,10 @@
// Rotation order should be preserved when created with the same subtype list.
final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
- final ControllerImpl newController = ControllerImpl.createFrom(controller,
- sameEnabledItems, new ArrayList<>());
- assertRotationOrder(newController, false /* onlyCurrentIme */,
+ controller.update(sameEnabledItems, new ArrayList<>());
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
subtypeAwareIme, latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
- assertRotationOrder(newController, false /* onlyCurrentIme */,
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi, subtypeUnawareIme,
switchUnawareJapaneseIme_ja_jp);
@@ -348,11 +347,10 @@
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, new ArrayList<>());
- assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+ controller.update(differentEnabledItems, new ArrayList<>());
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
latinIme_en_us, latinIme_fr, subtypeAwareIme);
- assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
switchingUnawareLatinIme_en_uk, switchUnawareJapaneseIme_ja_jp, subtypeUnawareIme);
}
@@ -520,8 +518,8 @@
final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
final var hardwareSimpleIme = List.of(hardwareSimple);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
- hardwareItems);
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(items, hardwareItems);
final int mode = MODE_STATIC;
@@ -583,8 +581,8 @@
final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
final var hardwareSimpleIme = List.of(hardwareSimple);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
- hardwareItems);
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(items, hardwareItems);
final int mode = MODE_RECENT;
@@ -666,8 +664,8 @@
final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
final var hardwareSimpleIme = List.of(hardwareSimple);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
- hardwareItems);
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(items, hardwareItems);
final int mode = MODE_AUTO;
@@ -777,92 +775,73 @@
final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
final var hardwareSimpleIme = List.of(hardwareSimple);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
- hardwareItems);
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(items, hardwareItems);
final int mode = MODE_RECENT;
// Recency order is initialized to static order.
assertNextOrder(controller, false /* forHardware */, mode,
items, List.of(latinIme, simpleIme));
-
assertNextOrder(controller, true /* forHardware */, mode,
hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
// User action on french IME.
assertTrue("Recency updated for french IME", onUserAction(controller, french));
- final var equalItems = new ArrayList<>(items);
- final var otherItems = new ArrayList<>(items);
- otherItems.remove(simple);
-
- final var equalController = ControllerImpl.createFrom(controller, equalItems,
- hardwareItems);
- final var otherController = ControllerImpl.createFrom(controller, otherItems,
- hardwareItems);
-
final var recencyItems = List.of(french, english, italian, simple);
final var recencyLatinIme = List.of(french, english, italian);
final var recencySimpleIme = List.of(simple);
+ final var equalItems = new ArrayList<>(items);
+ controller.update(equalItems, hardwareItems);
+
+ // The order of non-hardware items remains unchanged when updated with equal items.
assertNextOrder(controller, false /* forHardware */, mode,
recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
- // The order of equal non-hardware items is unchanged.
- assertNextOrder(equalController, false /* forHardware */, mode,
- recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
- // The order of other hardware items is reset.
- assertNextOrder(otherController, false /* forHardware */, mode,
- latinIme, List.of(latinIme));
-
- // The order of hardware remains unchanged.
+ // The order of hardware items remains unchanged when only non-hardware items are updated.
assertNextOrder(controller, true /* forHardware */, mode,
hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
- assertNextOrder(equalController, true /* forHardware */, mode,
- hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
+ final var otherItems = new ArrayList<>(items);
+ otherItems.remove(simple);
+ controller.update(otherItems, hardwareItems);
- assertNextOrder(otherController, true /* forHardware */, mode,
+ // The order of non-hardware items is reset when updated with other items.
+ assertNextOrder(controller, false /* forHardware */, mode,
+ latinIme, List.of(latinIme));
+ // The order of hardware items remains unchanged when only non-hardware items are updated.
+ assertNextOrder(controller, true /* forHardware */, mode,
hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
assertTrue("Recency updated for french hardware IME",
onUserAction(controller, hardwareFrench));
- final var equalHardwareItems = new ArrayList<>(hardwareItems);
- final var otherHardwareItems = new ArrayList<>(hardwareItems);
- otherHardwareItems.remove(hardwareSimple);
-
- final var equalHardwareController = ControllerImpl.createFrom(controller, items,
- equalHardwareItems);
- final var otherHardwareController = ControllerImpl.createFrom(controller, items,
- otherHardwareItems);
-
final var recencyHardwareItems =
List.of(hardwareFrench, hardwareEnglish, hardwareItalian, hardwareSimple);
final var recencyHardwareLatinIme =
List.of(hardwareFrench, hardwareEnglish, hardwareItalian);
final var recencyHardwareSimpleIme = List.of(hardwareSimple);
- // The order of non-hardware items remains unchanged.
+ final var equalHardwareItems = new ArrayList<>(hardwareItems);
+ controller.update(otherItems, equalHardwareItems);
+
+ // The order of non-hardware items remains unchanged when only hardware items are updated.
assertNextOrder(controller, false /* forHardware */, mode,
- recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
- assertNextOrder(equalHardwareController, false /* forHardware */, mode,
- recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
- assertNextOrder(otherHardwareController, false /* forHardware */, mode,
- recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
+ latinIme, List.of(latinIme));
+ // The order of hardware items remains unchanged when updated with equal items.
assertNextOrder(controller, true /* forHardware */, mode,
recencyHardwareItems, List.of(recencyHardwareLatinIme, recencyHardwareSimpleIme));
- // The order of equal hardware items is unchanged.
- assertNextOrder(equalHardwareController, true /* forHardware */, mode,
- recencyHardwareItems, List.of(recencyHardwareLatinIme, recencyHardwareSimpleIme));
+ final var otherHardwareItems = new ArrayList<>(hardwareItems);
+ otherHardwareItems.remove(hardwareSimple);
+ controller.update(otherItems, otherHardwareItems);
- // The order of other hardware items is reset.
- assertNextOrder(otherHardwareController, true /* forHardware */, mode,
+ // The order of non-hardware items remains unchanged when only hardware items are updated.
+ assertNextOrder(controller, false /* forHardware */, mode,
+ latinIme, List.of(latinIme));
+ // The order of hardware items is reset when updated with other items.
+ assertNextOrder(controller, true /* forHardware */, mode,
hardwareLatinIme, List.of(hardwareLatinIme));
}
@@ -882,8 +861,8 @@
addTestImeSubtypeListItems(hardwareItems, "hardwareSwitchUnaware", "hardwareSwitchUnaware",
null, false /* supportsSwitchingToNextInputMethod*/);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
- hardwareItems);
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(items, hardwareItems);
for (int mode = MODE_STATIC; mode <= MODE_AUTO; mode++) {
assertNextOrder(controller, false /* forHardware */, false /* onlyCurrentIme */,
@@ -910,55 +889,102 @@
addTestImeSubtypeListItems(hardwareItems, "HardwareIme", "HardwareIme",
List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */, List.of(),
- List.of());
+ final var controller = new InputMethodSubtypeSwitchingController();
- assertNoAction(controller, false /* forHardware */, items);
- assertNoAction(controller, true /* forHardware */, hardwareItems);
+ assertNextItemNoAction(controller, false /* forHardware */, items,
+ null /* expectedNext */);
+ assertNextItemNoAction(controller, true /* forHardware */, hardwareItems,
+ null /* expectedNext */);
}
- /** Verifies that a controller with a single item can't take any actions. */
+ /**
+ * Verifies that a controller with a single item can't update the recency, and cannot switch
+ * away from the item, but allows switching from unknown items to the single item.
+ */
@RequiresFlagsEnabled(Flags.FLAG_IME_SWITCHER_REVAMP)
@Test
public void testSingleItemList() {
final var items = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ null, true /* supportsSwitchingToNextInputMethod */);
+ final var unknownItems = new ArrayList<ImeSubtypeListItem>();
+ addTestImeSubtypeListItems(unknownItems, "UnknownIme", "UnknownIme",
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var hardwareItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(hardwareItems, "HardwareIme", "HardwareIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ null, true /* supportsSwitchingToNextInputMethod */);
+ final var unknownHardwareItems = new ArrayList<ImeSubtypeListItem>();
+ addTestImeSubtypeListItems(unknownHardwareItems, "HardwareUnknownIme", "HardwareUnknownIme",
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */,
- List.of(items.get(0)), List.of(hardwareItems.get(0)));
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(items, hardwareItems);
- assertNoAction(controller, false /* forHardware */, items);
- assertNoAction(controller, true /* forHardware */, hardwareItems);
+ assertNextItemNoAction(controller, false /* forHardware */, items,
+ null /* expectedNext */);
+ assertNextItemNoAction(controller, false /* forHardware */, unknownItems,
+ items.get(0));
+ assertNextItemNoAction(controller, true /* forHardware */, hardwareItems,
+ null /* expectedNext */);
+ assertNextItemNoAction(controller, true /* forHardware */, unknownHardwareItems,
+ hardwareItems.get(0));
}
- /** Verifies that a controller can't take any actions for unknown items. */
+ /**
+ * Verifies that the recency cannot be updated for unknown items, but switching from unknown
+ * items reaches the most recent known item.
+ */
@RequiresFlagsEnabled(Flags.FLAG_IME_SWITCHER_REVAMP)
@Test
public void testUnknownItems() {
final var items = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
+
+ final var english = items.get(0);
+ final var french = items.get(1);
+ final var italian = items.get(2);
+
final var unknownItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(unknownItems, "UnknownIme", "UnknownIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var hardwareItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(hardwareItems, "HardwareIme", "HardwareIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
final var unknownHardwareItems = new ArrayList<ImeSubtypeListItem>();
addTestImeSubtypeListItems(unknownHardwareItems, "HardwareUnknownIme", "HardwareUnknownIme",
- List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
+ List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
- final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
- hardwareItems);
+ final var controller = new InputMethodSubtypeSwitchingController();
+ controller.update(items, hardwareItems);
- assertNoAction(controller, false /* forHardware */, unknownItems);
- assertNoAction(controller, true /* forHardware */, unknownHardwareItems);
+ assertTrue("Recency updated for french IME", onUserAction(controller, french));
+
+ final var recencyItems = List.of(french, english, italian);
+
+ assertNextItemNoAction(controller, false /* forHardware */, unknownItems,
+ french);
+ assertNextItemNoAction(controller, true /* forHardware */, unknownHardwareItems,
+ hardwareItems.get(0));
+
+ // Known items must not be able to switch to unknown items.
+ assertNextOrder(controller, false /* forHardware */, MODE_STATIC, items,
+ List.of(items));
+ assertNextOrder(controller, false /* forHardware */, MODE_RECENT, recencyItems,
+ List.of(recencyItems));
+ assertNextOrder(controller, false /* forHardware */, MODE_AUTO, true /* forward */,
+ recencyItems, List.of(recencyItems));
+ assertNextOrder(controller, false /* forHardware */, MODE_AUTO, false /* forward */,
+ items.reversed(), List.of(items.reversed()));
+
+ assertNextOrder(controller, true /* forHardware */, MODE_STATIC, hardwareItems,
+ List.of(hardwareItems));
+ assertNextOrder(controller, true /* forHardware */, MODE_RECENT, hardwareItems,
+ List.of(hardwareItems));
+ assertNextOrder(controller, true /* forHardware */, MODE_AUTO, hardwareItems,
+ List.of(hardwareItems));
}
/** Verifies that the IME name does influence the comparison order. */
@@ -1070,8 +1096,9 @@
* @param allItems the list of items across all IMEs.
* @param perImeItems the list of lists of items per IME.
*/
- private static void assertNextOrder(@NonNull ControllerImpl controller, boolean forHardware,
- @SwitchMode int mode, boolean forward, @NonNull List<ImeSubtypeListItem> allItems,
+ private static void assertNextOrder(@NonNull InputMethodSubtypeSwitchingController controller,
+ boolean forHardware, @SwitchMode int mode, boolean forward,
+ @NonNull List<ImeSubtypeListItem> allItems,
@NonNull List<List<ImeSubtypeListItem>> perImeItems) {
assertNextOrder(controller, forHardware, false /* onlyCurrentIme */, mode,
forward, allItems);
@@ -1094,8 +1121,8 @@
* @param allItems the list of items across all IMEs.
* @param perImeItems the list of lists of items per IME.
*/
- private static void assertNextOrder(@NonNull ControllerImpl controller, boolean forHardware,
- @SwitchMode int mode, @NonNull List<ImeSubtypeListItem> allItems,
+ private static void assertNextOrder(@NonNull InputMethodSubtypeSwitchingController controller,
+ boolean forHardware, @SwitchMode int mode, @NonNull List<ImeSubtypeListItem> allItems,
@NonNull List<List<ImeSubtypeListItem>> perImeItems) {
assertNextOrder(controller, forHardware, false /* onlyCurrentIme */, mode,
true /* forward */, allItems);
@@ -1122,7 +1149,7 @@
* @param forward whether to search forwards or backwards in the list.
* @param items the list of items to verify, in the expected order.
*/
- private static void assertNextOrder(@NonNull ControllerImpl controller,
+ private static void assertNextOrder(@NonNull InputMethodSubtypeSwitchingController controller,
boolean forHardware, boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
@NonNull List<ImeSubtypeListItem> items) {
final int numItems = items.size();
@@ -1166,7 +1193,7 @@
* @param item the item to find the next value from.
* @param expectedNext the expected next value.
*/
- private static void assertNextItem(@NonNull ControllerImpl controller,
+ private static void assertNextItem(@NonNull InputMethodSubtypeSwitchingController controller,
boolean forHardware, boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
@NonNull ImeSubtypeListItem item, @Nullable ImeSubtypeListItem expectedNext) {
final var nextItem = getNextItem(controller, forHardware, onlyCurrentIme, mode, forward,
@@ -1186,38 +1213,41 @@
* @return the next item found, otherwise {@code null}.
*/
@Nullable
- private static ImeSubtypeListItem getNextItem(@NonNull ControllerImpl controller,
- boolean forHardware, boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
+ private static ImeSubtypeListItem getNextItem(
+ @NonNull InputMethodSubtypeSwitchingController controller, boolean forHardware,
+ boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
@NonNull ImeSubtypeListItem item) {
final var subtype = item.mSubtypeName != null
? createTestSubtype(item.mSubtypeName.toString()) : null;
return forHardware
? controller.getNextInputMethodForHardware(
onlyCurrentIme, item.mImi, subtype, mode, forward)
- : controller.getNextInputMethod(
+ : controller.getNextInputMethodLocked(
onlyCurrentIme, item.mImi, subtype, mode, forward);
}
/**
- * Verifies that no next items can be found, and the recency cannot be updated for the
+ * Verifies that the expected next item is returned, and the recency cannot be updated for the
* given items.
*
- * @param controller the controller to verify the items on.
- * @param forHardware whether to try finding the next hardware item, or software item.
- * @param items the list of items to verify.
+ * @param controller the controller to verify the items on.
+ * @param forHardware whether to try finding the next hardware item, or software item.
+ * @param items the list of items to verify.
+ * @param expectedNext the expected next item.
*/
- private void assertNoAction(@NonNull ControllerImpl controller, boolean forHardware,
- @NonNull List<ImeSubtypeListItem> items) {
+ private void assertNextItemNoAction(@NonNull InputMethodSubtypeSwitchingController controller,
+ boolean forHardware, @NonNull List<ImeSubtypeListItem> items,
+ @Nullable ImeSubtypeListItem expectedNext) {
for (var item : items) {
for (int mode = MODE_STATIC; mode <= MODE_AUTO; mode++) {
assertNextItem(controller, forHardware, false /* onlyCurrentIme */, mode,
- false /* forward */, item, null /* expectedNext */);
+ false /* forward */, item, expectedNext);
assertNextItem(controller, forHardware, false /* onlyCurrentIme */, mode,
- true /* forward */, item, null /* expectedNext */);
+ true /* forward */, item, expectedNext);
assertNextItem(controller, forHardware, true /* onlyCurrentIme */, mode,
- false /* forward */, item, null /* expectedNext */);
+ false /* forward */, item, expectedNext);
assertNextItem(controller, forHardware, true /* onlyCurrentIme */, mode,
- true /* forward */, item, null /* expectedNext */);
+ true /* forward */, item, expectedNext);
}
assertFalse("User action shouldn't have updated the recency.",
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index b980ca0..30de0e8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -512,6 +512,9 @@
when(mPermissionManagerInternal.getAppOpPermissionPackages(
SCHEDULE_EXACT_ALARM)).thenReturn(EmptyArray.STRING);
+ // Initialize timestamps with arbitrary values of time
+ mNowElapsedTest = 12;
+ mNowRtcTest = 345;
mInjector = new Injector(mMockContext);
mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
@@ -774,6 +777,61 @@
}
@Test
+ public void timeChangeBroadcastForward() throws Exception {
+ final long timeDelta = 12345;
+ // AlarmManagerService sends the broadcast if real time clock proceeds 1000ms more than boot
+ // time clock.
+ mNowRtcTest += timeDelta + 1001;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ verify(mMockContext)
+ .sendBroadcastAsUser(
+ argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+ eq(UserHandle.ALL),
+ isNull(),
+ any());
+ }
+
+ @Test
+ public void timeChangeBroadcastBackward() throws Exception {
+ final long timeDelta = 12345;
+ // AlarmManagerService sends the broadcast if real time clock proceeds 1000ms less than boot
+ // time clock.
+ mNowRtcTest += timeDelta - 1001;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ verify(mMockContext)
+ .sendBroadcastAsUser(
+ argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+ eq(UserHandle.ALL),
+ isNull(),
+ any());
+ }
+
+ @Test
+ public void timeChangeFilterMinorAdjustment() throws Exception {
+ final long timeDelta = 12345;
+ // AlarmManagerService does not send the broadcast if real time clock proceeds within 1000ms
+ // than boot time clock.
+ mNowRtcTest += timeDelta + 1000;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ mNowRtcTest += timeDelta - 1000;
+ mNowElapsedTest += timeDelta;
+ mTestTimer.expire(TIME_CHANGED_MASK);
+
+ verify(mMockContext, never())
+ .sendBroadcastAsUser(
+ argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+ any(),
+ any(),
+ any());
+ }
+
+ @Test
public void testSingleAlarmExpiration() throws Exception {
final long triggerTime = mNowElapsedTest + 5000;
final PendingIntent alarmPi = getNewMockPendingIntent();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 9ab607d..0a6edf1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -314,6 +314,7 @@
whenever(mocks.systemConfig.defaultVrComponents).thenReturn(ArraySet())
whenever(mocks.systemConfig.hiddenApiWhitelistedApps).thenReturn(ArraySet())
whenever(mocks.systemConfig.appMetadataFilePaths).thenReturn(ArrayMap())
+ whenever(mocks.systemConfig.oemDefinedUids).thenReturn(ArrayMap())
wheneverStatic { SystemProperties.set(anyString(), anyString()) }.thenDoNothing()
wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true)
wheneverStatic { Environment.getApexDirectory() }.thenReturn(apexDirectory)
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
index 0d14c9f..1b9f8d1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
@@ -40,6 +40,8 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -224,6 +226,45 @@
}
}
+ private LongArrayQueue getEvents(int userId, String packageName, String tag) {
+ synchronized (mQuotaTracker.mLock) {
+ return mQuotaTracker.getEvents(userId, packageName, tag);
+ }
+ }
+
+ private void updateExecutionStats(final int userId, @NonNull final String packageName,
+ @Nullable final String tag, @NonNull ExecutionStats stats) {
+ synchronized (mQuotaTracker.mLock) {
+ mQuotaTracker.updateExecutionStatsLocked(userId, packageName, tag, stats);
+ }
+ }
+
+ private ExecutionStats getExecutionStats(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ synchronized (mQuotaTracker.mLock) {
+ return mQuotaTracker.getExecutionStatsLocked(userId, packageName, tag);
+ }
+ }
+
+ private void maybeScheduleStartAlarm(final int userId, @NonNull final String packageName,
+ @Nullable final String tag) {
+ synchronized (mQuotaTracker.mLock) {
+ mQuotaTracker.maybeScheduleStartAlarmLocked(userId, packageName, tag);
+ }
+ }
+
+ private void maybeScheduleCleanupAlarm() {
+ synchronized (mQuotaTracker.mLock) {
+ mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+ }
+ }
+
+ private void deleteObsoleteEvents() {
+ synchronized (mQuotaTracker.mLock) {
+ mQuotaTracker.deleteObsoleteEventsLocked();
+ }
+ }
+
@Test
public void testDeleteObsoleteEventsLocked() {
// Count window size should only apply to event list.
@@ -243,9 +284,9 @@
expectedEvents.addLast(now - HOUR_IN_MILLIS);
expectedEvents.addLast(now - 1);
- mQuotaTracker.deleteObsoleteEventsLocked();
+ deleteObsoleteEvents();
- LongArrayQueue remainingEvents = mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE,
+ LongArrayQueue remainingEvents = getEvents(TEST_USER_ID, TEST_PACKAGE,
TEST_TAG);
assertTrue(longArrayQueueEquals(expectedEvents, remainingEvents));
}
@@ -270,15 +311,15 @@
removal.putExtra(Intent.EXTRA_UID, TEST_UID);
mReceiver.onReceive(mContext, removal);
assertNull(
- mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag1"));
+ getEvents(TEST_USER_ID, "com.android.test.remove", "tag1"));
assertNull(
- mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag2"));
+ getEvents(TEST_USER_ID, "com.android.test.remove", "tag2"));
assertNull(
- mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag3"));
+ getEvents(TEST_USER_ID, "com.android.test.remove", "tag3"));
assertTrue(longArrayQueueEquals(expected1,
- mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag1")));
+ getEvents(TEST_USER_ID, "com.android.test.stay", "tag1")));
assertTrue(longArrayQueueEquals(expected2,
- mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag2")));
+ getEvents(TEST_USER_ID, "com.android.test.stay", "tag2")));
}
@Test
@@ -298,10 +339,10 @@
Intent removal = new Intent(Intent.ACTION_USER_REMOVED);
removal.putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID);
mReceiver.onReceive(mContext, removal);
- assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag1"));
- assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag2"));
- assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag3"));
- longArrayQueueEquals(expected, mQuotaTracker.getEvents(10, TEST_PACKAGE, "tag4"));
+ assertNull(getEvents(TEST_USER_ID, TEST_PACKAGE, "tag1"));
+ assertNull(getEvents(TEST_USER_ID, TEST_PACKAGE, "tag2"));
+ assertNull(getEvents(TEST_USER_ID, TEST_PACKAGE, "tag3"));
+ longArrayQueueEquals(expected, getEvents(10, TEST_PACKAGE, "tag4"));
}
@Test
@@ -323,7 +364,7 @@
inputStats.countLimit = expectedStats.countLimit = 3;
// Invalid time is now +24 hours since there are no sessions at all for the app.
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, "com.android.test.not.run", TEST_TAG,
+ updateExecutionStats(TEST_USER_ID, "com.android.test.not.run", TEST_TAG,
inputStats);
assertEquals(expectedStats, inputStats);
@@ -333,19 +374,19 @@
// Invalid time is now since there was an event exactly windowSizeMs ago.
expectedStats.expirationTimeElapsed = now;
expectedStats.countInWindow = 1;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
expectedStats.expirationTimeElapsed = now + 2 * MINUTE_IN_MILLIS;
expectedStats.countInWindow = 1;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 4 * MINUTE_IN_MILLIS;
expectedStats.expirationTimeElapsed = now + 3 * MINUTE_IN_MILLIS;
expectedStats.countInWindow = 1;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
@@ -353,13 +394,13 @@
// minutes.
expectedStats.expirationTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
expectedStats.countInWindow = 2;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
expectedStats.expirationTimeElapsed = now + 45 * MINUTE_IN_MILLIS;
expectedStats.countInWindow = 2;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
@@ -370,28 +411,28 @@
// App is at event count limit but the oldest session is at the edge of the window, so
// in quota time is now.
expectedStats.inQuotaTimeElapsed = now;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
expectedStats.countInWindow = 3;
expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * HOUR_IN_MILLIS;
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
expectedStats.countInWindow = 4;
expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
expectedStats.expirationTimeElapsed = now + 2 * HOUR_IN_MILLIS;
expectedStats.countInWindow = 4;
expectedStats.inQuotaTimeElapsed = now + 5 * HOUR_IN_MILLIS;
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
assertEquals(expectedStats, inputStats);
}
@@ -428,7 +469,7 @@
expectedStats.countInWindow = 1;
mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
assertEquals(expectedStats,
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
// Working
expectedStats.expirationTimeElapsed = now;
@@ -437,7 +478,7 @@
expectedStats.countInWindow = 2;
mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
assertEquals(expectedStats,
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
// Frequent
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
@@ -447,7 +488,7 @@
expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
assertEquals(expectedStats,
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
// Rare
expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
@@ -457,7 +498,7 @@
expectedStats.inQuotaTimeElapsed = now + 19 * HOUR_IN_MILLIS;
mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
assertEquals(expectedStats,
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
}
/**
@@ -481,7 +522,7 @@
expectedStats.countInWindow = 3;
expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + 30_000;
assertEquals(expectedStats,
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
}
@Test
@@ -556,20 +597,20 @@
mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 5, 24 * HOUR_IN_MILLIS);
// No sessions saved yet.
- mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+ maybeScheduleCleanupAlarm();
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any());
// Test with only one timing session saved.
final long now = mInjector.getElapsedRealtime();
logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 6 * HOUR_IN_MILLIS);
- mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+ maybeScheduleCleanupAlarm();
verify(mAlarmManager, timeout(1000).times(1))
.set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
// Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again.
logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS);
logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS);
- mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+ maybeScheduleCleanupAlarm();
verify(mAlarmManager, times(1))
.set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
}
@@ -587,14 +628,14 @@
mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 8 * HOUR_IN_MILLIS);
// No sessions saved yet.
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
verify(mAlarmManager, never()).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
// Test with timing sessions out of window.
final long now = mInjector.getElapsedRealtime();
logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 10 * HOUR_IN_MILLIS, 20);
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
verify(mAlarmManager, never()).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
@@ -602,26 +643,26 @@
final long start = now - (6 * HOUR_IN_MILLIS);
final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS;
logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, start, 5);
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
verify(mAlarmManager, never()).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
// Add some more sessions, but still in quota.
logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS, 1);
logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 3);
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
verify(mAlarmManager, never()).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
// Test when out of quota.
logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 1);
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
any(Handler.class));
// Alarm already scheduled, so make sure it's not scheduled again.
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
verify(mAlarmManager, times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
any(Handler.class));
@@ -656,7 +697,7 @@
// Start in ACTIVE bucket.
mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
inOrder.verify(mAlarmManager, never())
.setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
any(Handler.class));
@@ -665,40 +706,40 @@
// And down from there.
final long expectedWorkingAlarmTime = outOfQuotaTime + (2 * HOUR_IN_MILLIS);
mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
inOrder.verify(mAlarmManager, timeout(1000).times(1))
.setWindow(anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
final long expectedFrequentAlarmTime = outOfQuotaTime + (8 * HOUR_IN_MILLIS);
mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
inOrder.verify(mAlarmManager, timeout(1000).times(1))
.setWindow(anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
final long expectedRareAlarmTime = outOfQuotaTime + (24 * HOUR_IN_MILLIS);
mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
inOrder.verify(mAlarmManager, timeout(1000).times(1))
.setWindow(anyInt(), eq(expectedRareAlarmTime), anyLong(),
eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
// And back up again.
mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
inOrder.verify(mAlarmManager, timeout(1000).times(1))
.setWindow(anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
inOrder.verify(mAlarmManager, timeout(1000).times(1))
.setWindow(anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
- mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
inOrder.verify(mAlarmManager, timeout(1000).times(1))
.cancel(any(AlarmManager.OnAlarmListener.class));
inOrder.verify(mAlarmManager, timeout(1000).times(0))
@@ -745,14 +786,14 @@
mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
ExecutionStats stats =
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
assertEquals(0, stats.countInWindow);
for (int i = 0; i < 10; ++i) {
mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
assertEquals(0, stats.countInWindow);
}
}
@@ -766,14 +807,14 @@
mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
ExecutionStats stats =
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
assertEquals(0, stats.countInWindow);
for (int i = 0; i < 10; ++i) {
mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
assertEquals(i + 1, stats.countInWindow);
}
}
@@ -785,14 +826,14 @@
mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
ExecutionStats stats =
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
assertEquals(0, stats.countInWindow);
for (int i = 0; i < 10; ++i) {
mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
assertEquals(0, stats.countInWindow);
}
}
@@ -806,14 +847,14 @@
mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
ExecutionStats stats =
- mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+ getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
assertEquals(0, stats.countInWindow);
for (int i = 0; i < 10; ++i) {
mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
advanceElapsedClock(10 * SECOND_IN_MILLIS);
- mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+ updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
assertEquals(i + 1, stats.countInWindow);
}
}
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 d45e312..fc4d8d8 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -61,7 +61,6 @@
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.Executor;
@@ -264,8 +263,8 @@
BatteryStats.WAKE_TYPE_PARTIAL, false);
verifyNoMoreInteractions(mWakeLockLog, mBatteryStats);
- WorkSource worksourceOld = Mockito.mock(WorkSource.class);
- WorkSource worksourceNew = Mockito.mock(WorkSource.class);
+ WorkSource worksourceOld = new WorkSource(/*uid=*/ 1);
+ WorkSource worksourceNew = new WorkSource(/*uid=*/ 2);
mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, worksourceOld, /* historyTag= */ null,
@@ -309,6 +308,40 @@
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
}
+ @Test
+ public void
+ test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
+ 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");
+ }
+ };
+
+ final int uid = 1234;
+ final int pid = 5678;
+ mTestLooper.dispatchAll();
+ WorkSource worksourceOld = new WorkSource(/*uid=*/ 1);
+ WorkSource worksourceNew = new WorkSource(/*uid=*/ 2);
+
+ mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksourceOld, /* historyTag= */ null,
+ exceptingCallback,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksourceNew, /* newHistoryTag= */ null,
+ exceptingCallback);
+ // The newWorksource is modified before notifier could process it.
+ worksourceNew.set(/*uid=*/ 3);
+
+ mTestLooper.dispatchAll();
+ verify(mBatteryStats).noteChangeWakelockFromSource(worksourceOld, pid,
+ "wakelockTag", null, BatteryStats.WAKE_TYPE_PARTIAL,
+ new WorkSource(/*uid=*/ 2), pid, "wakelockTag", null,
+ BatteryStats.WAKE_TYPE_FULL, false);
+ }
+
@Test
public void testOnWakeLockListener_FullWakeLock_ProcessesOnHandler() throws RemoteException {
diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 1838fe8..53e3143 100644
--- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -37,6 +38,8 @@
import android.os.Looper;
import android.os.PowerMonitor;
import android.os.ResultReceiver;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
@@ -58,6 +61,7 @@
import com.android.server.testutils.FakeDeviceConfigInterface;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -101,6 +105,8 @@
private static final int STATE_RESIDENCY_COUNT = 4;
private static final int APP_UID = 10042;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
private PowerStatsService mService;
private TestPowerStatsHALWrapper mPowerStatsHALWrapper = new TestPowerStatsHALWrapper();
@@ -1198,4 +1204,22 @@
assertThat(Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
.map(PowerMonitor::getName).toList()).contains("ENERGYCONSUMER0");
}
+
+ @EnableFlags(Flags.FLAG_VERIFY_NON_NULL_ARGUMENTS)
+ @Test
+ public void testGetSupportedPowerMonitors_withNullArguments() {
+ IPowerStatsService iPowerStatsService = mService.getIPowerStatsServiceForTest();
+ assertThrows(NullPointerException.class,
+ () -> iPowerStatsService.getSupportedPowerMonitors(null));
+ }
+
+ @EnableFlags(Flags.FLAG_VERIFY_NON_NULL_ARGUMENTS)
+ @Test
+ public void testGetPowerMonitorReadings_withNullArguments() {
+ IPowerStatsService iPowerStatsService = mService.getIPowerStatsServiceForTest();
+ assertThrows(NullPointerException.class, () -> iPowerStatsService.getPowerMonitorReadings(
+ null, new GetPowerMonitorsResult()));
+ assertThrows(NullPointerException.class, () -> iPowerStatsService.getPowerMonitorReadings(
+ new int[] {0}, null));
+ }
}
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 dbab54b..1db46bf 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -19,6 +19,8 @@
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_FALSE;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
@@ -103,6 +105,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.AlarmManagerInternal;
import com.android.server.FgThread;
import com.android.server.SystemService;
import com.android.server.am.UserState.KeyEvictedCallback;
@@ -122,6 +125,7 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -201,7 +205,8 @@
doNothing().when(mInjector).systemServiceManagerOnUserStopped(anyInt());
doNothing().when(mInjector).systemServiceManagerOnUserCompletedEvent(
anyInt(), anyInt());
- doNothing().when(mInjector).activityManagerForceStopPackage(anyInt(), anyString());
+ doNothing().when(mInjector).activityManagerForceStopUserPackages(anyInt(),
+ anyString(), anyBoolean());
doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
@@ -727,6 +732,39 @@
mUserController.getRunningUsersLU());
}
+ /** Test scheduling stopping of background users - reschedule if user with a scheduled alarm. */
+ @Test
+ public void testScheduleStopOfBackgroundUser_rescheduleIfAlarm() throws Exception {
+ mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
+ /* backgroundUserScheduledStopTimeSecs= */ 2);
+
+ setUpAndStartUserInBackground(TEST_USER_ID);
+ assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID),
+ new HashSet<>(mUserController.getRunningUsersLU()));
+
+ // Initially, the background user has an alarm that will fire soon. So don't stop the user.
+ when(mInjector.mAlarmManagerInternal.getNextAlarmTriggerTimeForUser(eq(TEST_USER_ID)))
+ .thenReturn(System.currentTimeMillis() + Duration.ofMinutes(2).toMillis());
+ assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
+ assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID),
+ new HashSet<>(mUserController.getRunningUsersLU()));
+
+ // Now, that alarm is gone and the next alarm isn't for a long time. Do stop the user.
+ when(mInjector.mAlarmManagerInternal.getNextAlarmTriggerTimeForUser(eq(TEST_USER_ID)))
+ .thenReturn(System.currentTimeMillis() + Duration.ofDays(1).toMillis());
+ assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
+ assertEquals(newHashSet(SYSTEM_USER_ID),
+ new HashSet<>(mUserController.getRunningUsersLU()));
+
+ // No-one is scheduled to stop anymore.
+ assertAndProcessScheduledStopBackgroundUser(false, null);
+ verify(mInjector.mAlarmManagerInternal, never())
+ .getNextAlarmTriggerTimeForUser(eq(SYSTEM_USER_ID));
+ }
+
/**
* Process queued SCHEDULED_STOP_BACKGROUND_USER_MSG message, if expected.
* @param userId the user we are checking to see whether it is scheduled.
@@ -936,6 +974,61 @@
new HashSet<>(mUserController.getRunningUsersLU()));
}
+ @Test
+ public void testEarlyPackageKillEnabledForUserSwitch_enabled() {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ true,
+ /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+ assertTrue(mUserController
+ .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+ }
+
+ @Test
+ public void testEarlyPackageKillEnabledForUserSwitch_withoutDelayUserDataLocking() {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ false,
+ /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+ assertFalse(mUserController
+ .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+ }
+
+ @Test
+ public void testEarlyPackageKillEnabledForUserSwitch_withPrevSystemUser() {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ true,
+ /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+ assertFalse(mUserController
+ .isEarlyPackageKillEnabledForUserSwitch(SYSTEM_USER_ID, TEST_USER_ID1));
+ }
+
+ @Test
+ public void testEarlyPackageKillEnabledForUserSwitch_stopUserOnSwitchModeOn() {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ false,
+ /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+ mUserController.setStopUserOnSwitch(STOP_USER_ON_SWITCH_TRUE);
+
+ assertTrue(mUserController
+ .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+ }
+
+ @Test
+ public void testEarlyPackageKillEnabledForUserSwitch_stopUserOnSwitchModeOff() {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ true,
+ /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+ mUserController.setStopUserOnSwitch(STOP_USER_ON_SWITCH_FALSE);
+
+ assertFalse(mUserController
+ .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+ }
+
+
/**
* Test that, in getRunningUsersLU, parents come after their profile, even if the profile was
* started afterwards.
@@ -1689,6 +1782,7 @@
private final WindowManagerService mWindowManagerMock;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final PowerManagerInternal mPowerManagerInternal;
+ private final AlarmManagerInternal mAlarmManagerInternal;
private final KeyguardManager mKeyguardManagerMock;
private final LockPatternUtils mLockPatternUtilsMock;
@@ -1711,6 +1805,7 @@
mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
mStorageManagerMock = mock(IStorageManager.class);
mPowerManagerInternal = mock(PowerManagerInternal.class);
+ mAlarmManagerInternal = mock(AlarmManagerInternal.class);
mKeyguardManagerMock = mock(KeyguardManager.class);
when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
mLockPatternUtilsMock = mock(LockPatternUtils.class);
@@ -1781,6 +1876,11 @@
}
@Override
+ AlarmManagerInternal getAlarmManagerInternal() {
+ return mAlarmManagerInternal;
+ }
+
+ @Override
KeyguardManager getKeyguardManager() {
return mKeyguardManagerMock;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 1074f7b..6577e09 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -250,120 +250,120 @@
assertMessageValidity("04:33:0C:08:10:1E:04:30:08:13:AD:06")
.isEqualTo(ERROR_PARAMETER_SHORT);
// Out of range Day of Month
- assertMessageValidity("04:34:20:0C:16:0F:08:37:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:34:20:0C:22:15:08:55:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
// Out of range Month of Year
- assertMessageValidity("04:33:0C:00:10:1E:04:30:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:33:0C:00:16:30:04:48:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
// Out of range Start Time - Hour
- assertMessageValidity("04:34:04:0C:18:0F:08:37:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:34:04:0C:24:15:08:55:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
// Out of range Start Time - Minute
- assertMessageValidity("04:33:0C:08:10:50:04:30:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:33:0C:08:16:60:04:48:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
// Out of range Duration - Duration Hours
- assertMessageValidity("04:34:04:0C:16:0F:64:37:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:34:04:0C:22:15:9A:55:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
// Out of range Duration - Minute
- assertMessageValidity("04:33:0C:08:10:1E:04:64:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:33:0C:08:16:30:04:60:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
// Invalid Recording Sequence
- assertMessageValidity("04:34:04:0C:16:0F:08:37:88:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:34:04:0C:22:15:08:55:88:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
// Invalid Recording Sequence
- assertMessageValidity("04:33:0C:08:10:1E:04:30:A2:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:33:0C:08:16:30:04:48:A2:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
// Out of range Analogue Broadcast Type
- assertMessageValidity("04:34:04:0C:16:0F:08:37:00:03:EA:60:03").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:34:04:0C:22:15:08:55:00:03:EA:60:03").isEqualTo(ERROR_PARAMETER);
// Out of range Analogue Frequency
- assertMessageValidity("04:33:0C:08:10:1E:04:30:08:00:FF:FF:06").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:33:0C:08:16:30:04:48:08:00:FF:FF:06").isEqualTo(ERROR_PARAMETER);
// Out of range Broadcast System
- assertMessageValidity("04:34:04:0C:16:0F:08:37:00:02:EA:60:20").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:34:04:0C:22:15:08:55:00:02:EA:60:20").isEqualTo(ERROR_PARAMETER);
}
@Test
public void isValid_setDigitalTimer_clearDigitalTimer() {
// Services identified by Digital IDs - ARIB Broadcast System
- assertMessageValidity("04:99:0C:08:15:05:04:1E:00:00:C4:C2:11:D8:75:30").isEqualTo(OK);
+ assertMessageValidity("04:99:0C:08:21:05:04:30:00:00:C4:C2:11:D8:75:30").isEqualTo(OK);
// Service identified by Digital IDs - ATSC Broadcast System
- assertMessageValidity("04:97:1E:07:12:20:50:28:01:01:8B:5E:39:5A").isEqualTo(OK);
+ assertMessageValidity("04:97:1E:07:18:32:80:40:01:01:8B:5E:39:5A").isEqualTo(OK);
// Service identified by Digital IDs - DVB Broadcast System
- assertMessageValidity("04:99:05:0C:06:0A:19:3B:40:19:8B:44:03:11:04:FC").isEqualTo(OK);
+ assertMessageValidity("04:99:05:0C:06:10:25:59:40:19:8B:44:03:11:04:FC").isEqualTo(OK);
// Service identified by Channel - 1 part channel number
- assertMessageValidity("04:97:12:06:0C:2D:5A:19:08:91:04:00:B1").isEqualTo(OK);
+ assertMessageValidity("04:97:12:06:12:45:90:25:08:91:04:00:B1").isEqualTo(OK);
// Service identified by Channel - 2 part channel number
- assertMessageValidity("04:99:15:09:00:0F:00:2D:04:82:09:C8:72:C8").isEqualTo(OK);
+ assertMessageValidity("04:99:15:09:00:15:00:45:04:82:09:C8:72:C8").isEqualTo(OK);
- assertMessageValidity("4F:97:0C:08:15:05:04:1E:00:00:C4:C2:11:D8:75:30")
+ assertMessageValidity("4F:97:0C:08:21:05:04:30:00:00:C4:C2:11:D8:75:30")
.isEqualTo(ERROR_DESTINATION);
- assertMessageValidity("F0:99:15:09:00:0F:00:2D:04:82:09:C8:72:C8").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("F0:99:15:09:00:15:00:45:04:82:09:C8:72:C8").isEqualTo(ERROR_SOURCE);
assertMessageValidity("04:97:1E:12:20:58:01:01:8B:5E:39:5A")
.isEqualTo(ERROR_PARAMETER_SHORT);
// Out of range Day of Month
- assertMessageValidity("04:99:24:0C:06:0A:19:3B:40:19:8B:44:03:11:04:FC")
+ assertMessageValidity("04:99:24:0C:06:10:25:59:40:19:8B:44:03:11:04:FC")
.isEqualTo(ERROR_PARAMETER);
// Out of range Month of Year
- assertMessageValidity("04:97:12:10:0C:2D:5A:19:08:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:97:12:10:12:45:90:25:08:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
// Out of range Start Time - Hour
- assertMessageValidity("04:99:0C:08:20:05:04:1E:00:00:C4:C2:11:D8:75:30")
+ assertMessageValidity("04:99:0C:08:24:05:04:30:00:00:C4:C2:11:D8:75:30")
.isEqualTo(ERROR_PARAMETER);
// Out of range Start Time - Minute
- assertMessageValidity("04:97:15:09:00:4B:00:2D:04:82:09:C8:72:C8")
+ assertMessageValidity("04:97:15:09:00:60:00:45:04:82:09:C8:72:C8")
.isEqualTo(ERROR_PARAMETER);
// Out of range Duration - Duration Hours
- assertMessageValidity("04:99:1E:07:12:20:78:28:01:01:8B:5E:39:5A")
+ assertMessageValidity("04:99:1E:07:18:32:9A:40:01:01:8B:5E:39:5A")
.isEqualTo(ERROR_PARAMETER);
// Out of range Duration - Minute
- assertMessageValidity("04:97:05:0C:06:0A:19:48:40:19:8B:44:03:11:04:FC")
+ assertMessageValidity("04:97:05:0C:06:10:25:60:40:19:8B:44:03:11:04:FC")
.isEqualTo(ERROR_PARAMETER);
// Invalid Recording Sequence
- assertMessageValidity("04:99:12:06:0C:2D:5A:19:90:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:99:12:06:12:45:90:25:90:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
// Invalid Recording Sequence
assertMessageValidity("04:97:0C:08:15:05:04:1E:A1:00:C4:C2:11:D8:75:30")
.isEqualTo(ERROR_PARAMETER);
// Invalid Digital Broadcast System
- assertMessageValidity("04:99:1E:07:12:20:50:28:01:04:8B:5E:39:5A")
+ assertMessageValidity("04:99:1E:07:18:32:80:40:01:04:8B:5E:39:5A")
.isEqualTo(ERROR_PARAMETER);
// Invalid Digital Broadcast System
- assertMessageValidity("04:97:05:0C:06:0A:19:3B:40:93:8B:44:03:11:04:FC")
+ assertMessageValidity("04:97:05:0C:06:10:25:59:40:93:8B:44:03:11:04:FC")
.isEqualTo(ERROR_PARAMETER);
// Insufficient data for ARIB Broadcast system
- assertMessageValidity("04:99:0C:08:15:05:04:1E:00:00:C4:C2:11:D8:75")
+ assertMessageValidity("04:99:0C:08:21:05:04:30:00:00:C4:C2:11:D8:75")
.isEqualTo(ERROR_PARAMETER);
// Insufficient data for ATSC Broadcast system
- assertMessageValidity("04:97:1E:07:12:20:50:28:01:01:8B:5E:39").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:97:1E:07:18:32:80:40:01:01:8B:5E:39").isEqualTo(ERROR_PARAMETER);
// Insufficient data for DVB Broadcast system
- assertMessageValidity("04:99:05:0C:06:0A:19:3B:40:19:8B:44:03:11:04")
+ assertMessageValidity("04:99:05:0C:06:10:25:59:40:19:8B:44:03:11:04")
.isEqualTo(ERROR_PARAMETER);
// Insufficient data for 2 part channel number
- assertMessageValidity("04:97:15:09:00:0F:00:2D:04:82:09:C8:72").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:97:15:09:00:15:00:45:04:82:09:C8:72").isEqualTo(ERROR_PARAMETER);
// Invalid Channel Number format
- assertMessageValidity("04:99:12:06:0C:2D:5A:19:08:91:0D:00:B1").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:99:12:06:12:45:90:25:08:91:0D:00:B1").isEqualTo(ERROR_PARAMETER);
}
@Test
public void isValid_setExternalTimer_clearExternalTimer() {
- assertMessageValidity("40:A1:0C:08:15:05:04:1E:02:04:20").isEqualTo(OK);
- assertMessageValidity("40:A2:14:09:12:28:4B:19:10:05:10:00").isEqualTo(OK);
+ assertMessageValidity("40:A1:0C:08:21:05:04:30:02:04:20").isEqualTo(OK);
+ assertMessageValidity("40:A2:14:09:18:40:75:25:10:05:10:00").isEqualTo(OK);
- assertMessageValidity("4F:A1:0C:08:15:05:04:1E:02:04:20").isEqualTo(ERROR_DESTINATION);
- assertMessageValidity("F4:A2:14:09:12:28:4B:19:10:05:10:00").isEqualTo(ERROR_SOURCE);
- assertMessageValidity("40:A1:0C:08:15:05:04:1E:02:04").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("4F:A1:0C:08:21:05:04:30:02:04:20").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F4:A2:14:09:18:40:75:25:10:05:10:00").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:A1:0C:08:21:05:04:30:02:04").isEqualTo(ERROR_PARAMETER_SHORT);
// Out of range Day of Month
- assertMessageValidity("40:A2:28:09:12:28:4B:19:10:05:10:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A2:28:09:18:40:75:25:10:05:10:00").isEqualTo(ERROR_PARAMETER);
// Out of range Month of Year
- assertMessageValidity("40:A1:0C:0F:15:05:04:1E:02:04:20").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A1:0C:0F:21:05:04:30:02:04:20").isEqualTo(ERROR_PARAMETER);
// Out of range Start Time - Hour
- assertMessageValidity("40:A2:14:09:1A:28:4B:19:10:05:10:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A2:14:09:24:40:75:25:10:05:10:00").isEqualTo(ERROR_PARAMETER);
// Out of range Start Time - Minute
- assertMessageValidity("40:A1:0C:08:15:48:04:1E:02:04:20").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A1:0C:08:21:60:04:30:02:04:20").isEqualTo(ERROR_PARAMETER);
// Out of range Duration - Duration Hours
- assertMessageValidity("40:A2:14:09:12:28:66:19:10:05:10:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A2:14:09:18:40:9A:25:10:05:10:00").isEqualTo(ERROR_PARAMETER);
// Out of range Duration - Minute
- assertMessageValidity("40:A1:0C:08:15:05:04:3F:02:04:20").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A1:0C:08:21:05:04:60:02:04:20").isEqualTo(ERROR_PARAMETER);
// Invalid Recording Sequence
- assertMessageValidity("40:A2:14:09:12:28:4B:19:84:05:10:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A2:14:09:18:40:75:25:84:05:10:00").isEqualTo(ERROR_PARAMETER);
// Invalid Recording Sequence
assertMessageValidity("40:A1:0C:08:15:05:04:1E:94:04:20").isEqualTo(ERROR_PARAMETER);
// Invalid external source specifier
- assertMessageValidity("40:A2:14:09:12:28:4B:19:10:08:10:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A2:14:09:18:40:75:25:10:08:10:00").isEqualTo(ERROR_PARAMETER);
// Invalid External PLug
- assertMessageValidity("04:A1:0C:08:15:05:04:1E:02:04:00").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("04:A1:0C:08:21:05:04:30:02:04:00").isEqualTo(ERROR_PARAMETER);
// Invalid Physical Address
- assertMessageValidity("40:A2:14:09:12:28:4B:19:10:05:10:10").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:A2:14:09:18:40:75:25:10:05:10:10").isEqualTo(ERROR_PARAMETER);
}
@Test
@@ -396,9 +396,9 @@
// Non programmed - Invalid not programmed error info
assertMessageValidity("40:35:DE").isEqualTo(ERROR_PARAMETER);
// Programmed - Might not be enough space available - Invalid duration hours
- assertMessageValidity("40:35:BB:96:1C").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:35:BB:9A:28").isEqualTo(ERROR_PARAMETER);
// Not programmed - Duplicate - Invalid duration minutes
- assertMessageValidity("40:35:EE:52:4A").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:35:EE:82:60").isEqualTo(ERROR_PARAMETER);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 3d68849..dddab65 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -108,6 +108,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -165,6 +166,7 @@
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SimpleClock;
import android.os.SystemClock;
@@ -197,6 +199,7 @@
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
import com.android.internal.util.test.FsUtil;
@@ -2310,6 +2313,70 @@
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
+ @SuppressWarnings("GuardedBy") // For not holding mUidRulesFirstLock
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS)
+ public void testRulesNeverAppliedToCoreUids() throws Exception {
+ clearInvocations(mNetworkManager);
+
+ final int coreAppId = Process.FIRST_APPLICATION_UID - 102;
+ final int coreUid = UserHandle.getUid(USER_ID, coreAppId);
+
+ // Enable all restrictions and add this core uid to all allowlists.
+ mService.mDeviceIdleMode = true;
+ mService.mRestrictPower = true;
+ setRestrictBackground(true);
+ expectHasUseRestrictedNetworksPermission(coreUid, true);
+ enableRestrictedMode(true);
+ final NetworkPolicyManagerInternal internal = LocalServices.getService(
+ NetworkPolicyManagerInternal.class);
+ internal.setLowPowerStandbyActive(true);
+ internal.setLowPowerStandbyAllowlist(new int[]{coreUid});
+ internal.onTempPowerSaveWhitelistChange(coreAppId, true, REASON_OTHER, "testing");
+
+ when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean()))
+ .thenReturn(new int[]{coreAppId});
+ mPowerAllowlistReceiver.onReceive(mServiceContext, null);
+
+ // A normal uid would undergo a rule change from denied to allowed on all chains, but we
+ // should not request any rule change for this core uid.
+ verify(mNetworkManager, never()).setFirewallUidRule(anyInt(), eq(coreUid), anyInt());
+ verify(mNetworkManager, never()).setFirewallUidRules(anyInt(),
+ argThat(ar -> ArrayUtils.contains(ar, coreUid)), any(int[].class));
+ }
+
+ @SuppressWarnings("GuardedBy") // For not holding mUidRulesFirstLock
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS)
+ public void testRulesNeverAppliedToUidsWithoutInternetPermission() throws Exception {
+ clearInvocations(mNetworkManager);
+
+ mService.mInternetPermissionMap.clear();
+ expectHasInternetPermission(UID_A, false);
+
+ // Enable all restrictions and add this uid to all allowlists.
+ mService.mDeviceIdleMode = true;
+ mService.mRestrictPower = true;
+ setRestrictBackground(true);
+ expectHasUseRestrictedNetworksPermission(UID_A, true);
+ enableRestrictedMode(true);
+ final NetworkPolicyManagerInternal internal = LocalServices.getService(
+ NetworkPolicyManagerInternal.class);
+ internal.setLowPowerStandbyActive(true);
+ internal.setLowPowerStandbyAllowlist(new int[]{UID_A});
+ internal.onTempPowerSaveWhitelistChange(APP_ID_A, true, REASON_OTHER, "testing");
+
+ when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean()))
+ .thenReturn(new int[]{APP_ID_A});
+ mPowerAllowlistReceiver.onReceive(mServiceContext, null);
+
+ // A normal uid would undergo a rule change from denied to allowed on all chains, but we
+ // should not request any rule this uid without the INTERNET permission.
+ verify(mNetworkManager, never()).setFirewallUidRule(anyInt(), eq(UID_A), anyInt());
+ verify(mNetworkManager, never()).setFirewallUidRules(anyInt(),
+ argThat(ar -> ArrayUtils.contains(ar, UID_A)), any(int[].class));
+ }
+
private boolean isUidState(int uid, int procState, int procStateSeq, int capability) {
final NetworkPolicyManager.UidState uidState = mService.getUidStateForTest(uid);
if (uidState == null) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index d714db99..7912156 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -121,6 +121,9 @@
// Making a copy of mUsersToRemove to avoid ConcurrentModificationException
mUsersToRemove.stream().toList().forEach(this::removeUser);
mUserRemovalWaiter.close();
+
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ mContext.getUser());
}
private void removeExistingUsers() {
@@ -935,6 +938,35 @@
@MediumTest
@Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY)
+ public void testSetUserAdminThrowsSecurityException() throws Exception {
+ UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0);
+ assertThat(targetUser.isAdmin()).isFalse();
+
+ try {
+ // 1. Target User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ targetUser.getUserHandle());
+ assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id));
+
+ // 2. Current User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ mContext.getUser());
+ assertThrows(SecurityException.class, () -> mUserManager.setUserAdmin(targetUser.id));
+
+ } finally {
+ // Ensure restriction is removed even if test fails
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ mContext.getUser());
+ }
+ }
+
+ @MediumTest
+ @Test
public void testRevokeUserAdmin() throws Exception {
UserInfo userInfo = createUser("Admin", /*flags=*/ UserInfo.FLAG_ADMIN);
assertThat(userInfo.isAdmin()).isTrue();
@@ -959,6 +991,37 @@
@MediumTest
@Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY)
+ public void testRevokeUserAdminThrowsSecurityException() throws Exception {
+ UserInfo targetUser = createUser("SecondaryUser", /*flags=*/ 0);
+ assertThat(targetUser.isAdmin()).isFalse();
+
+ try {
+ // 1. Target User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ targetUser.getUserHandle());
+ assertThrows(SecurityException.class, () -> mUserManager
+ .revokeUserAdmin(targetUser.id));
+
+ // 2. Current User Restriction
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true,
+ mContext.getUser());
+ assertThrows(SecurityException.class, () -> mUserManager
+ .revokeUserAdmin(targetUser.id));
+
+ } finally {
+ // Ensure restriction is removed even if test fails
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ targetUser.getUserHandle());
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false,
+ mContext.getUser());
+ }
+ }
+
+ @MediumTest
+ @Test
public void testGetProfileParent() throws Exception {
assumeManagedUsersSupported();
int mainUserId = mUserManager.getMainUser().getIdentifier();
@@ -1184,6 +1247,23 @@
}
}
+ // Make sure createUser for ADMIN would fail if we have DISALLOW_GRANT_ADMIN.
+ @MediumTest
+ @Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_UNICORN_MODE_REFACTORING_FOR_HSUM_READ_ONLY)
+ public void testCreateAdminUser_disallowGrantAdmin() throws Exception {
+ final int creatorId = ActivityManager.getCurrentUser();
+ final UserHandle creatorHandle = asHandle(creatorId);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, true, creatorHandle);
+ try {
+ UserInfo createdInfo = createUser("SecondaryUser", /*flags=*/ UserInfo.FLAG_ADMIN);
+ assertThat(createdInfo).isNull();
+ } finally {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
+ creatorHandle);
+ }
+ }
+
// Make sure createProfile would fail if we have DISALLOW_ADD_CLONE_PROFILE.
@MediumTest
@Test
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index fad10f7..5518082 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -182,7 +182,7 @@
+ " <device-state>\n"
+ " <identifier>1</identifier>\n"
+ " <properties>\n"
- + " <property>PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS</property>\n"
+ " </properties>\n"
+ " <conditions/>\n"
+ " </device-state>\n"
@@ -338,11 +338,9 @@
+ " <identifier>4</identifier>\n"
+ " <name>THERMAL_TEST</name>\n"
+ " <properties>\n"
- + " <property>PROPERTY_EMULATED_ONLY</property>\n"
- + " <property>PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL"
- + "</property>\n"
- + " <property>PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE"
- + "</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_EMULATED_ONLY</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL</property>\n"
+ + " <property>com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE</property>\n"
+ " </properties>\n"
+ " </device-state>\n"
+ "</device-state-config>\n";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index f6e1162..af7f703 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -16,7 +16,6 @@
package com.android.server.notification;
-import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
@@ -31,13 +30,11 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import android.app.Flags;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.content.pm.IPackageManager;
import android.net.Uri;
import android.os.IInterface;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.Condition;
@@ -150,57 +147,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_UI)
- public void notifyConditions_appCannotUndoUserEnablement() {
- ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
- mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
- mock(ServiceConnection.class), 33, 100);
- // First, user enabled mode
- Condition[] userConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
- };
- mProviders.notifyConditions("package", msi, userConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
-
- // Second, app tries to disable it, but cannot
- Condition[] appConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_FALSE)
- };
- mProviders.notifyConditions("package", msi, appConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
- }
-
- @Test
- @EnableFlags(Flags.FLAG_MODES_UI)
- public void notifyConditions_appCanTakeoverUserEnablement() {
- ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
- mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
- mock(ServiceConnection.class), 33, 100);
- // First, user enabled mode
- Condition[] userConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
- };
- mProviders.notifyConditions("package", msi, userConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
-
- // Second, app now thinks the rule should be on due it its intelligence
- Condition[] appConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_TRUE)
- };
- mProviders.notifyConditions("package", msi, appConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
-
- // Lastly, app can turn rule off when its intelligence think it should be off
- appConditions = new Condition[] {
- new Condition(Uri.parse("a"), "summary", STATE_FALSE)
- };
- mProviders.notifyConditions("package", msi, appConditions);
- verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
-
- verifyNoMoreInteractions(mCallback);
- }
-
- @Test
public void testRemoveDefaultFromConfig() {
final int userId = 0;
ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 14ad15e..643ee4a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2458,6 +2458,74 @@
}
@Test
+ public void testBeepVolume_politeNotif_AvalancheStrategy_mixedNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ // Trigger avalanche trigger intent
+ final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", false);
+ mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+
+ // Regular notification: should beep at 0% volume
+ NotificationRecord r = getBeepyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ Mockito.reset(mRingtonePlayer);
+
+ // Conversation notification
+ mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+ NotificationRecord r2 = getConversationNotificationRecord(mId, false /* insistent */,
+ false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+ true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+ "shortcut");
+
+ // Should beep at 100% volume
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+ verifyBeepVolume(1.0f);
+
+ // Conversation notification on a different channel
+ mChannel = new NotificationChannel("test3", "test3", IMPORTANCE_DEFAULT);
+ NotificationRecord r3 = getConversationNotificationRecord(mId, false /* insistent */,
+ false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+ true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+ "shortcut");
+
+ // Should beep at 50% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
+ verifyBeepVolume(0.5f);
+
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ // Set important conversation
+ mChannel.setImportantConversation(true);
+ r3 = getConversationNotificationRecord(mId, false /* insistent */,
+ false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+ true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+ "shortcut");
+
+ // important conversation should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ verify(mAccessibilityService, times(5)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
+ }
+
+ @Test
public void testBeepVolume_politeNotif_Avalanche_exemptEmergency() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
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 60c4ac7..e70ed5f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -33,6 +33,8 @@
import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.service.notification.ZenModeConfig.XML_VERSION_MODES_API;
import static android.service.notification.ZenModeConfig.ZEN_TAG;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_NONE;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
@@ -524,7 +526,7 @@
rule.zenMode = INTERRUPTION_FILTER;
rule.modified = true;
rule.name = NAME;
- rule.snoozing = true;
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
@@ -546,7 +548,7 @@
ZenModeConfig.ZenRule parceled = new ZenModeConfig.ZenRule(parcel);
assertEquals(rule.pkg, parceled.pkg);
- assertEquals(rule.snoozing, parceled.snoozing);
+ assertEquals(rule.getConditionOverride(), parceled.getConditionOverride());
assertEquals(rule.enabler, parceled.enabler);
assertEquals(rule.component, parceled.component);
assertEquals(rule.configurationActivity, parceled.configurationActivity);
@@ -625,7 +627,7 @@
rule.zenMode = INTERRUPTION_FILTER;
rule.modified = true;
rule.name = NAME;
- rule.snoozing = true;
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
@@ -662,7 +664,7 @@
assertEquals(rule.pkg, fromXml.pkg);
// always resets on reboot
- assertFalse(fromXml.snoozing);
+ assertEquals(OVERRIDE_NONE, fromXml.getConditionOverride());
//should all match original
assertEquals(rule.component, fromXml.component);
assertEquals(rule.configurationActivity, fromXml.configurationActivity);
@@ -1115,7 +1117,6 @@
rule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
rule.modified = true;
rule.name = "name";
- rule.snoozing = false;
rule.pkg = "b";
config.automaticRules.put("key", rule);
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 9af0021..91eb2ed 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -158,7 +158,10 @@
RuleDiff.FIELD_ZEN_DEVICE_EFFECTS,
RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS));
}
- if (!(Flags.modesApi() && Flags.modesUi())) {
+ if (Flags.modesApi() && Flags.modesUi()) {
+ exemptFields.add(RuleDiff.FIELD_SNOOZING); // Obsolete.
+ } else {
+ exemptFields.add(RuleDiff.FIELD_CONDITION_OVERRIDE);
exemptFields.add(RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS);
}
return exemptFields;
@@ -339,7 +342,7 @@
rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
rule.modified = false;
rule.name = "name";
- rule.snoozing = true;
+ rule.setConditionOverride(ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE);
rule.pkg = "a";
if (android.app.Flags.modesApi()) {
rule.allowManualInvocation = true;
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 776a840..ed8ebc8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -51,6 +51,7 @@
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_SCHEDULE;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_FALSE;
@@ -61,6 +62,9 @@
import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_APP;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
@@ -2999,11 +3003,13 @@
assertWithMessage("Failure for origin " + origin.name())
.that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
assertWithMessage("Failure for origin " + origin.name())
- .that(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing)
- .isTrue();
+ .that(mZenModeHelper.mConfig.automaticRules
+ .get(activeRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_DEACTIVATE);
assertWithMessage("Failure for origin " + origin.name())
- .that(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing)
- .isFalse();
+ .that(mZenModeHelper.mConfig.automaticRules
+ .get(inactiveRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
}
}
@@ -3038,16 +3044,20 @@
assertWithMessage("Failure for origin " + origin.name()).that(
mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(activeRuleId).snoozing).isFalse();
+ config.automaticRules.get(activeRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+ config.automaticRules.get(inactiveRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
} else {
assertWithMessage("Failure for origin " + origin.name()).that(
mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(activeRuleId).snoozing).isTrue();
+ config.automaticRules.get(activeRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_DEACTIVATE);
assertWithMessage("Failure for origin " + origin.name()).that(
- config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+ config.automaticRules.get(inactiveRuleId).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
}
}
}
@@ -4288,7 +4298,7 @@
rule.zenMode = INTERRUPTION_FILTER_ZR;
rule.modified = true;
rule.name = NAME;
- rule.snoozing = true;
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
rule.zenPolicy = POLICY;
@@ -4962,7 +4972,7 @@
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_FALSE),
- ORIGIN_APP, SYSTEM_UID);
+ ORIGIN_APP, CUSTOM_PKG_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
@@ -5000,7 +5010,8 @@
mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, ZenModeConfig.ORIGIN_SYSTEM,
"", SYSTEM_UID);
- assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
+ assertEquals(OVERRIDE_NONE,
+ mZenModeHelper.mConfig.automaticRules.get(createdId).getConditionOverride());
}
@Test
@@ -5713,7 +5724,7 @@
assertThat(storedRule.isAutomaticActive()).isFalse();
assertThat(storedRule.isTrueOrUnknown()).isFalse();
assertThat(storedRule.condition).isNull();
- assertThat(storedRule.snoozing).isFalse();
+ assertThat(storedRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
}
@@ -6039,15 +6050,18 @@
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
ZEN_MODE_IMPORTANT_INTERRUPTIONS);
assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
- assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ORIGIN_APP, "test", "test", 0);
- assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+ .isEqualTo(OVERRIDE_DEACTIVATE);
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
ZEN_MODE_ALARMS);
- assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+ .isEqualTo(OVERRIDE_NONE);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
.isEqualTo(STATE_TRUE);
}
@@ -6451,6 +6465,213 @@
ORIGIN_UNKNOWN);
}
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualActivation_appliesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+
+ ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+ assertThat(zenRule.condition).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualActivationAndThenDeactivation_removesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
+ SOURCE_CONTEXT);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+ assertThat(zenRule.condition).isNull();
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isNull();
+
+ // Bonus check: app has resumed control over the rule and can now turn it on.
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn, ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualDeactivationAndThenReactivation_removesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
+ SOURCE_CONTEXT);
+ Condition autoOff = new Condition(rule.getConditionId(), "auto-off", STATE_FALSE,
+ SOURCE_CONTEXT);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn, ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+
+ // Bonus check: app has resumed control over the rule and can now turn it off.
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOff, ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOff);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualDeactivation_appliesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ ZenRule zenRuleOn = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRuleOn.isAutomaticActive()).isTrue();
+ assertThat(zenRuleOn.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRuleOn.condition).isNotNull();
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ ZenRule zenRuleOff = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRuleOff.isAutomaticActive()).isFalse();
+ assertThat(zenRuleOff.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+ assertThat(zenRuleOff.condition).isNotNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_ifManualActive_appCannotDeactivateBeforeActivating() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_ifManualInactive_appCannotReactivateBeforeDeactivating() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+ ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ }
+
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/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 72ef888..6f06050 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -119,6 +119,7 @@
USAGE_PHYSICAL_EMULATION,
USAGE_RINGTONE,
USAGE_TOUCH,
+ USAGE_IME_FEEDBACK
};
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -525,7 +526,7 @@
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
for (int usage : ALL_USAGES) {
- if (usage == USAGE_TOUCH) {
+ if (usage == USAGE_TOUCH || usage == USAGE_IME_FEEDBACK) {
assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
} else {
assertVibrationNotIgnoredForUsage(usage);
@@ -601,14 +602,14 @@
@Test
public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
+ setKeyboardVibrationSettingsSupported(true);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0 /* OFF*/);
- setKeyboardVibrationSettingsSupported(true);
// Keyboard touch ignored.
assertVibrationIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build(),
Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -617,7 +618,7 @@
assertVibrationNotIgnoredForUsage(USAGE_TOUCH);
assertVibrationNotIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.build());
@@ -625,9 +626,9 @@
@Test
public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
+ setKeyboardVibrationSettingsSupported(true);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
- setKeyboardVibrationSettingsSupported(true);
// General touch ignored.
assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -635,16 +636,16 @@
// Keyboard touch not ignored.
assertVibrationNotIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build());
}
@Test
- public void shouldIgnoreVibration_notSupportKeyboardVibration_ignoresKeyboardTouchVibration() {
+ public void shouldIgnoreVibration_notSupportKeyboardVibration_followsTouchFeedbackSettings() {
+ setKeyboardVibrationSettingsSupported(false);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
- setKeyboardVibrationSettingsSupported(false);
// General touch ignored.
assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -652,7 +653,7 @@
// Keyboard touch ignored.
assertVibrationIgnoredForAttributes(
new VibrationAttributes.Builder()
- .setUsage(USAGE_TOUCH)
+ .setUsage(USAGE_IME_FEEDBACK)
.setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build(),
Vibration.Status.IGNORED_FOR_SETTINGS);
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
index 12f5714f..d223272 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
@@ -43,10 +43,10 @@
import java.util.concurrent.Executor;
/**
- * Tests for {@link DisplayRotationCompatPolicy}.
+ * Tests for {@link CameraStateMonitor}.
*
* Build/Install/Run:
- * atest WmTests:DisplayRotationCompatPolicyTests
+ * atest WmTests:CameraStateMonitorTests
*/
@SmallTest
@Presubmit
@@ -68,23 +68,14 @@
private ActivityRecord mActivity;
private Task mTask;
- // Simulates a listener which will not react to the change on a particular activity.
- private final FakeCameraCompatStateListener mNotInterestedListener =
- new FakeCameraCompatStateListener(
- /*onCameraOpenedReturnValue=*/ false,
- /*simulateUnsuccessfulCloseOnce=*/ false);
// Simulates a listener which will react to the change on a particular activity - for example
// put the activity in a camera compat mode.
- private final FakeCameraCompatStateListener mInterestedListener =
- new FakeCameraCompatStateListener(
- /*onCameraOpenedReturnValue=*/ true,
- /*simulateUnsuccessfulCloseOnce=*/ false);
+ private final FakeCameraCompatStateListener mListener =
+ new FakeCameraCompatStateListener(/* simulateUnsuccessfulCloseOnce= */ false);
// Simulates a listener which for some reason cannot process `onCameraClosed` event once it
// first arrives - this means that the update needs to be postponed.
private final FakeCameraCompatStateListener mListenerCannotClose =
- new FakeCameraCompatStateListener(
- /*onCameraOpenedReturnValue=*/ true,
- /*simulateUnsuccessfulCloseOnce=*/ true);
+ new FakeCameraCompatStateListener(/* simulateUnsuccessfulCloseOnce= */ true);
@Before
public void setUp() throws Exception {
@@ -129,44 +120,31 @@
@After
public void tearDown() {
// Remove all listeners.
- mCameraStateMonitor.removeCameraStateListener(mNotInterestedListener);
- mCameraStateMonitor.removeCameraStateListener(mInterestedListener);
+ mCameraStateMonitor.removeCameraStateListener(mListener);
mCameraStateMonitor.removeCameraStateListener(mListenerCannotClose);
// Reset the listener's state.
- mNotInterestedListener.resetCounters();
- mInterestedListener.resetCounters();
+ mListener.resetCounters();
mListenerCannotClose.resetCounters();
}
@Test
public void testOnCameraOpened_listenerAdded_notifiesCameraOpened() {
- mCameraStateMonitor.addCameraStateListener(mNotInterestedListener);
+ mCameraStateMonitor.addCameraStateListener(mListener);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
- assertEquals(1, mNotInterestedListener.mOnCameraOpenedCounter);
+ assertEquals(1, mListener.mOnCameraOpenedCounter);
}
@Test
- public void testOnCameraOpened_listenerReturnsFalse_doesNotNotifyCameraClosed() {
- mCameraStateMonitor.addCameraStateListener(mNotInterestedListener);
- // Listener returns false on `onCameraOpened`.
- mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-
- mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
-
- assertEquals(0, mNotInterestedListener.mOnCameraClosedCounter);
- }
-
- @Test
- public void testOnCameraOpened_listenerReturnsTrue_notifyCameraClosed() {
- mCameraStateMonitor.addCameraStateListener(mInterestedListener);
+ public void testOnCameraOpened_cameraClosed_notifyCameraClosed() {
+ mCameraStateMonitor.addCameraStateListener(mListener);
// Listener returns true on `onCameraOpened`.
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
- assertEquals(1, mInterestedListener.mOnCameraClosedCounter);
+ assertEquals(1, mListener.mOnCameraClosedCounter);
}
@Test
@@ -182,32 +160,22 @@
@Test
public void testReconnectedToDifferentCamera_notifiesListener() {
- mCameraStateMonitor.addCameraStateListener(mInterestedListener);
+ mCameraStateMonitor.addCameraStateListener(mListener);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1);
- assertEquals(2, mInterestedListener.mOnCameraOpenedCounter);
+ assertEquals(2, mListener.mOnCameraOpenedCounter);
}
@Test
public void testDifferentAppConnectedToCamera_notifiesListener() {
- mCameraStateMonitor.addCameraStateListener(mInterestedListener);
+ mCameraStateMonitor.addCameraStateListener(mListener);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);
- assertEquals(2, mInterestedListener.mOnCameraOpenedCounter);
- }
-
- @Test
- public void testCameraAlreadyClosed_notifiesListenerOnce() {
- mCameraStateMonitor.addCameraStateListener(mInterestedListener);
- mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
- mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
- mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
-
- assertEquals(1, mInterestedListener.mOnCameraClosedCounter);
+ assertEquals(2, mListener.mOnCameraOpenedCounter);
}
private void configureActivity(@NonNull String packageName) {
@@ -232,7 +200,6 @@
int mOnCameraOpenedCounter = 0;
int mOnCameraClosedCounter = 0;
- boolean mOnCameraOpenedReturnValue = true;
private boolean mOnCameraClosedReturnValue = true;
/**
@@ -242,17 +209,14 @@
* subsequent calls. This fake implementation tests the
* retry mechanism in {@link CameraStateMonitor}.
*/
- FakeCameraCompatStateListener(boolean onCameraOpenedReturnValue,
- boolean simulateUnsuccessfulCloseOnce) {
- mOnCameraOpenedReturnValue = onCameraOpenedReturnValue;
+ FakeCameraCompatStateListener(boolean simulateUnsuccessfulCloseOnce) {
mOnCameraClosedReturnValue = !simulateUnsuccessfulCloseOnce;
}
@Override
- public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+ public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
@NonNull String cameraId) {
mOnCameraOpenedCounter++;
- return mOnCameraOpenedReturnValue;
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 771e290..e57e36d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -59,6 +59,7 @@
TestWindowContainer(WindowManagerService wm) {
super(wm);
+ setVisibleRequested(true);
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index e2524a2..ddadbc4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -115,6 +115,17 @@
}
@Test
+ public void testPrimaryDisplayUnchanged_whenWindowingModeAlreadySet_NoFreeformSupport() {
+ mPrimaryDisplay.getDefaultTaskDisplayArea().setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+
+ mDisplayWindowSettings.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+ assertEquals(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW,
+ mPrimaryDisplay.getDefaultTaskDisplayArea().getWindowingMode());
+ }
+
+ @Test
public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() {
mWm.mAtmService.mSupportsFreeformWindowManagement = true;
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 ffaa2d8..400fe8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -45,6 +46,12 @@
import java.util.function.Supplier;
+/**
+ * Test class for {@link Letterbox}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:LetterboxTest
+ */
@SmallTest
@Presubmit
public class LetterboxTest {
@@ -53,21 +60,21 @@
SurfaceControlMocker mSurfaces;
SurfaceControl.Transaction mTransaction;
- private boolean mAreCornersRounded = false;
- private int mColor = Color.BLACK;
- private boolean mHasWallpaperBackground = false;
- private int mBlurRadius = 0;
- private float mDarkScrimAlpha = 0.5f;
private SurfaceControl mParentSurface = mock(SurfaceControl.class);
+ private AppCompatLetterboxOverrides mLetterboxOverrides;
@Before
public void setUp() throws Exception {
mSurfaces = new SurfaceControlMocker();
+ mLetterboxOverrides = mock(AppCompatLetterboxOverrides.class);
+ doReturn(false).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
+ doReturn(Color.valueOf(Color.BLACK)).when(mLetterboxOverrides)
+ .getLetterboxBackgroundColor();
+ doReturn(false).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
+ doReturn(0).when(mLetterboxOverrides).getLetterboxWallpaperBlurRadiusPx();
+ doReturn(0.5f).when(mLetterboxOverrides).getLetterboxWallpaperDarkScrimAlpha();
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
- () -> mAreCornersRounded, () -> Color.valueOf(mColor),
- () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
- mock(AppCompatReachabilityPolicy.class),
- () -> mParentSurface);
+ mock(AppCompatReachabilityPolicy.class), mLetterboxOverrides, () -> mParentSurface);
mTransaction = spy(StubTransaction.class);
}
@@ -183,7 +190,8 @@
verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
- mColor = Color.GREEN;
+ doReturn(Color.valueOf(Color.GREEN)).when(mLetterboxOverrides)
+ .getLetterboxBackgroundColor();
assertTrue(mLetterbox.needsApplySurfaceChanges());
@@ -200,12 +208,12 @@
verify(mTransaction).setAlpha(mSurfaces.top, 1.0f);
assertFalse(mLetterbox.needsApplySurfaceChanges());
- mHasWallpaperBackground = true;
+ doReturn(true).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
assertTrue(mLetterbox.needsApplySurfaceChanges());
applySurfaceChanges();
- verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
+ verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, /* alpha */ 0.5f);
}
@Test
@@ -234,7 +242,7 @@
@Test
public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
- mAreCornersRounded = true;
+ doReturn(true).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
applySurfaceChanges();
@@ -243,7 +251,7 @@
@Test
public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
- mHasWallpaperBackground = true;
+ doReturn(true).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
applySurfaceChanges();
@@ -252,7 +260,7 @@
@Test
public void testNotIntersectsOrFullyContains_cornersRounded() {
- mAreCornersRounded = true;
+ doReturn(true).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
applySurfaceChanges();
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 695068a..cf321ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -107,12 +107,14 @@
// Makes requested sizes different
mainWindow.mRequestedWidth = opaqueBounds.width() - 1;
mainWindow.mRequestedHeight = opaqueBounds.height() - 1;
- assertNull(mActivity.mLetterboxUiController.getCropBoundsIfNeeded(mainWindow));
+ final AppCompatLetterboxPolicy letterboxPolicy =
+ mActivity.mAppCompatController.getAppCompatLetterboxPolicy();
+ assertNull(letterboxPolicy.getCropBoundsIfNeeded(mainWindow));
// Makes requested sizes equals
mainWindow.mRequestedWidth = opaqueBounds.width();
mainWindow.mRequestedHeight = opaqueBounds.height();
- assertNotNull(mActivity.mLetterboxUiController.getCropBoundsIfNeeded(mainWindow));
+ assertNotNull(letterboxPolicy.getCropBoundsIfNeeded(mainWindow));
}
@Test
@@ -123,12 +125,15 @@
// Do not apply crop if taskbar is collapsed
taskbar.setFrame(TASKBAR_COLLAPSED_BOUNDS);
- assertNull(mController.getExpandedTaskbarOrNull(mainWindow));
+ assertNull(AppCompatUtils.getExpandedTaskbarOrNull(mainWindow));
mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4,
SCREEN_WIDTH - SCREEN_WIDTH / 4, SCREEN_HEIGHT - SCREEN_HEIGHT / 4);
- final Rect noCrop = mController.getCropBoundsIfNeeded(mainWindow);
+ final AppCompatLetterboxPolicy letterboxPolicy =
+ mActivity.mAppCompatController.getAppCompatLetterboxPolicy();
+
+ final Rect noCrop = letterboxPolicy.getCropBoundsIfNeeded(mainWindow);
assertNotEquals(null, noCrop);
assertEquals(0, noCrop.left);
assertEquals(0, noCrop.top);
@@ -145,12 +150,14 @@
// Apply crop if taskbar is expanded
taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
- assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+ assertNotNull(AppCompatUtils.getExpandedTaskbarOrNull(mainWindow));
mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
SCREEN_HEIGHT);
- final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
+ final AppCompatLetterboxPolicy letterboxPolicy =
+ mActivity.mAppCompatController.getAppCompatLetterboxPolicy();
+ final Rect crop = letterboxPolicy.getCropBoundsIfNeeded(mainWindow);
assertNotEquals(null, crop);
assertEquals(0, crop.left);
assertEquals(0, crop.top);
@@ -169,7 +176,7 @@
// Apply crop if taskbar is expanded
taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
- assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
+ assertNotNull(AppCompatUtils.getExpandedTaskbarOrNull(mainWindow));
// With SizeCompat scaling
doReturn(true).when(mActivity).inSizeCompatMode();
mainWindow.mInvGlobalScale = scaling;
@@ -180,7 +187,9 @@
final int appWidth = mLetterboxedPortraitTaskBounds.width();
final int appHeight = mLetterboxedPortraitTaskBounds.height();
- final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
+ final AppCompatLetterboxPolicy letterboxPolicy =
+ mActivity.mAppCompatController.getAppCompatLetterboxPolicy();
+ final Rect crop = letterboxPolicy.getCropBoundsIfNeeded(mainWindow);
assertNotEquals(null, crop);
assertEquals(0, crop.left);
assertEquals(0, crop.top);
@@ -210,7 +219,8 @@
insets.setRoundedCorners(roundedCorners);
mAppCompatConfiguration.setLetterboxActivityCornersRadius(-1);
- assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(expectedRadius, mActivity.mAppCompatController.getAppCompatLetterboxPolicy()
+ .getRoundedCornersRadius(mainWindow));
}
@Test
@@ -223,18 +233,21 @@
mainWindow.mInvGlobalScale = invGlobalScale;
mAppCompatConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
+ final AppCompatLetterboxPolicy letterboxPolicy =
+ mActivity.mAppCompatController.getAppCompatLetterboxPolicy();
+
doReturn(true).when(mActivity).isInLetterboxAnimation();
- assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(expectedRadius, letterboxPolicy.getRoundedCornersRadius(mainWindow));
doReturn(false).when(mActivity).isInLetterboxAnimation();
- assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(expectedRadius, letterboxPolicy.getRoundedCornersRadius(mainWindow));
doReturn(false).when(mActivity).isVisibleRequested();
doReturn(false).when(mActivity).isVisible();
- assertEquals(0, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(0, letterboxPolicy.getRoundedCornersRadius(mainWindow));
doReturn(true).when(mActivity).isInLetterboxAnimation();
- assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(expectedRadius, letterboxPolicy.getRoundedCornersRadius(mainWindow));
}
@Test
@@ -244,14 +257,17 @@
final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
mAppCompatConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
+ final AppCompatLetterboxPolicy letterboxPolicy =
+ mActivity.mAppCompatController.getAppCompatLetterboxPolicy();
+
mainWindow.mInvGlobalScale = -1f;
- assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(configurationRadius, letterboxPolicy.getRoundedCornersRadius(mainWindow));
mainWindow.mInvGlobalScale = 0f;
- assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(configurationRadius, letterboxPolicy.getRoundedCornersRadius(mainWindow));
mainWindow.mInvGlobalScale = 1f;
- assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
+ assertEquals(configurationRadius, letterboxPolicy.getRoundedCornersRadius(mainWindow));
}
private WindowState mockForGetCropBoundsAndRoundedCorners(@Nullable InsetsSource taskbar) {
@@ -292,7 +308,8 @@
@Test
public void testIsLetterboxEducationEnabled() {
- mController.isLetterboxEducationEnabled();
+ mActivity.mAppCompatController.getAppCompatLetterboxOverrides()
+ .isLetterboxEducationEnabled();
verify(mAppCompatConfiguration).getIsEducationEnabled();
}
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 aa997ac..36696a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -247,9 +247,9 @@
.build();
mTask.addChild(translucentActivity);
- spyOn(translucentActivity.mLetterboxUiController);
- doReturn(true).when(translucentActivity.mLetterboxUiController)
- .shouldShowLetterboxUi(any());
+ spyOn(translucentActivity.mAppCompatController.getAppCompatLetterboxPolicy());
+ doReturn(true).when(translucentActivity.mAppCompatController
+ .getAppCompatLetterboxPolicy()).shouldShowLetterboxUi(any());
addWindowToActivity(translucentActivity);
translucentActivity.mRootWindowContainer.performSurfacePlacement();
@@ -257,7 +257,7 @@
final Function<ActivityRecord, Rect> innerBoundsOf =
(ActivityRecord a) -> {
final Rect bounds = new Rect();
- a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+ a.getLetterboxInnerBounds(bounds);
return bounds;
};
final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
@@ -369,7 +369,7 @@
final Function<ActivityRecord, Rect> innerBoundsOf =
(ActivityRecord a) -> {
final Rect bounds = new Rect();
- a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+ a.getLetterboxInnerBounds(bounds);
return bounds;
};
@@ -632,16 +632,15 @@
assertEquals(window, mActivity.findMainWindow());
- spyOn(mActivity.mLetterboxUiController);
doReturn(true).when(mActivity).isVisibleRequested();
- assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
- mActivity.findMainWindow()));
+ assertTrue(mActivity.mAppCompatController.getAppCompatLetterboxPolicy()
+ .shouldShowLetterboxUi(mActivity.findMainWindow()));
window.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
- assertFalse(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
- mActivity.findMainWindow()));
+ assertFalse(mActivity.mAppCompatController.getAppCompatLetterboxPolicy()
+ .shouldShowLetterboxUi(mActivity.findMainWindow()));
}
@Test
@@ -1757,7 +1756,8 @@
// Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
mActivity.mRootWindowContainer.performSurfacePlacement();
- LetterboxDetails letterboxDetails = mActivity.mLetterboxUiController.getLetterboxDetails();
+ LetterboxDetails letterboxDetails = mActivity.mAppCompatController
+ .getAppCompatLetterboxPolicy().getLetterboxDetails();
assertEquals(dh / scale, letterboxDetails.getLetterboxInnerBounds().width());
assertEquals(dw / scale, letterboxDetails.getLetterboxInnerBounds().height());
@@ -3825,7 +3825,8 @@
mActivity.mRootWindowContainer.performSurfacePlacement();
- LetterboxDetails letterboxDetails = mActivity.mLetterboxUiController.getLetterboxDetails();
+ LetterboxDetails letterboxDetails = mActivity.mAppCompatController
+ .getAppCompatLetterboxPolicy().getLetterboxDetails();
// Letterboxed activity at bottom
assertEquals(new Rect(0, 2100, 1400, 2800), mActivity.getBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 2b611b7..18255b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1488,7 +1488,7 @@
@Test
public void testReorderWithParents() {
/*
- root
+ default TDA
____|______
| |
firstTda secondTda
@@ -1496,10 +1496,12 @@
firstRootTask secondRootTask
*/
- final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
- final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
- mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+ final TaskDisplayArea firstTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "FirstTaskDisplayArea",
FEATURE_VENDOR_FIRST);
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "SecondTaskDisplayArea",
+ FEATURE_VENDOR_FIRST + 1);
final Task firstRootTask = firstTaskDisplayArea.createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
final Task secondRootTask = secondTaskDisplayArea.createRootTask(
@@ -1508,9 +1510,6 @@
.setTask(firstRootTask).build();
final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
.setTask(secondRootTask).build();
- // This assertion is just a defense to ensure that firstRootTask is not the top most
- // by default
- assertThat(mDisplayContent.getTopRootTask()).isEqualTo(secondRootTask);
WindowContainerTransaction wct = new WindowContainerTransaction();
// Reorder to top
@@ -1533,6 +1532,67 @@
}
@Test
+ public void testReorderDisplayArea() {
+ /*
+ defaultTda
+ ____|______
+ | |
+ firstTda secondTda
+ | |
+ firstRootTask secondRootTask
+
+ */
+ final TaskDisplayArea firstTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "FirstTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "SecondTaskDisplayArea",
+ FEATURE_VENDOR_FIRST + 1);
+ final Task firstRootTask = firstTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+ .setTask(firstRootTask).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(secondRootTask).build();
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ // Reorder to top
+ wct.reorder(firstTaskDisplayArea.mRemoteToken.toWindowContainerToken(), true /* onTop */,
+ true /* includingParents */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertThat(mDisplayContent.getTopRootTask()).isEqualTo(firstRootTask);
+
+ // Reorder to bottom
+ wct.reorder(firstTaskDisplayArea.mRemoteToken.toWindowContainerToken(), false /* onTop */,
+ true /* includingParents */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertThat(mDisplayContent.getBottomMostTask()).isEqualTo(firstRootTask);
+ }
+
+ @Test
+ public void testReparentDisplayAreaUnsupported() {
+ final TaskDisplayArea firstTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "FirstTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "SecondTaskDisplayArea",
+ FEATURE_VENDOR_FIRST + 1);
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(firstTaskDisplayArea.mRemoteToken.toWindowContainerToken(),
+ secondTaskDisplayArea.mRemoteToken.toWindowContainerToken(),
+ true /* onTop */
+ );
+
+ assertThrows(UnsupportedOperationException.class, () ->
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct)
+ );
+ }
+
+ @Test
public void testAppearDeferThenVanish() {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createRootTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ea2abf7..4a8a2e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -924,7 +924,8 @@
mSystemServicesTestRule.addProcess("pkgName", "procName",
WindowManagerService.MY_PID, WindowManagerService.MY_UID);
}
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer);
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer,
+ new Bundle());
}
/** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING
index 6e84543..6ceb763 100644
--- a/services/usage/java/com/android/server/usage/TEST_MAPPING
+++ b/services/usage/java/com/android/server/usage/TEST_MAPPING
@@ -1,12 +1,7 @@
{
"presubmit": [
{
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.app.usage"
- }
- ]
+ "name": "FrameworksCoreTests_usage"
},
{
"name": "FrameworksServicesTests",
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b9a001d..afd5720 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9992,6 +9992,16 @@
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
+ /**
+ * Defines the NIDD (Non-IP Data Delivery) APN to be used for carrier roaming to satellite
+ * attachment. For more on NIDD, see 3GPP TS 29.542.
+ * Note this config is the only source of truth regarding the definition of the APN.
+ *
+ * @hide
+ */
+ public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING =
+ "satellite_nidd_apn_name_string";
+
/** @hide */
@IntDef({
CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC,
@@ -11224,6 +11234,7 @@
sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
(int) TimeUnit.SECONDS.toMillis(30));
sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
+ sDefaults.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0);
sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911);
diff --git a/telephony/java/android/telephony/PcoData.java b/telephony/java/android/telephony/PcoData.java
index 39e4f2f..3cc32c6 100644
--- a/telephony/java/android/telephony/PcoData.java
+++ b/telephony/java/android/telephony/PcoData.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.uicc.IccUtils;
+
import java.util.Arrays;
import java.util.Objects;
@@ -84,8 +86,8 @@
@Override
public String toString() {
- return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + ", contents[" +
- contents.length + "])";
+ return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + " "
+ + IccUtils.bytesToHexString(contents) + ")";
}
@Override
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index db167c0..127bbff 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1211,6 +1211,8 @@
.append(", mIsDataRoamingFromRegistration=")
.append(mIsDataRoamingFromRegistration)
.append(", mIsIwlanPreferred=").append(mIsIwlanPreferred)
+ .append(", mIsUsingNonTerrestrialNetwork=")
+ .append(isUsingNonTerrestrialNetwork())
.append("}").toString();
}
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index ad6db2d..e657d7f 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -254,7 +254,6 @@
*/
public static final String KEY_PROVISION_SATELLITE_TOKENS = "provision_satellite";
-
/**
* The request was successfully processed.
*/
@@ -2643,7 +2642,7 @@
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public void requestProvisionSubscriberIds(@NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<List<ProvisionSubscriberId>, SatelliteException> callback) {
+ @NonNull OutcomeReceiver<List<SatelliteSubscriberInfo>, SatelliteException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
@@ -2655,10 +2654,10 @@
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == SATELLITE_RESULT_SUCCESS) {
if (resultData.containsKey(KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN)) {
- List<ProvisionSubscriberId> list =
+ List<SatelliteSubscriberInfo> list =
resultData.getParcelableArrayList(
KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN,
- ProvisionSubscriberId.class);
+ SatelliteSubscriberInfo.class);
executor.execute(() -> Binder.withCleanCallingIdentity(() ->
callback.onResult(list)));
} else {
@@ -2743,9 +2742,9 @@
}
/**
- * Deliver the list of provisioned satellite subscriber ids.
+ * Deliver the list of provisioned satellite subscriber infos.
*
- * @param list List of ProvisionSubscriberId.
+ * @param list The list of provisioned satellite subscriber infos.
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
*
@@ -2754,7 +2753,7 @@
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public void provisionSatellite(@NonNull List<ProvisionSubscriberId> list,
+ public void provisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
similarity index 94%
rename from telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
rename to telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
index fe46db8..992c9ae 100644
--- a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
@@ -16,4 +16,4 @@
package android.telephony.satellite;
-parcelable ProvisionSubscriberId;
+parcelable SatelliteSubscriberInfo;
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
similarity index 83%
rename from telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
rename to telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index 3e6f743..f26219b 100644
--- a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -26,7 +26,7 @@
import java.util.Objects;
/**
- * ProvisionSubscriberId
+ * SatelliteSubscriberInfo
*
* Satellite Gateway client will use these subscriber ids to register with satellite gateway service
* which identify user subscription with unique subscriber ids. These subscriber ids can be any
@@ -35,7 +35,7 @@
* @hide
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public final class ProvisionSubscriberId implements Parcelable {
+public final class SatelliteSubscriberInfo implements Parcelable {
/** provision subscriberId */
@NonNull
private String mSubscriberId;
@@ -49,14 +49,14 @@
/**
* @hide
*/
- public ProvisionSubscriberId(@NonNull String subscriberId, @NonNull int carrierId,
+ public SatelliteSubscriberInfo(@NonNull String subscriberId, @NonNull int carrierId,
@NonNull String niddApn) {
this.mCarrierId = carrierId;
this.mSubscriberId = subscriberId;
this.mNiddApn = niddApn;
}
- private ProvisionSubscriberId(Parcel in) {
+ private SatelliteSubscriberInfo(Parcel in) {
readFromParcel(in);
}
@@ -72,16 +72,16 @@
}
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final @android.annotation.NonNull Creator<ProvisionSubscriberId> CREATOR =
- new Creator<ProvisionSubscriberId>() {
+ public static final @android.annotation.NonNull Creator<SatelliteSubscriberInfo> CREATOR =
+ new Creator<SatelliteSubscriberInfo>() {
@Override
- public ProvisionSubscriberId createFromParcel(Parcel in) {
- return new ProvisionSubscriberId(in);
+ public SatelliteSubscriberInfo createFromParcel(Parcel in) {
+ return new SatelliteSubscriberInfo(in);
}
@Override
- public ProvisionSubscriberId[] newArray(int size) {
- return new ProvisionSubscriberId[size];
+ public SatelliteSubscriberInfo[] newArray(int size) {
+ return new SatelliteSubscriberInfo[size];
}
};
@@ -148,7 +148,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- ProvisionSubscriberId that = (ProvisionSubscriberId) o;
+ SatelliteSubscriberInfo that = (SatelliteSubscriberInfo) o;
return mSubscriberId.equals(that.mSubscriberId) && mCarrierId
== that.mCarrierId && mNiddApn.equals(that.mNiddApn);
}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
similarity index 95%
rename from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
rename to telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
index 460de8c..fb44f87 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
@@ -19,7 +19,7 @@
/**
* {@hide}
*/
-parcelable ProvisionSubscriberId {
+parcelable SatelliteSubscriberInfo {
/** provision subscriberId */
String subscriberId;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2f8e957..8919703 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -78,7 +78,7 @@
import android.telephony.satellite.NtnSignalStrength;
import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.SatelliteDatagram;
-import android.telephony.satellite.ProvisionSubscriberId;
+import android.telephony.satellite.SatelliteSubscriberInfo;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
@@ -3428,13 +3428,13 @@
void requestIsProvisioned(in String satelliteSubscriberId, in ResultReceiver result);
/**
- * Deliver the list of provisioned satellite subscriber ids.
+ * Deliver the list of provisioned satellite subscriber infos.
*
- * @param list List of provisioned satellite subscriber ids.
+ * @param list The list of provisioned satellite subscriber infos.
* @param result The result receiver that returns whether deliver success or fail.
* @hide
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void provisionSatellite(in List<ProvisionSubscriberId> list, in ResultReceiver result);
+ void provisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result);
}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index f518d53..9676bd7 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -967,7 +967,7 @@
byte[] serializedFplmns = new byte[dataLength];
int offset = 0;
for (String fplmn : fplmns) {
- if (offset >= dataLength) break;
+ if (offset + FPLMN_BYTE_SIZE > dataLength) break;
stringToBcdPlmn(fplmn, serializedFplmns, offset);
offset += FPLMN_BYTE_SIZE;
}
diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp
index e09fbf6..c681ce9 100644
--- a/tests/FlickerTests/ActivityEmbedding/Android.bp
+++ b/tests/FlickerTests/ActivityEmbedding/Android.bp
@@ -24,6 +24,9 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
filegroup {
name: "FlickerTestsOtherCommon-src",
srcs: ["src/**/ActivityEmbeddingTestBase.kt"],
@@ -82,3 +85,123 @@
":FlickerTestsOtherCommon-src",
],
}
+
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
+android_test {
+ name: "FlickerTestsActivityEmbedding",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.server.wm.flicker",
+ instrumentation_target_package: "com.android.server.wm.flicker",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsOtherCommon",
+ ],
+ data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsActivityEmbedding module
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-CatchAll",
+ base: "FlickerTestsActivityEmbedding",
+ exclude_filters: [
+ "com.android.server.wm.flicker.activityembedding.close.CloseSecondaryActivityInSplitTest",
+ "com.android.server.wm.flicker.activityembedding.layoutchange.HorizontalSplitChangeRatioTest",
+ "com.android.server.wm.flicker.activityembedding.open.MainActivityStartsSecondaryWithAlwaysExpandTest",
+ "com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingPlaceholderSplitTest",
+ "com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingSecondaryToSplitTest",
+ "com.android.server.wm.flicker.activityembedding.open.OpenThirdActivityOverSplitTest",
+ "com.android.server.wm.flicker.activityembedding.open.OpenTrampolineActivityTest",
+ "com.android.server.wm.flicker.activityembedding.pip.SecondaryActivityEnterPipTest",
+ "com.android.server.wm.flicker.activityembedding.rotation.RotateSplitNoChangeTest",
+ "com.android.server.wm.flicker.activityembedding.rtl.RTLStartSecondaryWithPlaceholderTest",
+ "com.android.server.wm.flicker.activityembedding.splitscreen.EnterSystemSplitTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Close-CloseSecondaryActivityInSplitTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.close.CloseSecondaryActivityInSplitTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-LayoutChange-HorizontalSplitChangeRatioTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.layoutchange.HorizontalSplitChangeRatioTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Open-MainActivityStartsSecondaryWithAlwaysExpandTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.open.MainActivityStartsSecondaryWithAlwaysExpandTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Open-OpenActivityEmbeddingPlaceholderSplitTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingPlaceholderSplitTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Open-OpenActivityEmbeddingSecondaryToSplitTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingSecondaryToSplitTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Open-OpenThirdActivityOverSplitTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenThirdActivityOverSplitTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Open-OpenTrampolineActivityTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenTrampolineActivityTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Pip-SecondaryActivityEnterPipTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.pip.SecondaryActivityEnterPipTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Rotation-RotateSplitNoChangeTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.rotation.RotateSplitNoChangeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-Rtl-RTLStartSecondaryWithPlaceholderTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.rtl.RTLStartSecondaryWithPlaceholderTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsActivityEmbedding-SplitScreen-EnterSystemSplitTest",
+ base: "FlickerTestsActivityEmbedding",
+ include_filters: ["com.android.server.wm.flicker.activityembedding.splitscreen.EnterSystemSplitTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsActivityEmbedding module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
index ee2c05e..06326f8 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
@@ -36,7 +36,7 @@
teardown { testApp.exit(wmHelper) }
transitions {
this.setRotation(flicker.scenario.endRotation)
- if (!flicker.scenario.isTablet) {
+ if (!usesTaskbar) {
wmHelper.StateSyncBuilder()
.add(navBarInPosition(flicker.scenario.isGesturalNavigation))
.waitForAndVerify()
diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp
index d14a178..8b45740 100644
--- a/tests/FlickerTests/AppClose/Android.bp
+++ b/tests/FlickerTests/AppClose/Android.bp
@@ -33,3 +33,34 @@
static_libs: ["FlickerTestsBase"],
data: ["trace_config/*"],
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsAppClose module
+
+test_module_config {
+ name: "FlickerTestsAppClose-CatchAll",
+ base: "FlickerTestsAppClose",
+ exclude_filters: [
+ "com.android.server.wm.flicker.close.CloseAppBackButtonTest",
+ "com.android.server.wm.flicker.close.CloseAppHomeButtonTest",
+ "com.android.server.wm.flicker.close.",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppClose-CloseAppBackButtonTest",
+ base: "FlickerTestsAppClose",
+ include_filters: ["com.android.server.wm.flicker.close.CloseAppBackButtonTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppClose-CloseAppHomeButtonTest",
+ base: "FlickerTestsAppClose",
+ include_filters: ["com.android.server.wm.flicker.close.CloseAppHomeButtonTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsAppClose module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp
index 72a9065..b61739f 100644
--- a/tests/FlickerTests/AppLaunch/Android.bp
+++ b/tests/FlickerTests/AppLaunch/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_windowing_animations_transitions",
// 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"
@@ -23,6 +24,9 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
filegroup {
name: "FlickerTestsAppLaunchCommon-src",
srcs: ["src/**/common/*"],
@@ -69,3 +73,122 @@
],
data: ["trace_config/*"],
}
+
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
+android_test {
+ name: "FlickerTestsAppLaunch",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsAppLaunchCommon",
+ ],
+ data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsAppLaunch module
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-CatchAll",
+ base: "FlickerTestsAppLaunch",
+ exclude_filters: [
+ "com.android.server.wm.flicker.launch.TaskTransitionTest",
+ "com.android.server.wm.flicker.launch.ActivityTransitionTest",
+ "com.android.server.wm.flicker.launch.OpenAppFromIconColdTest",
+ "com.android.server.wm.flicker.launch.OpenAppFromIntentColdAfterCameraTest",
+ "com.android.server.wm.flicker.launch.OpenAppFromIntentColdTest",
+ "com.android.server.wm.flicker.launch.OpenAppFromIntentWarmTest",
+ "com.android.server.wm.flicker.launch.OpenAppFromLockscreenViaIntentTest",
+ "com.android.server.wm.flicker.launch.OpenAppFromOverviewTest",
+ "com.android.server.wm.flicker.launch.OpenCameraFromHomeOnDoubleClickPowerButtonTest",
+ "com.android.server.wm.flicker.launch.OpenTransferSplashscreenAppFromLauncherTransition",
+ "com.android.server.wm.flicker.launch.OverrideTaskTransitionTest",
+ "com.android.server.wm.flicker.launch.TaskTransitionTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-ActivityTransitionTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.ActivityTransitionTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenAppFromIconColdTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIconColdTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenAppFromIntentColdAfterCameraTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentColdAfterCameraTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenAppFromIntentColdTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentColdTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenAppFromIntentWarmTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentWarmTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenAppFromLockscreenViaIntentTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromLockscreenViaIntentTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenAppFromOverviewTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromOverviewTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenCameraFromHomeOnDoubleClickPowerButtonTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenCameraFromHomeOnDoubleClickPowerButtonTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OpenTransferSplashscreenAppFromLauncherTransition",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OpenTransferSplashscreenAppFromLauncherTransition"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-OverrideTaskTransitionTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.OverrideTaskTransitionTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppLaunch-TaskTransitionTest",
+ base: "FlickerTestsAppLaunch",
+ include_filters: ["com.android.server.wm.flicker.launch.TaskTransitionTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsAppLaunch module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 44ae27c..adeba72 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -75,7 +75,7 @@
@FlakyTest(bugId = 288341660)
@Test
fun navBarLayerVisibilityChanges() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.assertLayers {
this.isInvisible(ComponentNameMatcher.NAV_BAR)
.then()
@@ -97,7 +97,7 @@
@FlakyTest(bugId = 293581770)
@Test
fun navBarWindowsVisibilityChanges() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.assertWm {
this.isNonAppWindowInvisible(ComponentNameMatcher.NAV_BAR)
.then()
@@ -112,7 +112,7 @@
@Presubmit
@Test
fun taskBarLayerIsVisibleAtEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
}
@@ -170,7 +170,7 @@
@Presubmit
@Test
fun navBarLayerIsVisibleAtEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) }
}
@@ -184,7 +184,7 @@
@Presubmit
@Test
override fun appLayerBecomesVisible() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
super.appLayerBecomesVisible()
}
@@ -192,7 +192,7 @@
@FlakyTest(bugId = 227143265)
@Test
fun appLayerBecomesVisibleTablet() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
super.appLayerBecomesVisible()
}
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
index 8a3304b..b497e30 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
@@ -28,6 +28,7 @@
get() = {
super.transition(this)
setup {
+ // By default, launcher doesn't rotate on phones, but rotates on tablets
if (flicker.scenario.isTablet) {
tapl.setExpectedRotation(flicker.scenario.startRotation.value)
} else {
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
index f8fd3586..a6e31d4 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
@@ -103,7 +103,7 @@
@Presubmit
@Test
open fun navBarLayerPositionAtEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerPositionAtEnd()
}
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
index 78d93e1..f80e6b4 100644
--- a/tests/FlickerTests/IME/Android.bp
+++ b/tests/FlickerTests/IME/Android.bp
@@ -24,6 +24,9 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
filegroup {
name: "FlickerTestsImeCommon-src",
srcs: ["src/**/common/*"],
@@ -39,6 +42,9 @@
srcs: ["src/**/ShowImeOnAppStart*"],
}
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
android_test {
name: "FlickerTestsIme",
defaults: ["FlickerTestsDefault"],
@@ -53,6 +59,9 @@
data: ["trace_config/*"],
}
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
java_library {
name: "FlickerTestsImeCommon",
defaults: ["FlickerTestsDefault"],
@@ -107,3 +116,140 @@
],
data: ["trace_config/*"],
}
+
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsIme module
+
+test_module_config {
+ name: "FlickerTestsIme-CatchAll",
+ base: "FlickerTestsIme",
+ exclude_filters: [
+ "com.android.server.wm.flicker.ime.CloseImeOnDismissPopupDialogTest",
+ "com.android.server.wm.flicker.ime.CloseImeOnGoHomeTest",
+ "com.android.server.wm.flicker.ime.CloseImeShownOnAppStartOnGoHomeTest",
+ "com.android.server.wm.flicker.ime.CloseImeShownOnAppStartToAppOnPressBackTest",
+ "com.android.server.wm.flicker.ime.CloseImeToAppOnPressBackTest",
+ "com.android.server.wm.flicker.ime.CloseImeToHomeOnFinishActivityTest",
+ "com.android.server.wm.flicker.ime.OpenImeWindowToFixedPortraitAppTest",
+ "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest",
+ "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromOverviewTest",
+ "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest",
+ "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppTest",
+ "com.android.server.wm.flicker.ime.ShowImeOnUnlockScreenTest",
+ "com.android.server.wm.flicker.ime.ShowImeWhenFocusingOnInputFieldTest",
+ "com.android.server.wm.flicker.ime.ShowImeWhileDismissingThemedPopupDialogTest",
+ "com.android.server.wm.flicker.ime.ShowImeWhileEnteringOverviewTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-CloseImeOnDismissPopupDialogTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.CloseImeOnDismissPopupDialogTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-CloseImeOnGoHomeTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.CloseImeOnGoHomeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-CloseImeShownOnAppStartOnGoHomeTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.CloseImeShownOnAppStartOnGoHomeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-CloseImeShownOnAppStartToAppOnPressBackTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.CloseImeShownOnAppStartToAppOnPressBackTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-CloseImeToAppOnPressBackTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.CloseImeToAppOnPressBackTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-CloseImeToHomeOnFinishActivityTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.CloseImeToHomeOnFinishActivityTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-OpenImeWindowToFixedPortraitAppTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.OpenImeWindowToFixedPortraitAppTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromOverviewTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromOverviewTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeOnUnlockScreenTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnUnlockScreenTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeWhenFocusingOnInputFieldTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhenFocusingOnInputFieldTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeWhileDismissingThemedPopupDialogTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhileDismissingThemedPopupDialogTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsIme-ShowImeWhileEnteringOverviewTest",
+ base: "FlickerTestsIme",
+ include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhileEnteringOverviewTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsIme module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index dc2bd1b..522c68b 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -72,7 +72,7 @@
@Presubmit
@Test
override fun navBarLayerPositionAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
flicker.navBarLayerPositionAtStartAndEnd()
}
@@ -80,7 +80,7 @@
@Presubmit
@Test
fun navBarLayerPositionAtStartAndEndLandscapeOrSeascapeAtStart() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
flicker.navBarLayerPositionAtStartAndEnd()
}
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index c96c760..638d594 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -93,7 +93,7 @@
@Presubmit
@Test
fun navBarLayerIsVisibleAtStartAndEnd3Button() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
flicker.navBarLayerIsVisibleAtStartAndEnd()
}
@@ -105,7 +105,7 @@
@Presubmit
@Test
fun navBarLayerIsInvisibleInLandscapeGestural() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) }
@@ -121,7 +121,7 @@
fun statusBarLayerIsInvisibleInLandscapePhone() {
Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
}
@@ -135,7 +135,7 @@
fun statusBarLayerIsInvisibleInLandscapeTablet() {
Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.statusBarLayerIsVisibleAtStartAndEnd()
}
@@ -174,7 +174,7 @@
@Test
fun statusBarLayerIsInvisibleInLandscape() {
Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
}
diff --git a/tests/FlickerTests/Notification/Android.bp b/tests/FlickerTests/Notification/Android.bp
index 4648383..06daaaf 100644
--- a/tests/FlickerTests/Notification/Android.bp
+++ b/tests/FlickerTests/Notification/Android.bp
@@ -32,3 +32,57 @@
static_libs: ["FlickerTestsBase"],
data: ["trace_config/*"],
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsNotification module
+
+test_module_config {
+ name: "FlickerTestsNotification-CatchAll",
+ base: "FlickerTestsNotification",
+ exclude_filters: [
+ "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest",
+ "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWarmTest",
+ "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWithOverlayAppTest",
+ "com.android.server.wm.flicker.notification.OpenAppFromNotificationColdTest",
+ "com.android.server.wm.flicker.notification.OpenAppFromNotificationWarmTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationColdTest",
+ base: "FlickerTestsNotification",
+ include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationWarmTest",
+ base: "FlickerTestsNotification",
+ include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWarmTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationWithOverlayAppTest",
+ base: "FlickerTestsNotification",
+ include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWithOverlayAppTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsNotification-OpenAppFromNotificationColdTest",
+ base: "FlickerTestsNotification",
+ include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromNotificationColdTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsNotification-OpenAppFromNotificationWarmTest",
+ base: "FlickerTestsNotification",
+ include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromNotificationWarmTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsNotification module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index 07fc230..ad70757 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -151,7 +151,7 @@
@Presubmit
@Test
open fun taskBarWindowIsVisibleAtEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.taskBarWindowIsVisibleAtEnd()
}
@@ -163,7 +163,7 @@
@Presubmit
@Test
open fun taskBarLayerIsVisibleAtEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.taskBarLayerIsVisibleAtEnd()
}
@@ -171,7 +171,7 @@
@Presubmit
@Test
open fun navBarLayerPositionAtEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerPositionAtEnd()
}
@@ -179,14 +179,14 @@
@Presubmit
@Test
open fun navBarLayerIsVisibleAtEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerIsVisibleAtEnd()
}
@Presubmit
@Test
open fun navBarWindowIsVisibleAtEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarWindowIsVisibleAtEnd()
}
diff --git a/tests/FlickerTests/QuickSwitch/Android.bp b/tests/FlickerTests/QuickSwitch/Android.bp
index 8755d0e..4d5dba3 100644
--- a/tests/FlickerTests/QuickSwitch/Android.bp
+++ b/tests/FlickerTests/QuickSwitch/Android.bp
@@ -32,3 +32,41 @@
static_libs: ["FlickerTestsBase"],
data: ["trace_config/*"],
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsQuickswitch module
+
+test_module_config {
+ name: "FlickerTestsQuickswitch-CatchAll",
+ base: "FlickerTestsQuickswitch",
+ exclude_filters: [
+ "com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsBackTest",
+ "com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsForwardTest",
+ "com.android.server.wm.flicker.quickswitch.QuickSwitchFromLauncherTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsQuickswitch-QuickSwitchBetweenTwoAppsBackTest",
+ base: "FlickerTestsQuickswitch",
+ include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsBackTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsQuickswitch-QuickSwitchBetweenTwoAppsForwardTest",
+ base: "FlickerTestsQuickswitch",
+ include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsForwardTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsQuickswitch-QuickSwitchFromLauncherTest",
+ base: "FlickerTestsQuickswitch",
+ include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchFromLauncherTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsQuickswitch module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp
index aceb8ba..0884ef9 100644
--- a/tests/FlickerTests/Rotation/Android.bp
+++ b/tests/FlickerTests/Rotation/Android.bp
@@ -37,3 +37,41 @@
static_libs: ["FlickerTestsBase"],
data: ["trace_config/*"],
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsRotation module
+
+test_module_config {
+ name: "FlickerTestsAppRotation-CatchAll",
+ base: "FlickerTestsRotation",
+ exclude_filters: [
+ "com.android.server.wm.flicker.rotation.ChangeAppRotationTest",
+ "com.android.server.wm.flicker.rotation.OpenShowWhenLockedSeamlessAppRotationTest",
+ "com.android.server.wm.flicker.rotation.SeamlessAppRotationTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppRotation-ChangeAppRotationTest",
+ base: "FlickerTestsRotation",
+ include_filters: ["com.android.server.wm.flicker.rotation.ChangeAppRotationTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppRotation-OpenShowWhenLockedSeamlessAppRotationTest",
+ base: "FlickerTestsRotation",
+ include_filters: ["com.android.server.wm.flicker.rotation.OpenShowWhenLockedSeamlessAppRotationTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "FlickerTestsAppRotation-SeamlessAppRotationTest",
+ base: "FlickerTestsRotation",
+ include_filters: ["com.android.server.wm.flicker.rotation.SeamlessAppRotationTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsRotation module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 060015b..70d762e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -26,6 +26,7 @@
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.wm.shell.Flags
import org.junit.Assume
import org.junit.AssumptionViolatedException
import org.junit.Test
@@ -48,6 +49,9 @@
private val logTag = this::class.java.simpleName
+ protected val usesTaskbar: Boolean
+ get() = flicker.scenario.isTablet || Flags.enableTaskbarOnPhones()
+
/** Specification of the test transition to execute */
abstract val transition: FlickerBuilder.() -> Unit
@@ -87,7 +91,7 @@
@Presubmit
@Test
open fun navBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerIsVisibleAtStartAndEnd()
}
@@ -100,7 +104,7 @@
@Presubmit
@Test
open fun navBarLayerPositionAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarLayerPositionAtStartAndEnd()
}
@@ -112,7 +116,7 @@
@Presubmit
@Test
open fun navBarWindowIsAlwaysVisible() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
flicker.navBarWindowIsAlwaysVisible()
}
@@ -126,7 +130,7 @@
@Presubmit
@Test
open fun navBarWindowIsVisibleAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(usesTaskbar)
flicker.navBarWindowIsVisibleAtStartAndEnd()
}
@@ -139,7 +143,7 @@
@Presubmit
@Test
open fun taskBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.taskBarLayerIsVisibleAtStartAndEnd()
}
@@ -151,7 +155,7 @@
@Presubmit
@Test
open fun taskBarWindowIsAlwaysVisible() {
- Assume.assumeTrue(flicker.scenario.isTablet)
+ Assume.assumeTrue(usesTaskbar)
flicker.taskBarWindowIsAlwaysVisible()
}
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index 827ff4f..ad98e47 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -24,6 +24,7 @@
"flickerlib-parsers",
"perfetto_trace_java_protos",
"flickerlib-trace_processor_shell",
+ "ravenwood-junit",
],
java_resource_dirs: ["res"],
certificate: "platform",
@@ -39,6 +40,7 @@
"platform-test-annotations",
],
srcs: [
+ "src/com/android/internal/graphics/ColorUtilsTest.java",
"src/com/android/internal/util/ParcellingTests.java",
],
auto_gen_config: true,
diff --git a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
index d0bb8e3..38a22f2 100644
--- a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
+++ b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java
@@ -19,14 +19,19 @@
import static org.junit.Assert.assertTrue;
import android.graphics.Color;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
@SmallTest
public class ColorUtilsTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void calculateMinimumBackgroundAlpha_satisfiestContrast() {
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index fad94d4..7d0c596 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -126,30 +126,35 @@
.setMessage("My Test Debug Log Message %b")
.setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG)
.setGroupId(1)
+ .setLocation("com/test/MyTestClass.java:123")
).addMessages(
Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
.setMessageId(2)
.setMessage("My Test Verbose Log Message %b")
.setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE)
.setGroupId(1)
+ .setLocation("com/test/MyTestClass.java:342")
).addMessages(
Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
.setMessageId(3)
.setMessage("My Test Warn Log Message %b")
.setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN)
.setGroupId(1)
+ .setLocation("com/test/MyTestClass.java:563")
).addMessages(
Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
.setMessageId(4)
.setMessage("My Test Error Log Message %b")
.setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR)
.setGroupId(1)
+ .setLocation("com/test/MyTestClass.java:156")
).addMessages(
Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
.setMessageId(5)
.setMessage("My Test WTF Log Message %b")
.setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF)
.setGroupId(1)
+ .setLocation("com/test/MyTestClass.java:192")
);
ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock(
@@ -465,6 +470,26 @@
.isEqualTo("My test message :: test, 2, 4, 6, 0.400000, true");
}
+ @Test
+ public void supportsLocationInformation() throws IOException {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(true).build();
+ try {
+ traceMonitor.start();
+ mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ LogDataType.BOOLEAN, new Object[]{true});
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).hasSize(1);
+ Truth.assertThat(protolog.messages.get(0).getLocation())
+ .isEqualTo("com/test/MyTestClass.java:123");
+ }
+
private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) {
final long messageId = new Random().nextLong();
mViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
diff --git a/tests/Internal/src/com/android/internal/util/ParcellingTests.java b/tests/Internal/src/com/android/internal/util/ParcellingTests.java
index 65a3436..fb63422 100644
--- a/tests/Internal/src/com/android/internal/util/ParcellingTests.java
+++ b/tests/Internal/src/com/android/internal/util/ParcellingTests.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -26,6 +27,7 @@
import com.android.internal.util.Parcelling.BuiltIn.ForInstant;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -38,6 +40,9 @@
@RunWith(JUnit4.class)
public class ParcellingTests {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private Parcel mParcel = Parcel.obtain();
@Test
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 3f9016b..f43cf52 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -113,6 +113,7 @@
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
"link/FeatureFlagsFilter.cpp",
+ "link/FlagDisabledResourceRemover.cpp",
"link/ManifestFixer.cpp",
"link/NoDefaultResourceRemover.cpp",
"link/PrivateAttributeMover.cpp",
@@ -189,6 +190,8 @@
"integration-tests/CommandTests/**/*",
"integration-tests/ConvertTest/**/*",
"integration-tests/DumpTest/**/*",
+ ":resource-flagging-test-app-apk",
+ ":resource-flagging-test-app-r-java",
],
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9444dd9..1c85e9f 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -690,9 +690,7 @@
resource_format = item_iter->second.format;
}
- // Don't bother parsing the item if it is behind a disabled flag
- if (out_resource->flag_status != FlagStatus::Disabled &&
- !ParseItem(parser, out_resource, resource_format)) {
+ if (!ParseItem(parser, out_resource, resource_format)) {
return false;
}
return true;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 2e6ad13..b59b165 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -69,13 +69,8 @@
return TestParse(str, ConfigDescription{});
}
- ::testing::AssertionResult TestParse(StringPiece str, ResourceParserOptions parserOptions) {
- return TestParse(str, ConfigDescription{}, parserOptions);
- }
-
- ::testing::AssertionResult TestParse(
- StringPiece str, const ConfigDescription& config,
- ResourceParserOptions parserOptions = ResourceParserOptions()) {
+ ::testing::AssertionResult TestParse(StringPiece str, const ConfigDescription& config) {
+ ResourceParserOptions parserOptions;
ResourceParser parser(context_->GetDiagnostics(), &table_, android::Source{"test"}, config,
parserOptions);
@@ -247,19 +242,6 @@
EXPECT_FALSE(TestParse(R"(<string name="foo4" translatable="yes">Translate</string>)"));
}
-TEST_F(ResourceParserTest, ParseStringBehindDisabledFlag) {
- FeatureFlagProperties flag_properties(true, false);
- ResourceParserOptions options;
- options.feature_flag_values = {{"falseFlag", flag_properties}};
- ASSERT_TRUE(TestParse(
- R"(<string name="foo" android:featureFlag="falseFlag"
- xmlns:android="http://schemas.android.com/apk/res/android">foo</string>)",
- options));
-
- String* str = test::GetValue<String>(&table_, "string/foo");
- ASSERT_THAT(str, IsNull());
-}
-
TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index c132792..6c3eae1 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -244,6 +244,10 @@
return verbose_;
}
+ void SetVerbose(bool verbose) {
+ verbose_ = verbose;
+ }
+
int GetMinSdkVersion() override {
return min_sdk_;
}
@@ -388,6 +392,8 @@
}
Context context;
+ context.SetVerbose(verbose_);
+
StringPiece path = args[0];
unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
if (apk == nullptr) {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 642a561..56f5288 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -57,6 +57,7 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/FeatureFlagsFilter.h"
+#include "link/FlagDisabledResourceRemover.h"
#include "link/Linkers.h"
#include "link/ManifestFixer.h"
#include "link/NoDefaultResourceRemover.h"
@@ -1840,11 +1841,57 @@
return validate(attr->value);
}
+ class FlagDisabledStringVisitor : public DescendingValueVisitor {
+ public:
+ using DescendingValueVisitor::Visit;
+
+ explicit FlagDisabledStringVisitor(android::StringPool& string_pool)
+ : string_pool_(string_pool) {
+ }
+
+ void Visit(RawString* value) override {
+ value->value = string_pool_.MakeRef("");
+ }
+
+ void Visit(String* value) override {
+ value->value = string_pool_.MakeRef("");
+ }
+
+ void Visit(StyledString* value) override {
+ value->value = string_pool_.MakeRef(android::StyleString{{""}, {}});
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FlagDisabledStringVisitor);
+ android::StringPool& string_pool_;
+ };
+
// Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
// to the IArchiveWriter.
bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
ResourceTable* table) {
TRACE_CALL();
+
+ FlagDisabledStringVisitor visitor(table->string_pool);
+
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ for (auto& config_value : entry->values) {
+ if (config_value->flag_status == FlagStatus::Disabled) {
+ config_value->value->Accept(&visitor);
+ }
+ }
+ }
+ }
+ }
+
+ if (!FlagDisabledResourceRemover{}.Consume(context_, table)) {
+ context_->GetDiagnostics()->Error(android::DiagMessage()
+ << "failed removing resources behind disabled flags");
+ return 1;
+ }
+
const bool keep_raw_values = (context_->GetPackageType() == PackageType::kStaticLib)
|| options_.keep_raw_values;
bool result = FlattenXml(context_, *manifest, kAndroidManifestPath, keep_raw_values,
@@ -2331,6 +2378,12 @@
return 1;
};
+ if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
+ if (!GenerateJavaClasses()) {
+ return 1;
+ }
+ }
+
if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
return 1;
}
@@ -2339,12 +2392,6 @@
return 1;
}
- if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
- if (!GenerateJavaClasses()) {
- return 1;
- }
- }
-
if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) {
return 1;
}
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
new file mode 100644
index 0000000..5932271
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
@@ -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 {
+ // 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_android_resources",
+}
+
+genrule {
+ name: "resource-flagging-test-app-compile",
+ tools: ["aapt2"],
+ srcs: [
+ "res/values/bools.xml",
+ "res/values/bools2.xml",
+ "res/values/strings.xml",
+ ],
+ out: [
+ "values_bools.arsc.flat",
+ "values_bools2.arsc.flat",
+ "values_strings.arsc.flat",
+ ],
+ cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
+ "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
+}
+
+genrule {
+ name: "resource-flagging-test-app-apk",
+ tools: ["aapt2"],
+ // The first input file in the list must be the manifest
+ srcs: [
+ "AndroidManifest.xml",
+ ":resource-flagging-test-app-compile",
+ ],
+ out: [
+ "resapp.apk",
+ ],
+ cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
+}
+
+genrule {
+ name: "resource-flagging-test-app-r-java",
+ tools: ["aapt2"],
+ // The first input file in the list must be the manifest
+ srcs: [
+ "AndroidManifest.xml",
+ ":resource-flagging-test-app-compile",
+ ],
+ out: [
+ "resource-flagging-java/com/android/intenal/flaggedresources/R.java",
+ ],
+ cmd: "$(location aapt2) link -o $(genDir)/resapp.apk --java $(genDir)/resource-flagging-java --manifest $(in)",
+}
+
+java_genrule {
+ name: "resource-flagging-test-app-apk-as-resource",
+ srcs: [
+ ":resource-flagging-test-app-apk",
+ ],
+ out: ["apks_as_resources.res.zip"],
+ tools: ["soong_zip"],
+
+ cmd: "mkdir -p $(genDir)/res/raw && " +
+ "cp $(in) $(genDir)/res/raw/$$(basename $(in)) && " +
+ "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
+}
diff --git a/core/tests/resourceflaggingtests/TestAppAndroidManifest.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/AndroidManifest.xml
similarity index 100%
rename from core/tests/resourceflaggingtests/TestAppAndroidManifest.xml
rename to tools/aapt2/integration-tests/FlaggedResourcesTest/AndroidManifest.xml
diff --git a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
similarity index 82%
rename from core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml
rename to tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
index 8d01465..3e094fb 100644
--- a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
@@ -7,4 +7,6 @@
<bool name="res2" android:featureFlag="test.package.trueFlag">true</bool>
<bool name="res3">false</bool>
+
+ <bool name="res4" android:featureFlag="test.package.falseFlag">true</bool>
</resources>
\ No newline at end of file
diff --git a/core/tests/resourceflaggingtests/flagged_resources_res/values/bools2.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml
similarity index 100%
rename from core/tests/resourceflaggingtests/flagged_resources_res/values/bools2.xml
rename to tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
new file mode 100644
index 0000000..5c0fca1
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <string name="str">plain string</string>
+
+ <string name="str1" android:featureFlag="test.package.falseFlag">DONTFIND</string>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/link/FlagDisabledResourceRemover.cpp b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
new file mode 100644
index 0000000..e3289e2
--- /dev/null
+++ b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
@@ -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.
+ */
+
+#include "link/FlagDisabledResourceRemover.h"
+
+#include <algorithm>
+
+#include "ResourceTable.h"
+
+using android::ConfigDescription;
+
+namespace aapt {
+
+static bool KeepResourceEntry(const std::unique_ptr<ResourceEntry>& entry) {
+ if (entry->values.empty()) {
+ return true;
+ }
+ const auto end_iter = entry->values.end();
+ const auto remove_iter =
+ std::stable_partition(entry->values.begin(), end_iter,
+ [](const std::unique_ptr<ResourceConfigValue>& value) -> bool {
+ return value->flag_status != FlagStatus::Disabled;
+ });
+
+ bool keep = remove_iter != entry->values.begin();
+
+ entry->values.erase(remove_iter, end_iter);
+ return keep;
+}
+
+bool FlagDisabledResourceRemover::Consume(IAaptContext* context, ResourceTable* table) {
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ const auto end_iter = type->entries.end();
+ const auto remove_iter = std::stable_partition(
+ type->entries.begin(), end_iter, [](const std::unique_ptr<ResourceEntry>& entry) -> bool {
+ return KeepResourceEntry(entry);
+ });
+
+ type->entries.erase(remove_iter, end_iter);
+ }
+ }
+ return true;
+}
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/link/FlagDisabledResourceRemover.h b/tools/aapt2/link/FlagDisabledResourceRemover.h
new file mode 100644
index 0000000..2db2cb4
--- /dev/null
+++ b/tools/aapt2/link/FlagDisabledResourceRemover.h
@@ -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.
+ */
+
+#pragma once
+
+#include "android-base/macros.h"
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+// Removes any resource that are behind disabled flags.
+class FlagDisabledResourceRemover : public IResourceTableConsumer {
+ public:
+ FlagDisabledResourceRemover() = default;
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FlagDisabledResourceRemover);
+};
+
+} // namespace aapt
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
new file mode 100644
index 0000000..c901b58
--- /dev/null
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LoadedApk.h"
+#include "cmd/Dump.h"
+#include "io/StringStream.h"
+#include "test/Test.h"
+#include "text/Printer.h"
+
+using ::aapt::io::StringOutputStream;
+using ::aapt::text::Printer;
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+using FlaggedResourcesTest = CommandTestFixture;
+
+static android::NoOpDiagnostics noop_diag;
+
+void DumpStringPoolToString(LoadedApk* loaded_apk, std::string* output) {
+ StringOutputStream output_stream(output);
+ Printer printer(&output_stream);
+
+ DumpStringsCommand command(&printer, &noop_diag);
+ ASSERT_EQ(command.Dump(loaded_apk), 0);
+ output_stream.Flush();
+}
+
+void DumpResourceTableToString(LoadedApk* loaded_apk, std::string* output) {
+ StringOutputStream output_stream(output);
+ Printer printer(&output_stream);
+
+ DumpTableCommand command(&printer, &noop_diag);
+ ASSERT_EQ(command.Dump(loaded_apk), 0);
+ output_stream.Flush();
+}
+
+void DumpChunksToString(LoadedApk* loaded_apk, std::string* output) {
+ StringOutputStream output_stream(output);
+ Printer printer(&output_stream);
+
+ DumpChunks command(&printer, &noop_diag);
+ ASSERT_EQ(command.Dump(loaded_apk), 0);
+ output_stream.Flush();
+}
+
+TEST_F(FlaggedResourcesTest, DisabledStringRemovedFromPool) {
+ auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "resapp.apk"});
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+ std::string output;
+ DumpStringPoolToString(loaded_apk.get(), &output);
+
+ std::string excluded = "DONTFIND";
+ ASSERT_EQ(output.find(excluded), std::string::npos);
+}
+
+TEST_F(FlaggedResourcesTest, DisabledResourcesRemovedFromTable) {
+ auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "resapp.apk"});
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+ std::string output;
+ DumpResourceTableToString(loaded_apk.get(), &output);
+}
+
+TEST_F(FlaggedResourcesTest, DisabledResourcesRemovedFromTableChunks) {
+ auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "resapp.apk"});
+ auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+ std::string output;
+ DumpChunksToString(loaded_apk.get(), &output);
+
+ ASSERT_EQ(output.find("res4"), std::string::npos);
+ ASSERT_EQ(output.find("str1"), std::string::npos);
+}
+
+TEST_F(FlaggedResourcesTest, DisabledResourcesInRJava) {
+ auto r_path = file::BuildPath({android::base::GetExecutableDirectory(), "resource-flagging-java",
+ "com", "android", "intenal", "flaggedresources", "R.java"});
+ std::string r_contents;
+ ::android::base::ReadFileToString(r_path, &r_contents);
+
+ ASSERT_NE(r_contents.find("public static final int res4"), std::string::npos);
+ ASSERT_NE(r_contents.find("public static final int str1"), std::string::npos);
+}
+
+} // namespace aapt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 5dde265..36bfbef 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -58,17 +58,19 @@
// Dump the classes, if specified.
options.inputJarDumpFile.ifSet {
- PrintWriter(it).use { pw -> allClasses.dump(pw) }
- log.i("Dump file created at $it")
+ log.iTime("Dump file created at $it") {
+ PrintWriter(it).use { pw -> allClasses.dump(pw) }
+ }
}
options.inputJarAsKeepAllFile.ifSet {
- PrintWriter(it).use {
- pw -> allClasses.forEach {
- classNode -> printAsTextPolicy(pw, classNode)
+ log.iTime("Dump file created at $it") {
+ PrintWriter(it).use { pw ->
+ allClasses.forEach { classNode ->
+ printAsTextPolicy(pw, classNode)
+ }
}
}
- log.i("Dump file created at $it")
}
// Build the filters.
@@ -91,16 +93,18 @@
// Dump statistics, if specified.
options.statsFile.ifSet {
- PrintWriter(it).use { pw -> stats.dumpOverview(pw) }
- log.i("Dump file created at $it")
+ log.iTime("Dump file created at $it") {
+ PrintWriter(it).use { pw -> stats.dumpOverview(pw) }
+ }
}
options.apiListFile.ifSet {
- PrintWriter(it).use { pw ->
- // TODO, when dumping a jar that's not framework-minus-apex.jar, we need to feed
- // framework-minus-apex.jar so that we can dump inherited methods from it.
- ApiDumper(pw, allClasses, null, filter).dump()
+ log.iTime("API list file created at $it") {
+ PrintWriter(it).use { pw ->
+ // TODO, when dumping a jar that's not framework-minus-apex.jar, we need to feed
+ // framework-minus-apex.jar so that we can dump inherited methods from it.
+ ApiDumper(pw, allClasses, null, filter).dump()
+ }
}
- log.i("API list file created at $it")
}
}
@@ -221,47 +225,48 @@
log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar)
log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
- val start = System.currentTimeMillis()
+ log.iTime("Transforming jar") {
+ val packageRedirector = PackageRedirectRemapper(options.packageRedirects)
- val packageRedirector = PackageRedirectRemapper(options.packageRedirects)
+ var itemIndex = 0
+ var numItemsProcessed = 0
+ var numItems = -1 // == Unknown
- var itemIndex = 0
- var numItemsProcessed = 0
- var numItems = -1 // == Unknown
+ log.withIndent {
+ // Open the input jar file and process each entry.
+ ZipFile(inJar).use { inZip ->
- log.withIndent {
- // Open the input jar file and process each entry.
- ZipFile(inJar).use { inZip ->
+ numItems = inZip.size()
+ val shardStart = numItems * shard / numShards
+ val shardNextStart = numItems * (shard + 1) / numShards
- numItems = inZip.size()
- val shardStart = numItems * shard / numShards
- val shardNextStart = numItems * (shard + 1) / numShards
-
- maybeWithZipOutputStream(outStubJar) { stubOutStream ->
- maybeWithZipOutputStream(outImplJar) { implOutStream ->
- val inEntries = inZip.entries()
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
- val inShard = (shardStart <= itemIndex) && (itemIndex < shardNextStart)
- itemIndex++
- if (!inShard) {
- continue
- }
- convertSingleEntry(inZip, entry, stubOutStream, implOutStream,
+ maybeWithZipOutputStream(outStubJar) { stubOutStream ->
+ maybeWithZipOutputStream(outImplJar) { implOutStream ->
+ val inEntries = inZip.entries()
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+ val inShard = (shardStart <= itemIndex)
+ && (itemIndex < shardNextStart)
+ itemIndex++
+ if (!inShard) {
+ continue
+ }
+ convertSingleEntry(
+ inZip, entry, stubOutStream, implOutStream,
filter, packageRedirector, remapper,
- enableChecker, classes, errors, stats)
- numItemsProcessed++
+ enableChecker, classes, errors, stats
+ )
+ numItemsProcessed++
+ }
+ log.i("Converted all entries.")
}
- log.i("Converted all entries.")
}
+ outStubJar?.let { log.i("Created stub: $it") }
+ outImplJar?.let { log.i("Created impl: $it") }
}
- outStubJar?.let { log.i("Created stub: $it") }
- outImplJar?.let { log.i("Created impl: $it") }
}
+ log.i("%d / %d item(s) processed.", numItemsProcessed, numItems)
}
- val end = System.currentTimeMillis()
- log.i("Done transforming the jar in %.1f second(s). %d / %d item(s) processed.",
- (end - start) / 1000.0, numItemsProcessed, numItems)
}
private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
index 18065ba..ee4a06f 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
@@ -185,6 +185,16 @@
println(LogLevel.Debug, format, *args)
}
+ inline fun <T> iTime(message: String, block: () -> T): T {
+ val start = System.currentTimeMillis()
+ val ret = block()
+ val end = System.currentTimeMillis()
+
+ log.i("%s: took %.1f second(s).", message, (end - start) / 1000.0)
+
+ return ret
+ }
+
inline fun forVerbose(block: () -> Unit) {
if (isEnabled(LogLevel.Verbose)) {
block()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
index 92906a7..2607df6 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
@@ -184,49 +184,50 @@
* Load all the classes, without code.
*/
fun loadClassStructures(inJar: String): ClassNodes {
- log.i("Reading class structure from $inJar ...")
- val start = System.currentTimeMillis()
+ log.iTime("Reading class structure from $inJar") {
+ val allClasses = ClassNodes()
- val allClasses = ClassNodes()
+ log.withIndent {
+ ZipFile(inJar).use { inZip ->
+ val inEntries = inZip.entries()
- log.withIndent {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- if (entry.name.endsWith(".class")) {
- val cr = ClassReader(bis)
- val cn = ClassNode()
- cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
- or ClassReader.SKIP_FRAMES)
- if (!allClasses.addClass(cn)) {
- log.w("Duplicate class found: ${cn.name}")
- }
- } else if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw InvalidJarFileException(
- "$inJar is not a desktop jar file. It contains a *.dex file.")
- } else {
- // Unknown file type. Skip.
- while (bis.available() > 0) {
- bis.skip((1024 * 1024).toLong())
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ if (entry.name.endsWith(".class")) {
+ val cr = ClassReader(bis)
+ val cn = ClassNode()
+ cr.accept(
+ cn, ClassReader.SKIP_CODE
+ or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES
+ )
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
+ }
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$inJar is not a desktop jar file."
+ + " It contains a *.dex file."
+ )
+ } else {
+ // Unknown file type. Skip.
+ while (bis.available() > 0) {
+ bis.skip((1024 * 1024).toLong())
+ }
}
}
}
}
}
+ if (allClasses.size == 0) {
+ log.w("$inJar contains no *.class files.")
+ }
+ return allClasses
}
- if (allClasses.size == 0) {
- log.w("$inJar contains no *.class files.")
- }
-
- val end = System.currentTimeMillis()
- log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
- return allClasses
}
}
}
\ No newline at end of file
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
new file mode 100644
index 0000000..2cebfe9
--- /dev/null
+++ b/tools/systemfeatures/Android.bp
@@ -0,0 +1,63 @@
+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"],
+}
+
+java_library_host {
+ name: "systemfeatures-gen-lib",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "guava",
+ "javapoet",
+ ],
+}
+
+java_binary_host {
+ name: "systemfeatures-gen-tool",
+ main_class: "com.android.systemfeatures.SystemFeaturesGenerator",
+ static_libs: ["systemfeatures-gen-lib"],
+}
+
+// TODO(b/203143243): Add golden diff test for generated sources.
+// Functional runtime behavior is covered in systemfeatures-gen-tests.
+genrule {
+ name: "systemfeatures-gen-tests-srcs",
+ cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " +
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true > $(location RoNoFeatures.java) && " +
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeatures --readonly=false --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: > $(location RwFeatures.java) && " +
+ "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: > $(location RoFeatures.java)",
+ out: [
+ "RwNoFeatures.java",
+ "RoNoFeatures.java",
+ "RwFeatures.java",
+ "RoFeatures.java",
+ ],
+ tools: ["systemfeatures-gen-tool"],
+}
+
+java_test_host {
+ name: "systemfeatures-gen-tests",
+ test_suites: ["general-tests"],
+ srcs: [
+ "tests/**/*.java",
+ ":systemfeatures-gen-tests-srcs",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ static_libs: [
+ "aconfig-annotations-lib",
+ "framework-annotations-lib",
+ "junit",
+ "objenesis",
+ "mockito",
+ "truth",
+ ],
+}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
new file mode 100644
index 0000000..9bfda45
--- /dev/null
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -0,0 +1,218 @@
+/*
+ * 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.systemfeatures
+
+import com.google.common.base.CaseFormat
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+
+/*
+ * Simple Java code generator that takes as input a list of defined features and generates an
+ * accessory class based on the provided versions.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * <cmd> com.foo.RoSystemFeatures --readonly=true \
+ * --feature=WATCH:0 --feature=AUTOMOTIVE: --feature=VULKAN:9348
+ * </pre>
+ *
+ * This generates a class that has the following signature:
+ *
+ * <pre>
+ * package com.foo;
+ * public final class RoSystemFeatures {
+ * @AssumeTrueForR8
+ * public static boolean hasFeatureWatch(Context context);
+ * @AssumeFalseForR8
+ * public static boolean hasFeatureAutomotive(Context context);
+ * @AssumeTrueForR8
+ * public static boolean hasFeatureVulkan(Context context);
+ * public static Boolean maybeHasFeature(String feature, int version);
+ * }
+ * </pre>
+ */
+object SystemFeaturesGenerator {
+ private const val FEATURE_ARG = "--feature="
+ private const val READONLY_ARG = "--readonly="
+ private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
+ private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
+ private val ASSUME_TRUE_CLASS =
+ ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8")
+ private val ASSUME_FALSE_CLASS =
+ ClassName.get("com.android.aconfig.annotations", "AssumeFalseForR8")
+
+ private fun usage() {
+ println("Usage: SystemFeaturesGenerator <outputClassName> [options]")
+ println(" Options:")
+ println(" --readonly=true|false Whether to encode features as build-time constants")
+ println(" --feature=\$NAME:\$VER A feature+version pair (blank version == disabled)")
+ }
+
+ /** Main entrypoint for build-time system feature codegen. */
+ @JvmStatic
+ fun main(args: Array<String>) {
+ if (args.size < 1) {
+ usage()
+ return
+ }
+
+ var readonly = false
+ var outputClassName: ClassName? = null
+ val features = mutableListOf<FeatureInfo>()
+ for (arg in args) {
+ when {
+ arg.startsWith(READONLY_ARG) ->
+ readonly = arg.substring(READONLY_ARG.length).toBoolean()
+ arg.startsWith(FEATURE_ARG) -> {
+ features.add(parseFeatureArg(arg))
+ }
+ else -> outputClassName = ClassName.bestGuess(arg)
+ }
+ }
+
+ outputClassName
+ ?: run {
+ println("Output class name must be provided.")
+ usage()
+ return
+ }
+
+ val classBuilder =
+ TypeSpec.classBuilder(outputClassName)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addJavadoc("@hide")
+
+ addFeatureMethodsToClass(classBuilder, readonly, features)
+ addMaybeFeatureMethodToClass(classBuilder, readonly, features)
+
+ // TODO(b/203143243): Add validation of build vs runtime values to ensure consistency.
+ JavaFile.builder(outputClassName.packageName(), classBuilder.build())
+ .build()
+ .writeTo(System.out)
+ }
+
+ /*
+ * Parses a feature argument of the form "--feature=$NAME:$VER", where "$VER" is optional.
+ * * "--feature=WATCH:0" -> Feature enabled w/ version 0 (default version when enabled)
+ * * "--feature=WATCH:7" -> Feature enabled w/ version 7
+ * * "--feature=WATCH:" -> Feature disabled
+ */
+ private fun parseFeatureArg(arg: String): FeatureInfo {
+ val featureArgs = arg.substring(FEATURE_ARG.length).split(":")
+ val name = featureArgs[0].let { if (!it.startsWith("FEATURE_")) "FEATURE_$it" else it }
+ val version = featureArgs.getOrNull(1)?.toIntOrNull()
+ return FeatureInfo(name, version)
+ }
+
+ /*
+ * Adds per-feature query methods to the class with the form:
+ * {@code public static boolean hasFeatureX(Context context)},
+ * returning the fallback value from PackageManager if not readonly.
+ */
+ private fun addFeatureMethodsToClass(
+ builder: TypeSpec.Builder,
+ readonly: Boolean,
+ features: List<FeatureInfo>
+ ) {
+ for (feature in features) {
+ // Turn "FEATURE_FOO" into "hasFeatureFoo".
+ val methodName =
+ "has" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, feature.name)
+ val methodBuilder =
+ MethodSpec.methodBuilder(methodName)
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .returns(Boolean::class.java)
+ .addParameter(CONTEXT_CLASS, "context")
+
+ if (readonly) {
+ val featureEnabled = compareValues(feature.version, 0) >= 0
+ methodBuilder.addAnnotation(
+ if (featureEnabled) ASSUME_TRUE_CLASS else ASSUME_FALSE_CLASS
+ )
+ methodBuilder.addStatement("return $featureEnabled")
+ } else {
+ methodBuilder.addStatement(
+ "return hasFeatureFallback(context, \$T.\$N)",
+ PACKAGEMANAGER_CLASS,
+ feature.name
+ )
+ }
+ builder.addMethod(methodBuilder.build())
+ }
+
+ if (!readonly) {
+ builder.addMethod(
+ MethodSpec.methodBuilder("hasFeatureFallback")
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(Boolean::class.java)
+ .addParameter(CONTEXT_CLASS, "context")
+ .addParameter(String::class.java, "featureName")
+ .addStatement(
+ "return context.getPackageManager().hasSystemFeature(featureName, 0)"
+ )
+ .build()
+ )
+ }
+ }
+
+ /*
+ * Adds a generic query method to the class with the form: {@code public static boolean
+ * maybeHasFeature(String featureName, int version)}, returning null if the feature version is
+ * undefined or not readonly.
+ *
+ * This method is useful for internal usage within the framework, e.g., from the implementation
+ * of {@link android.content.pm.PackageManager#hasSystemFeature(Context)}, when we may only
+ * want a valid result if it's defined as readonly, and we want a custom fallback otherwise
+ * (e.g., to the existing runtime binder query).
+ */
+ private fun addMaybeFeatureMethodToClass(
+ builder: TypeSpec.Builder,
+ readonly: Boolean,
+ features: List<FeatureInfo>
+ ) {
+ val methodBuilder =
+ MethodSpec.methodBuilder("maybeHasFeature")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .addAnnotation(ClassName.get("android.annotation", "Nullable"))
+ .returns(Boolean::class.javaObjectType) // Use object type for nullability
+ .addParameter(String::class.java, "featureName")
+ .addParameter(Int::class.java, "version")
+
+ if (readonly) {
+ methodBuilder.beginControlFlow("switch (featureName)")
+ for (feature in features) {
+ methodBuilder.addCode("case \$T.\$N: ", PACKAGEMANAGER_CLASS, feature.name)
+ if (feature.version != null) {
+ methodBuilder.addStatement("return \$L >= version", feature.version)
+ } else {
+ methodBuilder.addStatement("return false")
+ }
+ }
+ methodBuilder.addCode("default: ")
+ methodBuilder.addStatement("break")
+ methodBuilder.endControlFlow()
+ }
+ methodBuilder.addStatement("return null")
+ builder.addMethod(methodBuilder.build())
+ }
+
+ private data class FeatureInfo(val name: String, val version: Int?)
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/tools/systemfeatures/tests/Context.java
similarity index 72%
copy from telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
copy to tools/systemfeatures/tests/Context.java
index 460de8c..630bc07 100644
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ b/tools/systemfeatures/tests/Context.java
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-package android.telephony.satellite.stub;
+package android.content;
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
- /** provision subscriberId */
- String subscriberId;
+import android.content.pm.PackageManager;
- /** carrier id */
- int mCarrierId;
-
- /** apn */
- String mNiddApn;
+/** Stub for testing. */
+public class Context {
+ /** @hide */
+ public PackageManager getPackageManager() {
+ return null;
+ }
}
diff --git a/tools/systemfeatures/tests/PackageManager.java b/tools/systemfeatures/tests/PackageManager.java
new file mode 100644
index 0000000..645d500
--- /dev/null
+++ b/tools/systemfeatures/tests/PackageManager.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+/** Stub for testing */
+public class PackageManager {
+ public static final String FEATURE_AUTO = "automotive";
+ public static final String FEATURE_VULKAN = "vulkan";
+ public static final String FEATURE_WATCH = "watch";
+ public static final String FEATURE_WIFI = "wifi";
+
+ /** @hide */
+ public boolean hasSystemFeature(String featureName, int version) {
+ return false;
+ }
+}
diff --git a/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
new file mode 100644
index 0000000..547d2cb
--- /dev/null
+++ b/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.systemfeatures;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(JUnit4.class)
+public class SystemFeaturesGeneratorTest {
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock private Context mContext;
+ @Mock private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() {
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ }
+
+ @Test
+ public void testReadonlyDisabledNoDefinedFeatures() {
+ // Always report null for conditional queries if readonly codegen is disabled.
+ assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isNull();
+ assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isNull();
+ assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
+ assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
+ assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+ }
+
+ @Test
+ public void testReadonlyNoDefinedFeatures() {
+ // If no features are explicitly declared as readonly available, always report
+ // null for conditional queries.
+ assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isNull();
+ assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isNull();
+ assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
+ assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
+ assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+ }
+
+ @Test
+ public void testReadonlyDisabledWithDefinedFeatures() {
+ // Always fall back to the PackageManager for defined, explicit features queries.
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(true);
+ assertThat(RwFeatures.hasFeatureWatch(mContext)).isTrue();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(false);
+ assertThat(RwFeatures.hasFeatureWatch(mContext)).isFalse();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI, 0)).thenReturn(true);
+ assertThat(RwFeatures.hasFeatureWifi(mContext)).isTrue();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN, 0)).thenReturn(false);
+ assertThat(RwFeatures.hasFeatureVulkan(mContext)).isFalse();
+
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO, 0)).thenReturn(false);
+ assertThat(RwFeatures.hasFeatureAuto(mContext)).isFalse();
+
+ // For defined and undefined features, conditional queries should report null (unknown).
+ assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isNull();
+ assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isNull();
+ assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
+ assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
+ assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+ }
+
+ @Test
+ public void testReadonlyWithDefinedFeatures() {
+ // Always use the build-time feature version for defined, explicit feature queries, never
+ // falling back to the runtime query.
+ assertThat(RoFeatures.hasFeatureWatch(mContext)).isTrue();
+ assertThat(RoFeatures.hasFeatureWifi(mContext)).isTrue();
+ assertThat(RoFeatures.hasFeatureVulkan(mContext)).isFalse();
+ assertThat(RoFeatures.hasFeatureAuto(mContext)).isFalse();
+ verify(mPackageManager, never()).hasSystemFeature(anyString(), anyInt());
+
+ // For defined feature types, conditional queries should reflect the build-time versions.
+ // VERSION=1
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, -1)).isTrue();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isTrue();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 100)).isFalse();
+
+ // VERSION=0
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, -1)).isTrue();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isTrue();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 100)).isFalse();
+
+ // VERSION=-1
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, -1)).isTrue();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isFalse();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 100)).isFalse();
+
+ // DISABLED
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, -1)).isFalse();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isFalse();
+ assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 100)).isFalse();
+
+ // For undefined types, conditional queries should report null (unknown).
+ assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", -1)).isNull();
+ assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+ assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull();
+ }
+}