Merge "Add offset when rescaling streams with different min indices" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 1b0ee63..12781e4 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -588,7 +588,6 @@
java_aconfig_library {
name: "android.view.inputmethod.flags-aconfig-java",
aconfig_declarations: "android.view.inputmethod.flags-aconfig",
- host_supported: true,
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e469f16..ce0da7e 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -25,6 +25,12 @@
"name": "FrameworksUiServicesTests"
},
{
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
+ },
+ {
"name": "FrameworksInputMethodSystemServerTests_server_inputmethod"
},
{
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 876274e..aae5bb3 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -126,3 +126,15 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "tune_quota_window_default_parameters"
+ namespace: "backstage_power"
+ description: "Tune default active/exempted bucket quota parameters"
+ bug: "401767691"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 54d337e..a9c4a15 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -360,13 +360,13 @@
/** How much time each app will have to run jobs within their standby bucket window. */
private final long[] mAllowedTimePerPeriodMs = new long[]{
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
0, // NEVER
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
};
/**
@@ -3178,9 +3178,11 @@
static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ // Legacy default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
10 * 60 * 1000L; // 10 minutes
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ // Legacy default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
10 * 60 * 1000L; // 10 minutes
@@ -3192,14 +3194,26 @@
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
10 * 60 * 1000L; // 10 minutes
+
+ // Current default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ // Current default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ 20 * 60 * 1000L; // 20 minutes
+
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
// Legacy default window size for EXEMPTED bucket
+ // EXEMPT apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
// Legacy default window size for ACTIVE bucket
+ // ACTIVE apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
// Legacy default window size for WORKING bucket
private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
2 * 60 * 60 * 1000L; // 2 hours
@@ -3216,6 +3230,13 @@
private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
12 * 60 * 60 * 1000L; // 12 hours
+ // Latest default window size for EXEMPTED bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS =
+ 40 * 60 * 1000L; // 40 minutes.
+ // Latest default window size for ACTIVE bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS =
+ 60 * 60 * 1000L; // 60 minutes.
+
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3276,12 +3297,13 @@
* bucket window.
*/
public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
/**
* How much time each app in the active bucket will have to run jobs within their standby
* bucket window.
*/
- public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
/**
* How much time each app in the working set bucket will have to run jobs within their
* standby bucket window.
@@ -3575,11 +3597,30 @@
public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
void adjustDefaultBucketWindowSizes() {
- WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
- WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+ mAllowedTimePerPeriodMs[ACTIVE_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
+
mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
@@ -3592,6 +3633,11 @@
mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
mAllowedTimePerPeriodMs[FREQUENT_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+
+ mAllowedTimePeriodAdditionaInstallerMs =
+ Math.min(mBucketPeriodsMs[EXEMPTED_INDEX]
+ - mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
}
void adjustDefaultEjLimits() {
@@ -3882,10 +3928,14 @@
KEY_WINDOW_SIZE_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
ALLOWED_TIME_PER_PERIOD_WORKING_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
@@ -3900,19 +3950,27 @@
DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS
+ : DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
+ (Flags.adjustQuotaDefaultConstants()
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ (Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
+ (Flags.adjustQuotaDefaultConstants()
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ (Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
WINDOW_SIZE_WORKING_MS =
properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
Flags.adjustQuotaDefaultConstants()
diff --git a/core/api/current.txt b/core/api/current.txt
index 1630d80..4cd6d6f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -46681,16 +46681,16 @@
method public int getLastCauseCode();
method @Nullable public android.net.LinkProperties getLinkProperties();
method public int getNetworkType();
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+ method public int getNetworkValidationStatus();
method public int getState();
method public int getTransportType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
- field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
+ field public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
+ field public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
+ field public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
+ field public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
+ field public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
}
public final class RadioAccessSpecifier implements android.os.Parcelable {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 132c65c..526a213 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -410,7 +410,6 @@
method public void invalidateCache();
method public static void invalidateCache(@NonNull String, @NonNull String);
method @Nullable public Result query(@NonNull Query);
- method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
field public static final String MODULE_BLUETOOTH = "bluetooth";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 32b170a..35720fd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2941,6 +2941,7 @@
package android.app.supervision {
@FlaggedApi("android.app.supervision.flags.supervision_manager_apis") public class SupervisionManager {
+ method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public android.content.Intent createConfirmSupervisionCredentialsIntent();
method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSupervisionEnabled();
}
@@ -3642,11 +3643,26 @@
method public int getDeviceId();
method @NonNull public String getName();
method public int getType();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public void sendAdditionalInfo(@NonNull android.companion.virtual.sensor.VirtualSensorAdditionalInfo);
method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR;
}
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public final class VirtualSensorAdditionalInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getType();
+ method @NonNull public java.util.List<float[]> getValues();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorAdditionalInfo> CREATOR;
+ }
+
+ public static final class VirtualSensorAdditionalInfo.Builder {
+ ctor public VirtualSensorAdditionalInfo.Builder(int);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo.Builder addValues(@NonNull float[]);
+ method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo build();
+ }
+
public interface VirtualSensorCallback {
method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
}
@@ -3664,6 +3680,7 @@
method public float getResolution();
method public int getType();
method @Nullable public String getVendor();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public boolean isAdditionalInfoSupported();
method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
@@ -3672,6 +3689,7 @@
public static final class VirtualSensorConfig.Builder {
ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setAdditionalInfoSupported(boolean);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMaxDelay(int);
@@ -16442,7 +16460,7 @@
method @Deprecated public int getMtu();
method public int getMtuV4();
method public int getMtuV6();
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+ method public int getNetworkValidationStatus();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
method public int getPduSessionId();
method public int getProtocolType();
@@ -16479,7 +16497,7 @@
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
@@ -16559,7 +16577,7 @@
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -16621,7 +16639,7 @@
method public final int getSlotIndex();
method public void reportEmergencyDataNetworkPreferredTransportChanged(int);
method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
- method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 12bfccf..4c82839 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2460,7 +2460,7 @@
method public static void invalidateCache(@NonNull String, @NonNull String);
method public final boolean isDisabled();
method @Nullable public Result query(@NonNull Query);
- method @FlaggedApi("android.os.ipc_data_cache_test_apis") public static void setTestMode(boolean);
+ method public static void setTestMode(boolean);
field public static final String MODULE_BLUETOOTH = "bluetooth";
field public static final String MODULE_SYSTEM = "system_server";
field public static final String MODULE_TEST = "test";
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1ed64f9..00fa1c1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -200,6 +200,8 @@
@GuardedBy("mPackageMonitorCallbacks")
private final ArraySet<IRemoteCallback> mPackageMonitorCallbacks = new ArraySet<>();
+ private final boolean mUseSystemFeaturesCache;
+
UserManager getUserManager() {
if (mUserManager == null) {
mUserManager = UserManager.get(mContext);
@@ -824,8 +826,7 @@
if (maybeHasSystemFeature != null) {
return maybeHasSystemFeature;
}
- if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
- && android.content.pm.Flags.cacheSdkSystemFeatures()) {
+ if (mUseSystemFeaturesCache) {
maybeHasSystemFeature =
SystemFeaturesCache.getInstance().maybeHasFeature(name, version);
if (maybeHasSystemFeature != null) {
@@ -2221,6 +2222,25 @@
protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
mContext = context;
mPM = pm;
+ mUseSystemFeaturesCache = isSystemFeaturesCacheEnabledAndAvailable();
+ }
+
+ private static boolean isSystemFeaturesCacheEnabledAndAvailable() {
+ if (!android.content.pm.Flags.cacheSdkSystemFeatures()) {
+ return false;
+ }
+ if (!com.android.internal.os.Flags.applicationSharedMemoryEnabled()) {
+ return false;
+ }
+ if (ActivityThread.isSystem() && !SystemFeaturesCache.hasInstance()) {
+ // There are a handful of utility "system" processes that are neither system_server nor
+ // bound as applications. For these processes, we don't have access to application
+ // shared memory or the dependent system features cache.
+ // TODO(b/400713460): Revisit this exception after deprecating these command-like
+ // system processes.
+ return false;
+ }
+ return true;
}
/**
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 2daa52b..fa977c9 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -228,7 +228,7 @@
public AutomaticZenRule(Parcel source) {
enabled = source.readInt() == ENABLED;
if (source.readInt() == ENABLED) {
- name = getTrimmedString(source.readString8());
+ name = getTrimmedString(source.readString());
}
interruptionFilter = source.readInt();
conditionId = getTrimmedUri(source.readParcelable(null, android.net.Uri.class));
@@ -238,11 +238,11 @@
source.readParcelable(null, android.content.ComponentName.class));
creationTime = source.readLong();
mZenPolicy = source.readParcelable(null, ZenPolicy.class);
- mPkg = source.readString8();
+ mPkg = source.readString();
mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
mAllowManualInvocation = source.readBoolean();
mIconResId = source.readInt();
- mTriggerDescription = getTrimmedString(source.readString8(), MAX_DESC_LENGTH);
+ mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
mType = source.readInt();
}
@@ -514,7 +514,7 @@
dest.writeInt(enabled ? ENABLED : DISABLED);
if (name != null) {
dest.writeInt(1);
- dest.writeString8(name);
+ dest.writeString(name);
} else {
dest.writeInt(0);
}
@@ -524,11 +524,11 @@
dest.writeParcelable(configurationActivity, 0);
dest.writeLong(creationTime);
dest.writeParcelable(mZenPolicy, 0);
- dest.writeString8(mPkg);
+ dest.writeString(mPkg);
dest.writeParcelable(mDeviceEffects, 0);
dest.writeBoolean(mAllowManualInvocation);
dest.writeInt(mIconResId);
- dest.writeString8(mTriggerDescription);
+ dest.writeString(mTriggerDescription);
dest.writeInt(mType);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7e5c0fb..99a2763 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2473,11 +2473,9 @@
@Override
public int getPermissionRequestState(String permission) {
Objects.requireNonNull(permission, "Permission name can't be null");
- int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
- permission);
PermissionManager permissionManager = getSystemService(PermissionManager.class);
return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
- deviceId);
+ getDeviceId());
}
@Override
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index feaa98f..7c293cb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6470,9 +6470,11 @@
contentView.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
contentView.setTextViewText(R.id.notification_material_reply_text_3, null);
- // This may get erased by bindSnoozeAction, or if we're showing the bubble icon
- contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
- RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+ if (!notificationsRedesignTemplates()) {
+ // This may get erased by bindSnoozeAction, or if we're showing the bubble icon
+ contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+ RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+ }
}
private boolean bindSnoozeAction(RemoteViews contentView, StandardTemplateParams p) {
@@ -6489,7 +6491,7 @@
final boolean snoozeEnabled = !hideSnoozeButton
&& mContext.getContentResolver() != null
&& isSnoozeSettingEnabled();
- if (snoozeEnabled) {
+ if (!notificationsRedesignTemplates() && snoozeEnabled) {
contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
RemoteViews.MARGIN_BOTTOM, 0);
}
@@ -6569,44 +6571,18 @@
}
boolean validRemoteInput = false;
+ // With the new design, the actions_container should always be visible to act as padding
+ // when there are no actions. We're making its child GONE instead.
+ int actionsContainerForVisibilityChange = notificationsRedesignTemplates()
+ ? R.id.actions_container_layout : R.id.actions_container;
if (numActions > 0 && !p.mHideActions) {
- contentView.setViewVisibility(R.id.actions_container, View.VISIBLE);
+ contentView.setViewVisibility(actionsContainerForVisibilityChange, View.VISIBLE);
contentView.setViewVisibility(R.id.actions, View.VISIBLE);
- contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
- RemoteViews.MARGIN_BOTTOM, 0);
- if (notificationsRedesignTemplates()) {
- // No need for additional space under smart replies/smart actions.
- contentView.setViewLayoutMarginDimen(R.id.smart_reply_container,
- RemoteViews.MARGIN_BOTTOM, 0);
- if (emphasizedMode) {
- // Emphasized actions look similar to smart replies, so let's use the same
- // margins.
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_TOP,
- R.dimen.notification_2025_smart_reply_container_margin);
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_BOTTOM,
- R.dimen.notification_2025_smart_reply_container_margin);
- } else {
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_TOP, 0);
- contentView.setViewLayoutMarginDimen(R.id.actions_container,
- RemoteViews.MARGIN_BOTTOM,
- R.dimen.notification_2025_action_list_margin_bottom);
- }
- }
+ updateMarginsForActions(contentView, emphasizedMode);
validRemoteInput = populateActionsContainer(contentView, p, nonContextualActions,
numActions, emphasizedMode);
} else {
- contentView.setViewVisibility(R.id.actions_container, View.GONE);
- if (notificationsRedesignTemplates() && !snoozeEnabled) {
- // Make sure smart replies & smart actions have enough space at the bottom
- // (if present) when there are no actions. This should be set to 0 if we're
- // showing the snooze or bubble buttons.
- contentView.setViewLayoutMarginDimen(R.id.smart_reply_container,
- RemoteViews.MARGIN_BOTTOM,
- R.dimen.notification_2025_smart_reply_container_margin);
- }
+ contentView.setViewVisibility(actionsContainerForVisibilityChange, View.GONE);
}
RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle(
@@ -6652,6 +6628,30 @@
return contentView;
}
+ private void updateMarginsForActions(RemoteViews contentView, boolean emphasizedMode) {
+ if (notificationsRedesignTemplates()) {
+ if (emphasizedMode) {
+ // Emphasized actions look similar to smart replies, so let's use the same
+ // margins.
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_TOP,
+ R.dimen.notification_2025_smart_reply_container_margin);
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_BOTTOM,
+ R.dimen.notification_2025_smart_reply_container_margin);
+ } else {
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_TOP, 0);
+ contentView.setViewLayoutMarginDimen(R.id.actions_container,
+ RemoteViews.MARGIN_BOTTOM,
+ R.dimen.notification_2025_action_list_margin_bottom);
+ }
+ } else {
+ contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+ RemoteViews.MARGIN_BOTTOM, 0);
+ }
+ }
+
private boolean populateActionsContainer(RemoteViews contentView, StandardTemplateParams p,
List<Action> nonContextualActions, int numActions, boolean emphasizedMode) {
boolean validRemoteInput = false;
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6e49576..38141cf 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1417,36 +1417,7 @@
}
/**
- * Throw if the current process is not allowed to use test APIs.
- */
- @android.ravenwood.annotation.RavenwoodReplace
- private static void throwIfNotTest() {
- final ActivityThread activityThread = ActivityThread.currentActivityThread();
- if (activityThread == null) {
- // Only tests can reach here.
- return;
- }
- final Instrumentation instrumentation = activityThread.getInstrumentation();
- if (instrumentation == null) {
- // Only tests can reach here.
- return;
- }
- if (instrumentation.isInstrumenting()) {
- return;
- }
- if (Flags.enforcePicTestmodeProtocol()) {
- throw new IllegalStateException("Test-only API called not from a test.");
- }
- }
-
- /**
- * Do not throw if running under ravenwood.
- */
- private static void throwIfNotTest$ravenwood() {
- }
-
- /**
- * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
+ * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -1454,12 +1425,10 @@
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public static void setTestMode(boolean mode) {
- throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == mode) {
final String msg = "cannot set test mode redundantly: mode=" + mode;
@@ -1495,11 +1464,9 @@
* for which it would not otherwise have permission. Caches in test mode do NOT write their
* values to the system properties. The effect is local to the current process. Test mode
* must be true when this method is called.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
public void testPropertyName() {
- throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == false) {
throw new IllegalStateException("cannot test property name with test mode off");
@@ -1810,12 +1777,10 @@
* When multiple caches share a single property value, using an instance method on one of
* the cache objects to invalidate all of the cache objects becomes confusing and you should
* just use the static version of this function.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public void disableSystemWide() {
- throwIfNotTest();
disableSystemWide(mPropertyName);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c74bd1a..c528db8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -224,7 +224,7 @@
* <li>A <i id="deviceowner">Device Owner</i>, which only ever exists on the
* {@link UserManager#isSystemUser System User} or Main User, is
* the most powerful type of Device Policy Controller and can affect policy across the device.
- * <li>A <i id="profileowner">Profile Owner<i>, which can exist on any user, can
+ * <li>A <i id="profileowner">Profile Owner</i>, which can exist on any user, can
* affect policy on the user it is on, and when it is running on
* {@link UserManager#isProfile a profile} has
* <a href="#profile-on-parent">limited</a> ability to affect policy on its parent.
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7718d15..3c8b1a0 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -366,7 +366,7 @@
30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
Integer.MAX_VALUE
};
- private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length];
+ private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length - 1];
// Histogram of frame duration overruns encoded in predetermined buckets.
public PendingJankStat() {
@@ -511,7 +511,7 @@
if (overrunTime <= 1000) {
return (overrunTime - 200) / 100 + 43;
}
- return sFrameOverrunHistogramBounds.length - 1;
+ return mFrameOverrunBuckets.length - 1;
}
}
diff --git a/core/java/android/app/jank/TEST_MAPPING b/core/java/android/app/jank/TEST_MAPPING
new file mode 100644
index 0000000..271eb4332
--- /dev/null
+++ b/core/java/android/app/jank/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "postsubmit": [
+ {
+ "name": "CoreAppJankTestCases"
+ },
+ {
+ "name": "CtsAppJankTestCases"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 5891bdd..6f0eafe 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -269,7 +269,7 @@
namespace: "systemui"
description: "enables metrics when redacting notifications on the lockscreen"
bug: "343631648"
- metadata {
+ metadata {
purpose: PURPOSE_BUGFIX
}
}
@@ -279,7 +279,16 @@
namespace: "systemui"
description: "enables user expanding the public view of a notification"
bug: "398853084"
- metadata {
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ }
+flag {
+ name: "notif_entry_creation_time_use_elapsed_realtime"
+ namespace: "systemui"
+ description: "makes the notification entry expect its creation time to be elapsedRealtime, not uptimeMillis"
+ bug: "343631648"
+ metadata {
purpose: PURPOSE_BUGFIX
}
}
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 8217ca1..172ed23 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -97,6 +97,8 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SUPERVISION_MANAGER_APIS)
@RequiresPermission(anyOf = {MANAGE_USERS, QUERY_USERS})
@Nullable
public Intent createConfirmSupervisionCredentialsIntent() {
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index f8ac27d..db77ade 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -24,6 +24,7 @@
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.companion.virtual.camera.VirtualCameraConfig;
@@ -251,6 +252,11 @@
boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event);
/**
+ * Sends additional information about the virtual sensor corresponding to the given token.
+ */
+ boolean sendSensorAdditionalInfo(IBinder token, in VirtualSensorAdditionalInfo info);
+
+ /**
* Launches a pending intent on the given display that is owned by this virtual device.
*/
void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 4fb3982..615a6df 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -158,3 +158,11 @@
bug: "370720522"
is_exported: true
}
+
+flag {
+ name: "virtual_sensor_additional_info"
+ namespace: "virtual_devices"
+ description: "API for injecting SensorAdditionalInfo for VirtualSensor"
+ bug: "393517834"
+ is_exported: true
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 934a1a8..8d4acfc 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -16,12 +16,15 @@
package android.companion.virtual.sensor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtualdevice.flags.Flags;
import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,20 +40,33 @@
*/
@SystemApi
public final class VirtualSensor implements Parcelable {
+
private final int mHandle;
private final int mType;
private final String mName;
+ private final int mFlags;
private final IVirtualDevice mVirtualDevice;
private final IBinder mToken;
+ // Only one additional info frame set at a time.
+ private final Object mAdditionalInfoLock = new Object();
/**
* @hide
*/
public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice,
IBinder token) {
+ this(handle, type, name, /*flags=*/0, virtualDevice, token);
+ }
+
+ /**
+ * @hide
+ */
+ public VirtualSensor(int handle, int type, String name, int flags, IVirtualDevice virtualDevice,
+ IBinder token) {
mHandle = handle;
mType = type;
mName = name;
+ mFlags = flags;
mVirtualDevice = virtualDevice;
mToken = token;
}
@@ -61,13 +77,14 @@
@SuppressLint("UnflaggedApi") // @TestApi without associated feature.
@TestApi
public VirtualSensor(int handle, int type, @NonNull String name) {
- this(handle, type, name, /*virtualDevice=*/null, /*token=*/null);
+ this(handle, type, name, /*flags=*/0, /*virtualDevice=*/null, /*token=*/null);
}
private VirtualSensor(Parcel parcel) {
mHandle = parcel.readInt();
mType = parcel.readInt();
mName = parcel.readString8();
+ mFlags = parcel.readInt();
mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder());
mToken = parcel.readStrongBinder();
}
@@ -123,6 +140,7 @@
parcel.writeInt(mHandle);
parcel.writeInt(mType);
parcel.writeString8(mName);
+ parcel.writeInt(mFlags);
parcel.writeStrongBinder(mVirtualDevice.asBinder());
parcel.writeStrongBinder(mToken);
}
@@ -143,6 +161,33 @@
}
}
+ /**
+ * Send additional information about the sensor to the system.
+ *
+ * @param info the additional sensor information to send.
+ * @throws UnsupportedOperationException if the sensor does not support sending additional info.
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ public void sendAdditionalInfo(@NonNull VirtualSensorAdditionalInfo info) {
+ if (!Flags.virtualSensorAdditionalInfo()) {
+ return;
+ }
+ if ((mFlags & VirtualSensorConfig.ADDITIONAL_INFO_MASK) == 0) {
+ throw new UnsupportedOperationException("Sensor additional info not supported.");
+ }
+ try {
+ synchronized (mAdditionalInfoLock) {
+ mVirtualDevice.sendSensorAdditionalInfo(mToken, info);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
public static final Parcelable.Creator<VirtualSensor> CREATOR =
new Parcelable.Creator<VirtualSensor>() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
similarity index 61%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
copy to core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
index 3cec5a9..7267be8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.domain.interactor
+package android.companion.virtual.sensor;
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-
-val Kosmos.keyguardServiceShowLockscreenInteractor by
- Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+parcelable VirtualSensorAdditionalInfo;
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
new file mode 100644
index 0000000..a4fca50
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual.sensor;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.hardware.SensorAdditionalInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An additional information frame for a {@link VirtualSensor}, which is reported through listener
+ * callback {@link android.hardware.SensorEventCallback#onSensorAdditionalInfo}.
+ *
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+@SystemApi
+public final class VirtualSensorAdditionalInfo implements Parcelable {
+
+ private final int mType;
+ @NonNull
+ private final List<float[]> mValues;
+
+ /** @hide */
+ @IntDef(prefix = "TYPE_", value = {
+ SensorAdditionalInfo.TYPE_UNTRACKED_DELAY,
+ SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE,
+ SensorAdditionalInfo.TYPE_VEC3_CALIBRATION,
+ SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT,
+ SensorAdditionalInfo.TYPE_SAMPLING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ private VirtualSensorAdditionalInfo(int type, @NonNull List<float[]> values) {
+ mType = type;
+ mValues = values;
+ }
+
+ private VirtualSensorAdditionalInfo(@NonNull Parcel parcel) {
+ mType = parcel.readInt();
+ final int valuesLength = parcel.readInt();
+ mValues = new ArrayList<>(valuesLength);
+ for (int i = 0; i < valuesLength; ++i) {
+ mValues.add(parcel.createFloatArray());
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mValues.size());
+ for (int i = 0; i < mValues.size(); ++i) {
+ parcel.writeFloatArray(mValues.get(i));
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the type of this information frame.
+ *
+ * @see SensorAdditionalInfo#type
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the float values of this information frame, if any.
+ *
+ * @see SensorAdditionalInfo#floatValues
+ */
+ @NonNull
+ public List<float[]> getValues() {
+ return mValues;
+ }
+
+ /**
+ * Builder for {@link VirtualSensorAdditionalInfo}.
+ */
+ public static final class Builder {
+
+ @VirtualSensorAdditionalInfo.Type
+ private final int mType;
+ @NonNull
+ private final ArrayList<float[]> mValues = new ArrayList<>();
+
+ /**
+ * Creates a new builder.
+ *
+ * @param type type of this additional info frame.
+ * @see SensorAdditionalInfo
+ */
+ public Builder(@VirtualSensorAdditionalInfo.Type int type) {
+ switch (type) {
+ case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+ case SensorAdditionalInfo.TYPE_SAMPLING:
+ case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+ case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+ case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported type " + type);
+ }
+ mType = type;
+ }
+
+ /**
+ * Additional info payload data represented in float values. Depending on the type of
+ * information, this may be null.
+ *
+ * @see SensorAdditionalInfo#floatValues
+ */
+ @NonNull
+ public Builder addValues(@NonNull float[] values) {
+ if (values.length > 14) {
+ throw new IllegalArgumentException("Maximum payload value size is 14.");
+ }
+ if (mValues.isEmpty()) {
+ switch (mType) {
+ case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+ case SensorAdditionalInfo.TYPE_SAMPLING:
+ assertValuesLength(values, 2);
+ break;
+ case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+ assertValuesLength(values, 1);
+ break;
+ case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+ case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+ assertValuesLength(values, 11);
+ break;
+ }
+ } else if (values.length != mValues.getFirst().length) {
+ throw new IllegalArgumentException("All payload values must have the same length");
+ }
+
+ mValues.add(values);
+ return this;
+ }
+
+ private void assertValuesLength(float[] values, int expected) {
+ if (values.length != expected) {
+ throw new IllegalArgumentException(
+ "Payload values must have size " + expected + " for type " + mType);
+ }
+ }
+
+ /**
+ * Creates a new {@link VirtualSensorAdditionalInfo}.
+ *
+ * @throws IllegalArgumentException if the payload doesn't match the expectation for the
+ * given type, as documented in {@link SensorAdditionalInfo}.
+ */
+ @NonNull
+ public VirtualSensorAdditionalInfo build() {
+ if (mValues.isEmpty()) {
+ throw new IllegalArgumentException("Payload is required");
+ }
+ return new VirtualSensorAdditionalInfo(mType, mValues);
+ }
+ }
+
+ public static final @NonNull Creator<VirtualSensorAdditionalInfo> CREATOR =
+ new Creator<>() {
+ public VirtualSensorAdditionalInfo createFromParcel(Parcel source) {
+ return new VirtualSensorAdditionalInfo(source);
+ }
+
+ public VirtualSensorAdditionalInfo[] newArray(int size) {
+ return new VirtualSensorAdditionalInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 68bc9bc..be8974e 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -59,10 +59,14 @@
private static final int REPORTING_MODE_MASK = 0xE;
private static final int REPORTING_MODE_SHIFT = 1;
+ // Mask for indication bit of sensor additional information support, bit 6.
+ static final int ADDITIONAL_INFO_MASK = 0x40;
+
// Mask for direct mode highest rate level, bit 7, 8, 9.
private static final int DIRECT_REPORT_MASK = 0x380;
private static final int DIRECT_REPORT_SHIFT = 7;
+
// Mask for supported direct channel, bit 10, 11
private static final int DIRECT_CHANNEL_SHIFT = 10;
@@ -253,6 +257,18 @@
}
/**
+ * Returns whether the sensor supports additional information.
+ *
+ * @see Builder#setAdditionalInfoSupported(boolean)
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see android.hardware.SensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ public boolean isAdditionalInfoSupported() {
+ return (mFlags & ADDITIONAL_INFO_MASK) > 0;
+ }
+
+ /**
* Returns the reporting mode of this sensor.
*
* @see Builder#setReportingMode(int)
@@ -450,6 +466,25 @@
}
/**
+ * Sets whether this sensor supports sensor additional information.
+ *
+ * @see Sensor#isAdditionalInfoSupported()
+ * @see android.hardware.SensorAdditionalInfo
+ * @see VirtualSensorAdditionalInfo
+ */
+ @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+ @NonNull
+ public VirtualSensorConfig.Builder setAdditionalInfoSupported(
+ boolean additionalInfoSupported) {
+ if (additionalInfoSupported) {
+ mFlags |= ADDITIONAL_INFO_MASK;
+ } else {
+ mFlags &= ~ADDITIONAL_INFO_MASK;
+ }
+ return this;
+ }
+
+ /**
* Sets the reporting mode of this sensor.
*
* @throws IllegalArgumentException if the reporting mode is not one of
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 10d3051..ded35b2 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -31,13 +31,13 @@
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseArrayMap;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -98,15 +98,16 @@
@GuardedBy("mServicesLock")
private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
- @GuardedBy("mServiceInfoCaches")
- private final ArrayMap<ComponentName, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>();
+ @GuardedBy("mUserIdToServiceInfoCaches")
+ private final SparseArrayMap<ComponentName, ServiceInfo<V>> mUserIdToServiceInfoCaches =
+ new SparseArrayMap<>();
private final Handler mBackgroundHandler;
private final Runnable mClearServiceInfoCachesRunnable = new Runnable() {
public void run() {
- synchronized (mServiceInfoCaches) {
- mServiceInfoCaches.clear();
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.clear();
}
}
};
@@ -537,8 +538,8 @@
Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e);
}
if (lastUpdateTime >= 0) {
- ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName,
- lastUpdateTime);
+ ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(userId,
+ componentName, lastUpdateTime);
if (serviceInfo != null) {
serviceInfos.add(serviceInfo);
continue;
@@ -553,8 +554,8 @@
}
serviceInfos.add(info);
if (Flags.optimizeParsingInRegisteredServicesCache()) {
- synchronized (mServiceInfoCaches) {
- mServiceInfoCaches.put(componentName, info);
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.add(userId, componentName, info);
}
}
} catch (XmlPullParserException | IOException e) {
@@ -563,8 +564,8 @@
}
if (Flags.optimizeParsingInRegisteredServicesCache()) {
- synchronized (mServiceInfoCaches) {
- if (!mServiceInfoCaches.isEmpty()) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ if (mUserIdToServiceInfoCaches.numMaps() > 0) {
mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable);
mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable,
SERVICE_INFO_CACHES_TIMEOUT_MILLIS);
@@ -873,6 +874,11 @@
synchronized (mServicesLock) {
mUserServices.remove(userId);
}
+ if (Flags.optimizeParsingInRegisteredServicesCache()) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.delete(userId);
+ }
+ }
}
@VisibleForTesting
@@ -916,10 +922,10 @@
mContext.unregisterReceiver(mUserRemovedReceiver);
}
- private ServiceInfo<V> getServiceInfoFromServiceCache(@NonNull ComponentName componentName,
- long lastUpdateTime) {
- synchronized (mServiceInfoCaches) {
- ServiceInfo<V> serviceCache = mServiceInfoCaches.get(componentName);
+ private ServiceInfo<V> getServiceInfoFromServiceCache(int userId,
+ @NonNull ComponentName componentName, long lastUpdateTime) {
+ synchronized (mUserIdToServiceInfoCaches) {
+ ServiceInfo<V> serviceCache = mUserIdToServiceInfoCaches.get(userId, componentName);
if (serviceCache != null && serviceCache.lastUpdateTime == lastUpdateTime) {
return serviceCache;
}
diff --git a/core/java/android/content/pm/SystemFeaturesCache.java b/core/java/android/content/pm/SystemFeaturesCache.java
index b3d70fa..57dd1e6 100644
--- a/core/java/android/content/pm/SystemFeaturesCache.java
+++ b/core/java/android/content/pm/SystemFeaturesCache.java
@@ -74,6 +74,11 @@
return instance;
}
+ /** Checks for existence of the process-global instance. */
+ public static boolean hasInstance() {
+ return sInstance != null;
+ }
+
/** Clears the process-global cache instance for testing. */
@VisibleForTesting
public static void clearInstance() {
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 23f1ff8..92ae15a 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -136,6 +136,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
@@ -261,6 +283,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 7f57f5dbf0..3411a48 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -636,3 +636,13 @@
description: "Enables support for new supervising user type"
bug: "389712089"
}
+
+flag {
+ name: "use_unified_resources"
+ namespace: "multiuser"
+ description: "Use same resources"
+ bug: "392972139"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
index fdde205..de3bafb 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
@@ -75,7 +75,6 @@
}
Parcel parcel = Parcel.obtain();
- byte[] parcelContents;
try {
value.writeToParcel(parcel, /*flags*/0);
@@ -85,17 +84,15 @@
"Parcelable " + value + " must not have file descriptors");
}
- parcelContents = parcel.marshall();
+ final int position = buffer.position();
+ parcel.marshall(buffer);
+ if (buffer.position() == position) {
+ throw new AssertionError("No data marshaled for " + value);
+ }
}
finally {
parcel.recycle();
}
-
- if (parcelContents.length == 0) {
- throw new AssertionError("No data marshaled for " + value);
- }
-
- buffer.put(parcelContents);
}
@Override
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 92a56fc..8216130 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -433,8 +433,9 @@
public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
/**
- * Virtual display flag: Indicates that the display should support system decorations. Virtual
- * displays without this flag shouldn't show home, navigation bar or wallpaper.
+ * Virtual display flag: Indicates that the display supports and should always show system
+ * decorations. Virtual displays without this flag shouldn't show home, navigation bar or
+ * wallpaper.
* <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p>
*
* @see #createVirtualDisplay
diff --git a/core/java/android/hardware/serial/OWNERS b/core/java/android/hardware/serial/OWNERS
index bc2c66a..13f6774 100644
--- a/core/java/android/hardware/serial/OWNERS
+++ b/core/java/android/hardware/serial/OWNERS
@@ -3,4 +3,5 @@
chominskib@google.com
wzwonarz@google.com
gstepniewski@google.com
-xutan@google.com
\ No newline at end of file
+xutan@google.com
+ovn@google.com
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index e888f52..2e7c3be 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -718,7 +718,7 @@
}
/**
- * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
+ * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -726,11 +726,8 @@
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
- * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
- @FlaggedApi(android.os.Flags.FLAG_IPC_DATA_CACHE_TEST_APIS)
- @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static void setTestMode(boolean mode) {
PropertyInvalidatedCache.setTestMode(mode);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 49d3f06..6cb49b3 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -52,6 +52,8 @@
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import java.nio.BufferOverflowException;
+import java.nio.ReadOnlyBufferException;
import libcore.util.SneakyThrow;
import java.io.ByteArrayInputStream;
@@ -62,6 +64,7 @@
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
+import java.nio.ByteBuffer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
@@ -457,8 +460,15 @@
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
+ private static native int nativeMarshallArray(
+ long nativePtr, byte[] data, int offset, int length);
+ private static native int nativeMarshallBuffer(
+ long nativePtr, ByteBuffer buffer, int offset, int length);
private static native void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
+ private static native void nativeUnmarshallBuffer(
+ long nativePtr, ByteBuffer buffer, int offset, int length);
+
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
private static native boolean nativeCompareDataInRange(
long ptrA, int offsetA, long ptrB, int offsetB, int length);
@@ -814,12 +824,80 @@
}
/**
+ * Writes the raw bytes of the parcel to a buffer.
+ *
+ * <p class="note">The data you retrieve here <strong>must not</strong>
+ * be placed in any kind of persistent storage (on local disk, across
+ * a network, etc). For that, you should use standard serialization
+ * or another kind of general serialization mechanism. The Parcel
+ * marshalled representation is highly optimized for local IPC, and as
+ * such does not attempt to maintain compatibility with data created
+ * in different versions of the platform.
+ *
+ * @param buffer The ByteBuffer to write the data to.
+ * @throws ReadOnlyBufferException if the buffer is read-only.
+ * @throws BufferOverflowException if the buffer is too small.
+ *
+ * @hide
+ */
+ public final void marshall(@NonNull ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException();
+ }
+ if (buffer.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ final int position = buffer.position();
+ final int remaining = buffer.remaining();
+
+ int marshalledSize = 0;
+ if (buffer.isDirect()) {
+ marshalledSize = nativeMarshallBuffer(mNativePtr, buffer, position, remaining);
+ } else if (buffer.hasArray()) {
+ marshalledSize = nativeMarshallArray(
+ mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ buffer.position(position + marshalledSize);
+ }
+
+ /**
* Fills the raw bytes of this Parcel with the supplied data.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
nativeUnmarshall(mNativePtr, data, offset, length);
}
+ /**
+ * Fills the raw bytes of this Parcel with data from the supplied buffer.
+ *
+ * @param buffer will read buffer.remaining() bytes from the buffer.
+ *
+ * @hide
+ */
+ public final void unmarshall(@NonNull ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException();
+ }
+
+ final int position = buffer.position();
+ final int remaining = buffer.remaining();
+
+ if (buffer.isDirect()) {
+ nativeUnmarshallBuffer(mNativePtr, buffer, position, remaining);
+ } else if (buffer.hasArray()) {
+ nativeUnmarshall(
+ mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ buffer.position(position + remaining);
+ }
+
public final void appendFrom(Parcel parcel, int offset, int length) {
nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 5d80119..86acb2b 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -227,14 +227,6 @@
}
flag {
- name: "ipc_data_cache_test_apis"
- namespace: "system_performance"
- description: "Expose IpcDataCache test apis to mainline modules."
- bug: "396173886"
- is_exported: true
-}
-
-flag {
name: "mainline_vcn_platform_api"
namespace: "vcn"
description: "Expose platform APIs to mainline VCN"
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 561a2c9..0433c76 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1907,8 +1907,9 @@
@Context.PermissionRequestState
public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
int deviceId) {
+ int actualDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permission);
return sPermissionRequestStateCache.query(
- new PermissionRequestStateQuery(packageName, permission, deviceId));
+ new PermissionRequestStateQuery(packageName, permission, actualDeviceId));
}
/**
@@ -2035,7 +2036,8 @@
*/
public int checkPackageNamePermission(String permName, String pkgName,
int deviceId, @UserIdInt int userId) {
- String persistentDeviceId = getPersistentDeviceId(deviceId);
+ int actualDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permName);
+ String persistentDeviceId = getPersistentDeviceId(actualDeviceId);
return sPackageNamePermissionCache.query(
new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5b708b3..85f38c9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6484,6 +6484,14 @@
public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification";
/**
+ * Setting to enable CV (proprietary)
+ *
+ * @hide
+ */
+ public static final String CV_ENABLED =
+ "cv_enabled";
+
+ /**
* Integer property that specifes the color for screen flash notification as a
* packed 32-bit color.
*
@@ -15514,7 +15522,8 @@
* <ul>
* <li><pre>secure</pre>: creates a secure display</li>
* <li><pre>own_content_only</pre>: only shows this display's own content</li>
- * <li><pre>should_show_system_decorations</pre>: supports system decorations</li>
+ * <li><pre>should_show_system_decorations</pre>: always shows system decorations</li>
+ * <li><pre>fixed_content_mode</pre>: does not allow the content mode switch</li>
* </ul>
* </p><p>
* Example:
@@ -20815,6 +20824,24 @@
@Readable
public static final String WEAR_LAUNCHER_UI_MODE = "wear_launcher_ui_mode";
+ /**
+ * Setting indicating whether the primary gesture input action has been enabled by the
+ * user.
+ *
+ * @hide
+ */
+ public static final String GESTURE_PRIMARY_ACTION_USER_PREFERENCE =
+ "gesture_primary_action_user_preference";
+
+ /**
+ * Setting indicating whether the dismiss gesture input action has been enabled by the
+ * user.
+ *
+ * @hide
+ */
+ public static final String GESTURE_DISMISS_ACTION_USER_PREFERENCE =
+ "gesture_dismiss_action_user_preference";
+
/** Whether Wear Power Anomaly Service is enabled.
*
* (0 = false, 1 = true)
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 0b2239a..62b2bcf 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -124,6 +124,18 @@
@Retention(RetentionPolicy.SOURCE)
public @interface FeatureId {}
+ /** @hide */
+ public static String featureIdToString(@FeatureId int featureId) {
+ return switch(featureId) {
+ case FEATURE_ID_DISALLOW_CELLULAR_2G -> "DISALLOW_CELLULAR_2G";
+ case FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES -> "DISALLOW_INSTALL_UNKNOWN_SOURCES";
+ case FEATURE_ID_DISALLOW_USB -> "DISALLOW_USB";
+ case FEATURE_ID_DISALLOW_WEP -> "DISALLOW_WEP";
+ case FEATURE_ID_ENABLE_MTE -> "ENABLE_MTE";
+ default -> "UNKNOWN";
+ };
+ }
+
private static final Set<Integer> ALL_FEATURE_IDS = Set.of(
FEATURE_ID_DISALLOW_CELLULAR_2G,
FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
@@ -147,7 +159,7 @@
"android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
/**
- * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+ * An int extra used with {@link #createSupportIntent} to identify the feature that needs to
* show a support dialog explaining it was disabled by advanced protection.
*
* @hide */
@@ -156,7 +168,7 @@
"android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
/**
- * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+ * An int extra used with {@link #createSupportIntent} to identify the type of the action that
* needs to be explained in the support dialog.
*
* @hide */
@@ -194,6 +206,16 @@
@Retention(RetentionPolicy.SOURCE)
public @interface SupportDialogType {}
+ /** @hide */
+ public static String supportDialogTypeToString(@SupportDialogType int type) {
+ return switch(type) {
+ case SUPPORT_DIALOG_TYPE_UNKNOWN -> "UNKNOWN";
+ case SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION -> "BLOCKED_INTERACTION";
+ case SUPPORT_DIALOG_TYPE_DISABLED_SETTING -> "DISABLED_SETTING";
+ default -> "UNKNOWN";
+ };
+ }
+
private static final Set<Integer> ALL_SUPPORT_DIALOG_TYPES = Set.of(
SUPPORT_DIALOG_TYPE_UNKNOWN,
SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
@@ -372,6 +394,17 @@
return createSupportIntent(featureId, type);
}
+ /** @hide */
+ @RequiresPermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+ public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+ boolean learnMoreClicked) {
+ try {
+ mService.logDialogShown(featureId, type, learnMoreClicked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* A callback class for monitoring changes to Advanced Protection state
*
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
index 1939f82..0fc0fbe 100644
--- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -35,4 +35,6 @@
void setAdvancedProtectionEnabled(boolean enabled);
@EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
List<AdvancedProtectionFeature> getAdvancedProtectionFeatures();
+ @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
+ void logDialogShown(int featureId, int type, boolean learnMoreClicked);
}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 7660ed9..815444d 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -219,7 +219,7 @@
// Gets a read/write buffer mapping the entire shared memory region.
buffer = mRankingMapFd.mapReadWrite();
// Puts the ranking map into the shared memory region buffer.
- buffer.put(mapParcel.marshall(), 0, mapSize);
+ mapParcel.marshall(buffer);
// Protects the region from being written to, by setting it to be read-only.
mRankingMapFd.setProtect(OsConstants.PROT_READ);
// Puts the SharedMemory object in the parcel.
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index dc7129cd..ea7ee4a 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
"name": "CtsNotificationTestCases_notification"
},
{
- "name": "FrameworksUiServicesTests_notification"
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
}
],
"postsubmit": [
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1cf43d4..4cbd5be 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -2636,7 +2636,7 @@
enabled = source.readInt() == 1;
snoozing = source.readInt() == 1;
if (source.readInt() == 1) {
- name = source.readString8();
+ name = source.readString();
}
zenMode = source.readInt();
conditionId = source.readParcelable(null, android.net.Uri.class);
@@ -2644,18 +2644,18 @@
component = source.readParcelable(null, android.content.ComponentName.class);
configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
if (source.readInt() == 1) {
- id = source.readString8();
+ id = source.readString();
}
creationTime = source.readLong();
if (source.readInt() == 1) {
- enabler = source.readString8();
+ enabler = source.readString();
}
zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
- pkg = source.readString8();
+ pkg = source.readString();
allowManualInvocation = source.readBoolean();
- iconResName = source.readString8();
- triggerDescription = source.readString8();
+ iconResName = source.readString();
+ triggerDescription = source.readString();
type = source.readInt();
userModifiedFields = source.readInt();
zenPolicyUserModifiedFields = source.readInt();
@@ -2703,7 +2703,7 @@
dest.writeInt(snoozing ? 1 : 0);
if (name != null) {
dest.writeInt(1);
- dest.writeString8(name);
+ dest.writeString(name);
} else {
dest.writeInt(0);
}
@@ -2714,23 +2714,23 @@
dest.writeParcelable(configurationActivity, 0);
if (id != null) {
dest.writeInt(1);
- dest.writeString8(id);
+ dest.writeString(id);
} else {
dest.writeInt(0);
}
dest.writeLong(creationTime);
if (enabler != null) {
dest.writeInt(1);
- dest.writeString8(enabler);
+ dest.writeString(enabler);
} else {
dest.writeInt(0);
}
dest.writeParcelable(zenPolicy, 0);
dest.writeParcelable(zenDeviceEffects, 0);
- dest.writeString8(pkg);
+ dest.writeString(pkg);
dest.writeBoolean(allowManualInvocation);
- dest.writeString8(iconResName);
- dest.writeString8(triggerDescription);
+ dest.writeString(iconResName);
+ dest.writeString(triggerDescription);
dest.writeInt(type);
dest.writeInt(userModifiedFields);
dest.writeInt(zenPolicyUserModifiedFields);
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 6ed8c6d..929e39f 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -421,7 +421,12 @@
Intent intent = new Intent(SERVICE_INTERFACE);
intent.setComponent(serviceInfo.getComponentName());
int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
- mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+ if (mServiceInfo == null) {
+ mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+ } else {
+ mLifecycleExecutor.execute(() -> mContext.bindServiceAsUser(intent, this, flags,
+ UserHandle.of(mServiceInfo.getUserId())));
+ }
resetServiceConnectionTimeout();
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3f45e29..5c81654 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -286,7 +286,7 @@
/**
* Display flag: Indicates that the display should show system decorations.
* <p>
- * This flag identifies secondary displays that should show system decorations, such as
+ * This flag identifies secondary displays that should always show system decorations, such as
* navigation bar, home activity or wallpaper.
* </p>
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
@@ -401,6 +401,18 @@
public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 14;
/**
+ * Display flag: Indicates that the display is allowed to switch the content mode between
+ * projected/extended and mirroring. This allows the display to dynamically add or remove the
+ * home and system decorations.
+ *
+ * Note that this flag shouldn't be enabled with {@link #FLAG_PRIVATE} or
+ * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} at the same time; otherwise it will be ignored.
+ *
+ * @hide
+ */
+ public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 15;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d880072..bf000d5 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -1125,6 +1125,9 @@
if ((flags & Display.FLAG_REAR) != 0) {
result.append(", FLAG_REAR_DISPLAY");
}
+ if ((flags & Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0) {
+ result.append(", FLAG_ALLOWS_CONTENT_MODE_SWITCH");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4fc894c..421d28d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -699,7 +699,7 @@
/**
* Indicates the display should show system decors.
* <p>
- * System decors include status bar, navigation bar, launcher.
+ * System decors include status bar, navigation bar, launcher, and wallpaper.
* </p>
*
* @param displayId The id of the display.
@@ -719,6 +719,23 @@
void setShouldShowSystemDecors(int displayId, boolean shouldShow);
/**
+ * Indicates that the display is eligible for the desktop mode from WindowManager's perspective.
+ * This includes:
+ * - The default display;
+ * - Any display that is allowed to switch the content mode between extended and mirroring
+ * (which means it can dynamically add or remove system decors), and it is now in extended mode
+ * (should currently show system decors).
+ * <p>
+ * System decors include status bar, navigation bar, launcher, and wallpaper.
+ * </p>
+ *
+ * @param displayId The id of the display.
+ * @return {@code true} if the display is eligible for the desktop mode from WindowManager's
+ * perspective.
+ */
+ boolean isEligibleForDesktopMode(int displayId);
+
+ /**
* Indicates the policy for how the display should show IME.
*
* @param displayId The id of the display.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index da91e8d..2edce5d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -555,8 +555,6 @@
@UiContext
public final Context mContext;
- private UiModeManager mUiModeManager;
-
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -2079,8 +2077,7 @@
// We also ignore dark theme, since the app developer can override the user's
// preference for dark mode in configuration.uiMode. Instead, we assume that both
// force invert and the system's dark theme are enabled.
- if (getUiModeManager().getForceInvertState() ==
- UiModeManager.FORCE_INVERT_TYPE_DARK) {
+ if (shouldApplyForceInvertDark()) {
final boolean isLightTheme =
a.getBoolean(R.styleable.Theme_isLightTheme, false);
// TODO: b/372558459 - Also check the background ColorDrawable color lightness
@@ -2108,6 +2105,14 @@
}
}
+ private boolean shouldApplyForceInvertDark() {
+ final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
+ if (uiModeManager == null) {
+ return false;
+ }
+ return uiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK;
+ }
+
private void updateForceDarkMode() {
if (mAttachInfo.mThreadedRenderer == null) return;
if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) {
@@ -9413,13 +9418,6 @@
return mAudioManager;
}
- private UiModeManager getUiModeManager() {
- if (mUiModeManager == null) {
- mUiModeManager = mContext.getSystemService(UiModeManager.class);
- }
- return mUiModeManager;
- }
-
private Vibrator getSystemVibrator() {
if (mVibrator == null) {
mVibrator = mContext.getSystemService(Vibrator.class);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a41ab36..b3bd89c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2454,6 +2454,7 @@
& WindowInsets.Type.ime()) == 0
|| viewRootImpl.getInsetsController()
.isPredictiveBackImeHideAnimInProgress())) {
+ Handler vh = view.getHandler();
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
if (resultReceiver != null) {
@@ -2464,8 +2465,17 @@
: InputMethodManager.RESULT_SHOWN, null);
}
// TODO(b/322992891) handle case of SHOW_IMPLICIT
- viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
- false /* fromIme */, statsToken);
+ if (vh.getLooper() != Looper.myLooper()) {
+ // The view is running on a different thread than our own, so
+ // we need to reschedule our work for over there.
+ if (DEBUG) Log.v(TAG, "Show soft input: reschedule to view thread");
+ final var finalStatsToken = statsToken;
+ vh.post(() -> viewRootImpl.getInsetsController().show(
+ WindowInsets.Type.ime(), false /* fromIme */, finalStatsToken));
+ } else {
+ viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
+ false /* fromIme */, statsToken);
+ }
return true;
}
ImeTracker.forLogging().onCancelled(statsToken,
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 3557f16..ced27d6 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,7 +28,6 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UiThread;
import android.app.UriGrantsManager;
import android.content.ContentProvider;
import android.content.Intent;
@@ -38,6 +37,7 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignalBeamer;
+import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -468,13 +468,27 @@
});
}
+ /**
+ * Returns {@code false} if there is a sessionId mismatch and logs the event.
+ */
+ private boolean checkSessionId(@NonNull InputConnectionCommandHeader header) {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ Log.w(TAG, "Session id mismatch header.sessionId: " + header.mSessionId
+ + " currentSessionId: " + mCurrentSessionId.get() + " while calling "
+ + Debug.getCaller());
+ //TODO(b/396066692): log metrics.
+ return false; // cancelled
+ }
+ return true;
+ }
+
@Dispatching(cancellable = true)
@Override
public void getTextAfterCursor(InputConnectionCommandHeader header, int length, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextAfterCursor", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -495,8 +509,8 @@
public void getTextBeforeCursor(InputConnectionCommandHeader header, int length, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextBeforeCursor", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -517,8 +531,8 @@
public void getSelectedText(InputConnectionCommandHeader header, int flags,
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getSelectedText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -539,8 +553,8 @@
public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
dispatchWithTracing("getSurroundingText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -567,8 +581,8 @@
public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
AndroidFuture future /* T=Integer */) {
dispatchWithTracing("getCursorCapsMode", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return 0; // cancelled
+ if (!checkSessionId(header)) {
+ return 0;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -584,8 +598,8 @@
public void getExtractedText(InputConnectionCommandHeader header, ExtractedTextRequest request,
int flags, AndroidFuture future /* T=ExtractedText */) {
dispatchWithTracing("getExtractedText", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null;
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -601,8 +615,8 @@
public void commitText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("commitText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return;
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -618,8 +632,8 @@
public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("commitTextWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -634,8 +648,8 @@
@Override
public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
dispatchWithTracing("commitCompletion", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -650,8 +664,8 @@
@Override
public void commitCorrection(InputConnectionCommandHeader header, CorrectionInfo info) {
dispatchWithTracing("commitCorrection", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -670,8 +684,8 @@
@Override
public void setSelection(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setSelection", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -686,8 +700,8 @@
@Override
public void performEditorAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performEditorAction", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -702,8 +716,8 @@
@Override
public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performContextMenuAction", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -718,8 +732,8 @@
@Override
public void setComposingRegion(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setComposingRegion", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -739,8 +753,8 @@
public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
int end, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -756,8 +770,8 @@
public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("setComposingText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -773,8 +787,8 @@
public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -826,8 +840,8 @@
}
return;
}
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null && mDeactivateRequested.get()) {
@@ -842,8 +856,8 @@
@Override
public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
dispatchWithTracing("sendKeyEvent", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -858,8 +872,8 @@
@Override
public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
dispatchWithTracing("clearMetaKeyStates", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -875,8 +889,8 @@
public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength) {
dispatchWithTracing("deleteSurroundingText", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -892,8 +906,8 @@
public void deleteSurroundingTextInCodePoints(InputConnectionCommandHeader header,
int beforeLength, int afterLength) {
dispatchWithTracing("deleteSurroundingTextInCodePoints", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -912,8 +926,8 @@
@Override
public void beginBatchEdit(InputConnectionCommandHeader header) {
dispatchWithTracing("beginBatchEdit", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -928,8 +942,8 @@
@Override
public void endBatchEdit(InputConnectionCommandHeader header) {
dispatchWithTracing("endBatchEdit", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -944,8 +958,8 @@
@Override
public void performSpellCheck(InputConnectionCommandHeader header) {
dispatchWithTracing("performSpellCheck", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -961,8 +975,8 @@
public void performPrivateCommand(InputConnectionCommandHeader header, String action,
Bundle data) {
dispatchWithTracing("performPrivateCommand", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -995,12 +1009,12 @@
}
}
dispatchWithTracing("performHandwritingGesture", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
+ if (!checkSessionId(header)) {
if (resultReceiver != null) {
resultReceiver.send(
InputConnection.HANDWRITING_GESTURE_RESULT_CANCELLED, null);
}
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1038,9 +1052,9 @@
(PreviewableHandwritingGesture) gestureContainer.get();
dispatchWithTracing("previewHandwritingGesture", () -> {
- if (header.mSessionId != mCurrentSessionId.get()
+ if (!checkSessionId(header)
|| (cancellationSignal != null && cancellationSignal.isCanceled())) {
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1065,8 +1079,8 @@
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
int imeDisplayId, AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
return requestCursorUpdatesInternal(
cursorUpdateMode, 0 /* cursorUpdateFilter */, imeDisplayId);
@@ -1079,8 +1093,8 @@
int cursorUpdateMode, int cursorUpdateFilter, int imeDisplayId,
AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
return requestCursorUpdatesInternal(
cursorUpdateMode, cursorUpdateFilter, imeDisplayId);
@@ -1123,9 +1137,9 @@
InputConnectionCommandHeader header, RectF bounds,
@NonNull ResultReceiver resultReceiver) {
dispatchWithTracing("requestTextBoundsInfo", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
+ if (!checkSessionId(header)) {
resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
- return; // cancelled
+ return; // cancelled
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1168,8 +1182,8 @@
return false;
}
- if (header.mSessionId != mCurrentSessionId.get()) {
- return false; // cancelled
+ if (!checkSessionId(header)) {
+ return false; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1193,8 +1207,8 @@
@Override
public void setImeConsumesInput(InputConnectionCommandHeader header, boolean imeConsumesInput) {
dispatchWithTracing("setImeConsumesInput", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1217,8 +1231,8 @@
dispatchWithTracing(
"replaceText",
() -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1236,8 +1250,8 @@
public void commitText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition, @Nullable TextAttribute textAttribute) {
dispatchWithTracing("commitTextFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1256,8 +1270,8 @@
@Override
public void setSelection(InputConnectionCommandHeader header, int start, int end) {
dispatchWithTracing("setSelectionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1273,8 +1287,8 @@
public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
dispatchWithTracing("getSurroundingTextFromA11yIme", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return null; // cancelled
+ if (!checkSessionId(header)) {
+ return null; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1301,8 +1315,8 @@
public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
int afterLength) {
dispatchWithTracing("deleteSurroundingTextFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1317,8 +1331,8 @@
@Override
public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
dispatchWithTracing("sendKeyEventFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1333,8 +1347,8 @@
@Override
public void performEditorAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performEditorActionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1349,8 +1363,8 @@
@Override
public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
dispatchWithTracing("performContextMenuActionFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1366,8 +1380,8 @@
public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
AndroidFuture future /* T=Integer */) {
dispatchWithTracing("getCursorCapsModeFromA11yIme", future, () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return 0; // cancelled
+ if (!checkSessionId(header)) {
+ return 0; // cancelled.
}
final InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
@@ -1382,8 +1396,8 @@
@Override
public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
dispatchWithTracing("clearMetaKeyStatesFromA11yIme", () -> {
- if (header.mSessionId != mCurrentSessionId.get()) {
- return; // cancelled
+ if (!checkSessionId(header)) {
+ return; // cancelled.
}
InputConnection ic = getInputConnection();
if (ic == null || mDeactivateRequested.get()) {
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index a4ea64e..67e5442 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -214,3 +214,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "invalidate_input_calls_restart"
+ namespace: "input_method"
+ description: "Feature flag to fix the race between invalidateInput and restartInput"
+ bug: "396066692"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index cc2afbc..d53c787 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -18,7 +18,6 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.RemoteAnimationTarget;
@@ -39,8 +38,6 @@
@BackEvent.SwipeEdge
private final int mSwipeEdge;
- @Nullable
- private final RemoteAnimationTarget mDepartingAnimationTarget;
/**
* Creates a new {@link BackMotionEvent} instance.
@@ -53,8 +50,6 @@
* @param progress Value between 0 and 1 on how far along the back gesture is.
* @param triggerBack Indicates whether the back arrow is in the triggered state or not
* @param swipeEdge Indicates which edge the swipe starts from.
- * @param departingAnimationTarget The remote animation target of the departing
- * application window.
*/
public BackMotionEvent(
float touchX,
@@ -62,15 +57,13 @@
long frameTimeMillis,
float progress,
boolean triggerBack,
- @BackEvent.SwipeEdge int swipeEdge,
- @Nullable RemoteAnimationTarget departingAnimationTarget) {
+ @BackEvent.SwipeEdge int swipeEdge) {
mTouchX = touchX;
mTouchY = touchY;
mFrameTimeMillis = frameTimeMillis;
mProgress = progress;
mTriggerBack = triggerBack;
mSwipeEdge = swipeEdge;
- mDepartingAnimationTarget = departingAnimationTarget;
}
private BackMotionEvent(@NonNull Parcel in) {
@@ -79,7 +72,6 @@
mProgress = in.readFloat();
mTriggerBack = in.readBoolean();
mSwipeEdge = in.readInt();
- mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
mFrameTimeMillis = in.readLong();
}
@@ -108,7 +100,6 @@
dest.writeFloat(mProgress);
dest.writeBoolean(mTriggerBack);
dest.writeInt(mSwipeEdge);
- dest.writeTypedObject(mDepartingAnimationTarget, flags);
dest.writeLong(mFrameTimeMillis);
}
@@ -160,16 +151,6 @@
return mFrameTimeMillis;
}
- /**
- * Returns the {@link RemoteAnimationTarget} of the top departing application window,
- * or {@code null} if the top window should not be moved for the current type of back
- * destination.
- */
- @Nullable
- public RemoteAnimationTarget getDepartingAnimationTarget() {
- return mDepartingAnimationTarget;
- }
-
@Override
public String toString() {
return "BackMotionEvent{"
@@ -179,7 +160,6 @@
+ ", mProgress=" + mProgress
+ ", mTriggerBack=" + mTriggerBack
+ ", mSwipeEdge=" + mSwipeEdge
- + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
+ "}";
}
}
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index 4908068..ea1b6406 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -20,7 +20,6 @@
import android.os.SystemProperties;
import android.util.MathUtils;
import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
import java.io.PrintWriter;
@@ -147,15 +146,14 @@
}
/** Creates a start {@link BackMotionEvent}. */
- public BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
+ public BackMotionEvent createStartEvent() {
return new BackMotionEvent(
/* touchX = */ mInitTouchX,
/* touchY = */ mInitTouchY,
/* frameTimeMillis = */ 0,
/* progress = */ 0,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ target);
+ /* swipeEdge = */ mSwipeEdge);
}
/** Creates a progress {@link BackMotionEvent}. */
@@ -239,8 +237,7 @@
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ mSwipeEdge);
}
/** Sets the thresholds for computing progress. */
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index 866c16c..5e8ce5e 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -56,9 +56,13 @@
ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
true),
+ ENABLE_DISPLAY_DISCONNECT_INTERACTION(Flags::enableDisplayDisconnectInteraction, false),
ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, true),
+ ENABLE_DISPLAY_RECONNECT_INTERACTION(Flags::enableDisplayReconnectInteraction, false),
ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, true),
ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
+ ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX(Flags::enableDynamicRadiusComputationBugfix, false),
+ ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false),
ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true),
ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false),
ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false),
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index aecf6eb..5b3044e 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -146,6 +146,8 @@
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES(
Flags::inheritTaskBoundsForTrampolineTaskLaunches, false),
+ SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX(
+ Flags::skipDecorViewRelayoutWhenClosingBugfix, false),
// go/keep-sorted end
;
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 6f4dd4e..0b84070 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -66,17 +66,6 @@
void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t);
/**
- * Starts a legacy transition.
- * @param type The transition type.
- * @param adapter The animation to use.
- * @param syncCallback A sync callback for the contents of `t`
- * @param t Operations that are part of the transition.
- * @return sync-id or -1 if this no-op'd because a transition is already running.
- */
- int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
- in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
-
- /**
* Finishes a transition. This must be called for all created transitions.
* @param transitionToken Which transition to finish
* @param t Changes to make before finishing but in the same SF Transaction. Can be null.
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index d478108..69613a7 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -270,8 +270,7 @@
}
mIOnBackInvokedCallback.onBackStarted(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge(),
- null));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge()));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
@@ -286,8 +285,7 @@
}
mIOnBackInvokedCallback.onBackProgressed(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge(),
- null));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge()));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 5c5da49..6e56b63 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -130,27 +130,6 @@
}
/**
- * Start a legacy transition.
- * @param type The type of the transition. This is ignored if a transitionToken is provided.
- * @param adapter An existing transition to start. If null, a new transition is created.
- * @param t The set of window operations that are part of this transition.
- * @return true on success, false if a transition was already running.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
- @NonNull
- public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
- @NonNull WindowContainerTransactionCallback syncCallback,
- @NonNull WindowContainerTransaction t) {
- try {
- return getWindowOrganizerController().startLegacyTransition(
- type, adapter, syncCallback.mInterface, t);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Register an ITransitionPlayer to handle transition animations.
* @hide
*/
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index e706af9..afc9660 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -602,6 +602,13 @@
}
flag {
+ name: "keyboard_shortcuts_to_switch_desks"
+ namespace: "lse_desktop_experience"
+ description: "Enable switching the active desk with keyboard shortcuts"
+ bug: "389957556"
+}
+
+flag {
name: "enable_connected_displays_dnd"
namespace: "lse_desktop_experience"
description: "Enable drag-and-drop between connected displays."
@@ -926,6 +933,16 @@
}
flag {
+ name: "skip_decor_view_relayout_when_closing_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables bugfix to skip DecorView relayout when the corresponding window is closing."
+ bug: "394502142"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_size_compat_mode_improvements_for_connected_displays"
namespace: "lse_desktop_experience"
description: "Enable some improvements in size compat mode for connected displays."
@@ -945,3 +962,20 @@
description: "Enable restart menu UI, which is shown when an app moves between displays."
bug: "397804287"
}
+
+flag {
+ name: "enable_dynamic_radius_computation_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables bugfix to compute the corner/shadow radius of desktop windows dynamically with the current window context."
+ bug: "399630464"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "show_home_behind_desktop"
+ namespace: "lse_desktop_experience"
+ description: "Enables the home to be shown behind the desktop."
+ bug: "375644149"
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserContentManager.java b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
new file mode 100644
index 0000000..09c6f5e
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
+import com.android.internal.R;
+
+public class MediaRouteChooserContentManager {
+ Context mContext;
+
+ private final boolean mShowProgressBarWhenEmpty;
+
+ public MediaRouteChooserContentManager(Context context, boolean showProgressBarWhenEmpty) {
+ mContext = context;
+ mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+ }
+
+ /**
+ * Starts binding all the views (list view, empty view, etc.) using the
+ * given container view.
+ */
+ public void bindViews(View containerView) {
+ View emptyView = containerView.findViewById(android.R.id.empty);
+ ListView listView = containerView.findViewById(R.id.media_route_list);
+ listView.setEmptyView(emptyView);
+
+ if (!mShowProgressBarWhenEmpty) {
+ containerView.findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
+
+ // Center the empty view when the progress bar is not shown.
+ LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) emptyView.getLayoutParams();
+ params.gravity = Gravity.CENTER;
+ emptyView.setLayoutParams(params);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
index 23d966fd..5030a14 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -23,14 +23,12 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -52,15 +50,15 @@
public class MediaRouteChooserDialog extends AlertDialog {
private final MediaRouter mRouter;
private final MediaRouterCallback mCallback;
- private final boolean mShowProgressBarWhenEmpty;
private int mRouteTypes;
private View.OnClickListener mExtendedSettingsClickListener;
private RouteAdapter mAdapter;
- private ListView mListView;
private Button mExtendedSettingsButton;
private boolean mAttachedToWindow;
+ private final MediaRouteChooserContentManager mContentManager;
+
public MediaRouteChooserDialog(Context context, int theme) {
this(context, theme, true);
}
@@ -70,7 +68,7 @@
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mCallback = new MediaRouterCallback();
- mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+ mContentManager = new MediaRouteChooserContentManager(context, showProgressBarWhenEmpty);
}
/**
@@ -128,8 +126,9 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
// Note: setView must be called before super.onCreate().
- setView(LayoutInflater.from(getContext()).inflate(R.layout.media_route_chooser_dialog,
- null));
+ View containerView = LayoutInflater.from(getContext()).inflate(
+ R.layout.media_route_chooser_dialog, null);
+ setView(containerView);
setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
? R.string.media_route_chooser_title_for_remote_display
@@ -140,25 +139,15 @@
super.onCreate(savedInstanceState);
- View emptyView = findViewById(android.R.id.empty);
mAdapter = new RouteAdapter(getContext());
- mListView = (ListView) findViewById(R.id.media_route_list);
- mListView.setAdapter(mAdapter);
- mListView.setOnItemClickListener(mAdapter);
- mListView.setEmptyView(emptyView);
+ ListView listView = findViewById(R.id.media_route_list);
+ listView.setAdapter(mAdapter);
+ listView.setOnItemClickListener(mAdapter);
- mExtendedSettingsButton = (Button) findViewById(R.id.media_route_extended_settings_button);
+ mExtendedSettingsButton = findViewById(R.id.media_route_extended_settings_button);
updateExtendedSettingsButton();
- if (!mShowProgressBarWhenEmpty) {
- findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
-
- // Center the empty view when the progress bar is not shown.
- LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) emptyView.getLayoutParams();
- params.gravity = Gravity.CENTER;
- emptyView.setLayoutParams(params);
- }
+ mContentManager.bindViews(containerView);
}
private void updateExtendedSettingsButton() {
@@ -240,8 +229,8 @@
view = mInflater.inflate(R.layout.media_route_list_item, parent, false);
}
MediaRouter.RouteInfo route = getItem(position);
- TextView text1 = (TextView)view.findViewById(android.R.id.text1);
- TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+ TextView text1 = view.findViewById(android.R.id.text1);
+ TextView text2 = view.findViewById(android.R.id.text2);
text1.setText(route.getName());
CharSequence description = route.getDescription();
if (TextUtils.isEmpty(description)) {
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
index a53d6b8..3fe6873 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
@@ -132,8 +132,13 @@
}
}
- // TODO correct values
- nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+ if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
+ nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1, -1, false));
+ nodeInfo.setClassName("android.widget.HorizontalScrollView");
+ } else {
+ nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+ nodeInfo.setClassName("android.widget.ScrollView");
+ }
if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
nodeInfo.setClassName("android.widget.HorizontalScrollView");
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
index f70f4cb..db2c460 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
@@ -33,6 +33,7 @@
import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics;
import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;
+import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent.ScrollDirection;
import java.util.ArrayList;
import java.util.Collections;
@@ -104,9 +105,9 @@
if (isClickAction(action)) {
return performClick(component);
} else if (isScrollForwardAction(action)) {
- return scrollByOffset(mRemoteContext, component, -500) != 0;
+ return scrollDirection(mRemoteContext, component, ScrollDirection.FORWARD);
} else if (isScrollBackwardAction(action)) {
- return scrollByOffset(mRemoteContext, component, 500) != 0;
+ return scrollDirection(mRemoteContext, component, ScrollDirection.BACKWARD);
} else if (isShowOnScreenAction(action)) {
return showOnScreen(mRemoteContext, component);
} else {
@@ -141,19 +142,32 @@
}
private boolean showOnScreen(RemoteContext context, Component component) {
- if (component.getParent() instanceof LayoutComponent) {
- LayoutComponent parent = (LayoutComponent) component.getParent();
- ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
+ ScrollableComponent scrollable = findScrollable(component);
- if (scrollable != null) {
- scrollable.showOnScreen(context, component.getComponentId());
- return true;
- }
+ if (scrollable != null) {
+ return scrollable.showOnScreen(context, component);
}
return false;
}
+ @Nullable
+ private static ScrollableComponent findScrollable(Component component) {
+ Component parent = component.getParent();
+
+ while (parent != null) {
+ ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
+
+ if (scrollable != null) {
+ return scrollable;
+ } else {
+ parent = parent.getParent();
+ }
+ }
+
+ return null;
+ }
+
/**
* scroll content by the given offset
*
@@ -173,6 +187,25 @@
}
/**
+ * scroll content in a given direction
+ *
+ * @param context
+ * @param component
+ * @param direction
+ * @return
+ */
+ public boolean scrollDirection(
+ RemoteContext context, Component component, ScrollDirection direction) {
+ ScrollableComponent scrollable = component.selfOrModifier(ScrollableComponent.class);
+
+ if (scrollable != null) {
+ return scrollable.scrollDirection(context, direction);
+ }
+
+ return false;
+ }
+
+ /**
* Perform a click on the given component
*
* @param component
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index c38a44a..da4e8d6 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -39,6 +39,7 @@
private final RemoteComposeDocumentAccessibility mRemoteDocA11y;
private final SemanticNodeApplier<AccessibilityNodeInfo> mApplier;
+ private final View mHost;
public PlatformRemoteComposeTouchHelper(
View host,
@@ -47,6 +48,7 @@
super(host);
this.mRemoteDocA11y = remoteDocA11y;
this.mApplier = applier;
+ this.mHost = host;
}
public static PlatformRemoteComposeTouchHelper forRemoteComposePlayer(
@@ -150,6 +152,7 @@
boolean performed = mRemoteDocA11y.performAction(component, action, arguments);
if (performed) {
+ mHost.invalidate();
invalidateRoot();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index caf19e1..e5c20eb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -73,7 +73,9 @@
// We also keep a more fine-grained BUILD number, exposed as
// ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
- static final float BUILD = 0.5f;
+ static final float BUILD = 0.6f;
+
+ private static final boolean UPDATE_VARIABLES_BEFORE_LAYOUT = false;
@NonNull ArrayList<Operation> mOperations = new ArrayList<>();
@@ -892,7 +894,10 @@
registerVariables(context, mOperations);
context.mMode = RemoteContext.ContextMode.UNSET;
- mFirstPaint = true;
+
+ if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+ mFirstPaint = true;
+ }
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -1241,11 +1246,13 @@
context.mRemoteComposeState = mRemoteComposeState;
context.mRemoteComposeState.setContext(context);
- // Update any dirty variables
- if (mFirstPaint) {
- mFirstPaint = false;
- } else {
- updateVariables(context, theme, mOperations);
+ if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+ // Update any dirty variables
+ if (mFirstPaint) {
+ mFirstPaint = false;
+ } else {
+ updateVariables(context, theme, mOperations);
+ }
}
// If we have a content sizing set, we are going to take the original document
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index ac9f98b..add9d5b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -111,6 +111,7 @@
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DrawContentOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
@@ -257,6 +258,7 @@
public static final int MODIFIER_HEIGHT = 67;
public static final int MODIFIER_WIDTH_IN = 231;
public static final int MODIFIER_HEIGHT_IN = 232;
+ public static final int MODIFIER_COLLAPSIBLE_PRIORITY = 235;
public static final int MODIFIER_BACKGROUND = 55;
public static final int MODIFIER_BORDER = 107;
public static final int MODIFIER_PADDING = 58;
@@ -368,6 +370,7 @@
map.put(MODIFIER_HEIGHT, HeightModifierOperation::read);
map.put(MODIFIER_WIDTH_IN, WidthInModifierOperation::read);
map.put(MODIFIER_HEIGHT_IN, HeightInModifierOperation::read);
+ map.put(MODIFIER_COLLAPSIBLE_PRIORITY, CollapsiblePriorityModifierOperation::read);
map.put(MODIFIER_PADDING, PaddingModifierOperation::read);
map.put(MODIFIER_BACKGROUND, BackgroundModifierOperation::read);
map.put(MODIFIER_BORDER, BorderModifierOperation::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index e37833f..b297a02 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -46,7 +46,7 @@
new CoreDocument(); // todo: is this a valid way to initialize? bbade@
public @NonNull RemoteComposeState mRemoteComposeState =
new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
-
+ private long mDocLoadTime = System.currentTimeMillis();
@Nullable protected PaintContext mPaintContext = null;
protected float mDensity = Float.NaN;
@@ -83,6 +83,20 @@
}
}
+ /**
+ * Get the time the document was loaded
+ *
+ * @return time in ms since the document was loaded
+ */
+ public long getDocLoadTime() {
+ return mDocLoadTime;
+ }
+
+ /** Set the time the document was loaded */
+ public void setDocLoadTime() {
+ mDocLoadTime = System.currentTimeMillis();
+ }
+
public boolean isAnimationEnabled() {
return mAnimate;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
index e9cc26f..dee79a4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
@@ -85,6 +85,9 @@
/** the year */
public static final short TIME_YEAR = 12;
+ /** (value - doc_load_time) * 1E-3 */
+ public static final short TIME_FROM_LOAD_SEC = 14;
+
/**
* creates a new operation
*
@@ -226,6 +229,7 @@
int val = mType & 255;
int flags = mType >> 8;
RemoteContext ctx = context.getContext();
+ long load_time = ctx.getDocLoadTime();
LongConstant longConstant = (LongConstant) ctx.getObject(mTimeId);
long value = longConstant.getValue();
long delta = 0;
@@ -292,6 +296,9 @@
case TIME_YEAR:
ctx.loadFloat(mId, time.getYear());
break;
+ case TIME_FROM_LOAD_SEC:
+ ctx.loadFloat(mId, (value - load_time) * 1E-3f);
+ break;
}
}
@@ -334,6 +341,8 @@
return "TIME_DAY_OF_WEEK";
case TIME_YEAR:
return "TIME_YEAR";
+ case TIME_FROM_LOAD_SEC:
+ return "TIME_FROM_LOAD_SEC";
default:
return "INVALID_TIME_TYPE";
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index f1158d9..2a809c6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -824,15 +824,27 @@
*
* @param value a 2 dimension float array that will receive the horizontal and vertical position
* of the component.
+ * @param forSelf whether the location is for this container or a child, relevant for scrollable
+ * items.
*/
- public void getLocationInWindow(@NonNull float[] value) {
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
value[0] += mX;
value[1] += mY;
if (mParent != null) {
- mParent.getLocationInWindow(value);
+ mParent.getLocationInWindow(value, false);
}
}
+ /**
+ * Returns the location of the component relative to the root component
+ *
+ * @param value a 2 dimension float array that will receive the horizontal and vertical position
+ * of the component.
+ */
+ public void getLocationInWindow(@NonNull float[] value) {
+ getLocationInWindow(value, true);
+ }
+
@NonNull
@Override
public String toString() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index 6163d80..bc099e3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -289,11 +289,11 @@
}
@Override
- public void getLocationInWindow(@NonNull float[] value) {
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
value[0] += mX + mPaddingLeft;
value[1] += mY + mPaddingTop;
if (mParent != null) {
- mParent.getLocationInWindow(value);
+ mParent.getLocationInWindow(value, false);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
index b008952..00ec605 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
@@ -24,10 +24,13 @@
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+import java.util.ArrayList;
import java.util.List;
public class CollapsibleColumnLayout extends ColumnLayout {
@@ -153,7 +156,7 @@
}
@Override
- protected boolean hasVerticalIntrinsicDimension() {
+ public boolean hasVerticalIntrinsicDimension() {
return true;
}
@@ -166,25 +169,72 @@
boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
+ computeVisibleChildren(
+ context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ }
+
+ @Override
+ public void computeSize(
+ @NonNull PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ @NonNull MeasurePass measure) {
+ computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+ }
+
+ @Override
+ public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+ // if needed, take care of weight calculations
+ super.internalLayoutMeasure(context, measure);
+ // Check again for visibility
+ ComponentMeasure m = measure.get(this);
+ computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+ }
+
+ private void computeVisibleChildren(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @Nullable Size size) {
int visibleChildren = 0;
ComponentMeasure self = measure.get(this);
self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
float currentMaxHeight = maxHeight;
+ boolean hasPriorities = false;
for (Component c : mChildrenComponents) {
- if (c instanceof CollapsibleColumnLayout) {
- c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
- } else {
- c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+ if (!measure.contains(c.getComponentId())) {
+ // No need to remeasure here if already done
+ if (c instanceof CollapsibleColumnLayout) {
+ c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
+ } else {
+ c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+ }
}
+
ComponentMeasure m = measure.get(c);
if (!m.isGone()) {
- size.setWidth(Math.max(size.getWidth(), m.getW()));
- size.setHeight(size.getHeight() + m.getH());
+ if (size != null) {
+ size.setWidth(Math.max(size.getWidth(), m.getW()));
+ size.setHeight(size.getHeight() + m.getH());
+ }
visibleChildren++;
currentMaxHeight -= m.getH();
}
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null) {
+ hasPriorities = true;
+ }
+ }
}
- if (!mChildrenComponents.isEmpty()) {
+ if (!mChildrenComponents.isEmpty() && size != null) {
size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildren - 1)));
}
@@ -192,7 +242,14 @@
float childrenHeight = 0f;
boolean overflow = false;
- for (Component child : mChildrenComponents) {
+ ArrayList<Component> children = mChildrenComponents;
+ if (hasPriorities) {
+ // TODO: We need to cache this.
+ children =
+ CollapsiblePriority.sortWithPriorities(
+ mChildrenComponents, CollapsiblePriority.VERTICAL);
+ }
+ for (Component child : children) {
ComponentMeasure childMeasure = measure.get(child);
if (overflow || childMeasure.isGone()) {
childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
@@ -209,10 +266,10 @@
visibleChildren++;
}
}
- if (verticalWrap) {
+ if (verticalWrap && size != null) {
size.setHeight(Math.min(maxHeight, childrenHeight));
}
- if (visibleChildren == 0 || size.getHeight() <= 0f) {
+ if (visibleChildren == 0 || (size != null && size.getHeight() <= 0f)) {
self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
new file mode 100644
index 0000000..46cd45e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+
+import java.util.ArrayList;
+
+/** Utility class to manage collapsible priorities on components */
+public class CollapsiblePriority {
+
+ public static final int HORIZONTAL = 0;
+ public static final int VERTICAL = 1;
+
+ /**
+ * Returns the priority of a child component
+ *
+ * @param c the child component
+ * @return priority value, or 0f if not found
+ */
+ static float getPriority(Component c, int orientation) {
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null && priority.getOrientation() == orientation) {
+ return priority.getPriority();
+ }
+ }
+ return Float.MAX_VALUE;
+ }
+
+ /**
+ * Allocate and return a sorted array of components by their priorities
+ *
+ * @param components the children components
+ * @return list of components sorted by their priority in decreasing order
+ */
+ static ArrayList<Component> sortWithPriorities(
+ ArrayList<Component> components, int orientation) {
+ ArrayList<Component> sorted = new ArrayList<>(components);
+ sorted.sort(
+ (t1, t2) -> {
+ float p1 = getPriority(t1, orientation);
+ float p2 = getPriority(t2, orientation);
+ return (int) (p2 - p1);
+ });
+ return sorted;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
index 05f3329..e3632f9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
@@ -24,10 +24,13 @@
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+import java.util.ArrayList;
import java.util.List;
public class CollapsibleRowLayout extends RowLayout {
@@ -135,8 +138,12 @@
}
@Override
- protected boolean hasHorizontalIntrinsicDimension() {
- return true;
+ public float minIntrinsicHeight(@NonNull RemoteContext context) {
+ float height = computeModifierDefinedHeight(context);
+ if (!mChildrenComponents.isEmpty()) {
+ height += mChildrenComponents.get(0).minIntrinsicHeight(context);
+ }
+ return height;
}
@Override
@@ -149,12 +156,8 @@
}
@Override
- public float minIntrinsicHeight(@NonNull RemoteContext context) {
- float height = computeModifierDefinedHeight(context);
- if (!mChildrenComponents.isEmpty()) {
- height += mChildrenComponents.get(0).minIntrinsicHeight(context);
- }
- return height;
+ public boolean hasHorizontalIntrinsicDimension() {
+ return true;
}
@Override
@@ -166,45 +169,107 @@
boolean verticalWrap,
@NonNull MeasurePass measure,
@NonNull Size size) {
- super.computeWrapSize(
- context, Float.MAX_VALUE, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ computeVisibleChildren(
+ context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
}
@Override
- public boolean applyVisibility(
- float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
- float childrenWidth = 0f;
- float childrenHeight = 0f;
- boolean changedVisibility = false;
+ public void computeSize(
+ @NonNull PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ @NonNull MeasurePass measure) {
+ computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+ }
+
+ @Override
+ public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+ // if needed, take care of weight calculations
+ super.internalLayoutMeasure(context, measure);
+ // Check again for visibility
+ ComponentMeasure m = measure.get(this);
+ computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+ }
+
+ private void computeVisibleChildren(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @Nullable Size size) {
int visibleChildren = 0;
ComponentMeasure self = measure.get(this);
- self.clearVisibilityOverride();
- if (selfWidth <= 0 || selfHeight <= 0) {
- self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
- return true;
+ self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
+ float currentMaxWidth = maxWidth;
+ boolean hasPriorities = false;
+ for (Component c : mChildrenComponents) {
+ if (!measure.contains(c.getComponentId())) {
+ // No need to remeasure here if already done
+ if (c instanceof CollapsibleRowLayout) {
+ c.measure(context, 0f, currentMaxWidth, 0f, maxHeight, measure);
+ } else {
+ c.measure(context, 0f, Float.MAX_VALUE, 0f, maxHeight, measure);
+ }
+ }
+ ComponentMeasure m = measure.get(c);
+ if (!m.isGone()) {
+ if (size != null) {
+ size.setHeight(Math.max(size.getHeight(), m.getH()));
+ size.setWidth(size.getWidth() + m.getW());
+ }
+ visibleChildren++;
+ currentMaxWidth -= m.getW();
+ }
+ if (c instanceof LayoutComponent) {
+ LayoutComponent lc = (LayoutComponent) c;
+ CollapsiblePriorityModifierOperation priority =
+ lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+ if (priority != null) {
+ hasPriorities = true;
+ }
+ }
}
- for (Component child : mChildrenComponents) {
+ if (!mChildrenComponents.isEmpty() && size != null) {
+ size.setWidth(size.getWidth() + (mSpacedBy * (visibleChildren - 1)));
+ }
+
+ float childrenWidth = 0f;
+ float childrenHeight = 0f;
+
+ boolean overflow = false;
+ ArrayList<Component> children = mChildrenComponents;
+ if (hasPriorities) {
+ // TODO: We need to cache this.
+ children =
+ CollapsiblePriority.sortWithPriorities(
+ mChildrenComponents, CollapsiblePriority.HORIZONTAL);
+ }
+ for (Component child : children) {
ComponentMeasure childMeasure = measure.get(child);
- int visibility = childMeasure.getVisibility();
- childMeasure.clearVisibilityOverride();
- if (!childMeasure.isVisible()) {
+ if (overflow || childMeasure.isGone()) {
+ childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
continue;
}
- if (childrenWidth + childMeasure.getW() > selfWidth
- && childrenHeight + childMeasure.getH() > selfHeight) {
+ float childWidth = childMeasure.getW();
+ boolean childDoesNotFits = childrenWidth + childWidth > maxWidth;
+ if (childDoesNotFits) {
childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
- if (visibility != childMeasure.getVisibility()) {
- changedVisibility = true;
- }
+ overflow = true;
} else {
- childrenWidth += childMeasure.getW();
+ childrenWidth += childWidth;
childrenHeight = Math.max(childrenHeight, childMeasure.getH());
visibleChildren++;
}
}
- if (visibleChildren == 0) {
+ if (horizontalWrap && size != null) {
+ size.setWidth(Math.min(maxWidth, childrenWidth));
+ }
+ if (visibleChildren == 0 || (size != null && size.getWidth() <= 0f)) {
self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
}
- return changedVisibility;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index cda90c2..9566242 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -33,6 +33,7 @@
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -372,6 +373,17 @@
DebugLog.e();
}
+ @Override
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+ super.getLocationInWindow(value, forSelf);
+
+ if (!forSelf && mVerticalScrollDelegate instanceof ScrollModifierOperation) {
+ ScrollModifierOperation smo = (ScrollModifierOperation) mVerticalScrollDelegate;
+
+ value[1] += smo.getScrollY();
+ }
+ }
+
/**
* The name of the class
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 5b66b95..eb10ead 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -226,9 +226,17 @@
measure,
mCachedWrapSize);
float w = mCachedWrapSize.getWidth();
- computeSize(context, 0f, w, 0, measuredHeight, measure);
if (hasHorizontalScroll()) {
+ computeSize(context, 0f, w, 0, measuredHeight, measure);
mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
+ } else {
+ computeSize(
+ context,
+ 0f,
+ Math.min(measuredWidth, insetMaxWidth),
+ 0,
+ Math.min(measuredHeight, insetMaxHeight),
+ measure);
}
} else if (hasVerticalIntrinsicDimension()) {
mCachedWrapSize.setWidth(0f);
@@ -236,9 +244,17 @@
computeWrapSize(
context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
float h = mCachedWrapSize.getHeight();
- computeSize(context, 0f, measuredWidth, 0, h, measure);
if (hasVerticalScroll()) {
+ computeSize(context, 0f, measuredWidth, 0, h, measure);
mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+ } else {
+ computeSize(
+ context,
+ 0f,
+ Math.min(measuredWidth, insetMaxWidth),
+ 0,
+ Math.min(measuredHeight, insetMaxHeight),
+ measure);
}
} else {
float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index d5d2e03..15b54a3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -32,6 +32,7 @@
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -386,6 +387,17 @@
DebugLog.e();
}
+ @Override
+ public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+ super.getLocationInWindow(value, forSelf);
+
+ if (!forSelf && mHorizontalScrollDelegate instanceof ScrollModifierOperation) {
+ ScrollModifierOperation smo = (ScrollModifierOperation) mHorizontalScrollDelegate;
+
+ value[0] += smo.getScrollX();
+ }
+ }
+
/**
* The name of the class
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index d383ee9..120c740 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -77,6 +77,7 @@
private final Size mCachedSize = new Size(0f, 0f);
@Nullable private String mCachedString = "";
+ @Nullable private String mNewString;
Platform.ComputedTextLayout mComputedTextLayout;
@@ -99,7 +100,7 @@
if (cachedString != null && cachedString.equalsIgnoreCase(mCachedString)) {
return;
}
- mCachedString = cachedString;
+ mNewString = cachedString;
if (mType == -1) {
if (mFontFamilyId != -1) {
String fontFamily = context.getText(mFontFamilyId);
@@ -119,8 +120,6 @@
mType = 0;
}
}
- mTextW = -1;
- mTextH = -1;
if (mHorizontalScrollDelegate != null) {
mHorizontalScrollDelegate.reset();
@@ -351,6 +350,9 @@
mPaint.setColor(mColor);
context.replacePaint(mPaint);
float[] bounds = new float[4];
+ if (mNewString != null && !mNewString.equals(mCachedString)) {
+ mCachedString = mNewString;
+ }
if (mCachedString == null) {
return;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
new file mode 100644
index 0000000..b1f2d2d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
+
+import java.util.List;
+
+/** Set an optional priority on a component within a collapsible layout */
+public class CollapsiblePriorityModifierOperation extends Operation
+ implements ModifierOperation, Serializable {
+ private static final int OP_CODE = Operations.MODIFIER_COLLAPSIBLE_PRIORITY;
+ public static final String CLASS_NAME = "CollapsiblePriorityModifierOperation";
+
+ private float mPriority;
+ private int mOrientation;
+
+ public CollapsiblePriorityModifierOperation(int orientation, float priority) {
+ mOrientation = orientation;
+ mPriority = priority;
+ }
+
+ public float getPriority() {
+ return mPriority;
+ }
+
+ public int getOrientation() {
+ return mOrientation;
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mOrientation, mPriority);
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ // nothing
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return "";
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int orientation = buffer.readInt();
+ float priority = buffer.readFloat();
+ operations.add(new CollapsiblePriorityModifierOperation(orientation, priority));
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "CollapsiblePriorityModifier")
+ .description("Add additional priority to children of Collapsible layouts")
+ .field(DocumentedOperation.INT, "orientation", "Horizontal(0) or Vertical (1)")
+ .field(DocumentedOperation.FLOAT, "priority", "The associated priority");
+ }
+
+ /**
+ * Writes out the CollapsiblePriorityModifier to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param priority priority value
+ * @param orientation orientation (HORIZONTAL or VERTICAL)
+ */
+ public static void apply(@NonNull WireBuffer buffer, int orientation, float priority) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(orientation);
+ buffer.writeFloat(priority);
+ }
+
+ @Override
+ public void serialize(MapSerializer serializer) {
+ serializer
+ .addTags(SerializeTags.MODIFIER)
+ .addType(name())
+ .add("orientation", mOrientation)
+ .add("priority", mPriority);
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "PRIORITY = [" + getPriority() + "] (" + mOrientation + ")");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
index 3e1f32d..42692f9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ScrollModifierOperation.java
@@ -430,9 +430,35 @@
}
@Override
- public boolean showOnScreen(RemoteContext context, int childId) {
- // TODO correct this when we trust the bounds in parent
- return scrollByOffset(context, -1000) != 0;
+ public boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+ float offset = mHostDimension * 0.7f;
+
+ if (direction == ScrollDirection.FORWARD
+ || direction == ScrollDirection.DOWN
+ || direction == ScrollDirection.RIGHT) {
+ offset *= -1;
+ }
+
+ return scrollByOffset(context, (int) offset) != 0;
+ }
+
+ @Override
+ public boolean showOnScreen(RemoteContext context, Component child) {
+ float[] locationInWindow = new float[2];
+ child.getLocationInWindow(locationInWindow);
+
+ int offset = 0;
+ if (handlesVerticalScroll()) {
+ offset = (int) -locationInWindow[1];
+ } else {
+ offset = (int) -locationInWindow[0];
+ }
+
+ if (offset == 0) {
+ return true;
+ } else {
+ return scrollByOffset(context, offset) != 0;
+ }
}
@Nullable
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index a95a175..120c7ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -35,7 +35,10 @@
@NonNull
public static String floatToString(
float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
-
+ boolean isNeg = value < 0;
+ if (isNeg) {
+ value = -value;
+ }
int integerPart = (int) value;
float fractionalPart = value % 1;
@@ -54,14 +57,13 @@
integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
}
if (afterDecimalPoint == 0) {
- return integerPartString;
+ return ((isNeg) ? "-" : "") + integerPartString;
}
// Convert fractional part to string and pad with zeros
for (int i = 0; i < afterDecimalPoint; i++) {
fractionalPart *= 10;
}
-
fractionalPart = Math.round(fractionalPart);
for (int i = 0; i < afterDecimalPoint; i++) {
@@ -87,6 +89,6 @@
fact = fact + new String(c);
}
- return integerPartString + "." + fact;
+ return ((isNeg) ? "-" : "") + integerPartString + "." + fact;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
index 3d1bd12..1610e63 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
/**
* Interface for components that support scrolling.
@@ -48,13 +49,23 @@
}
/**
+ * Scrolls the content in the specified direction.
+ *
+ * @param direction the direction to scroll
+ * @return whether a scroll was possible
+ */
+ default boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+ return false;
+ }
+
+ /**
* Show a child with the given ID on the screen, typically scrolling so it's fully on screen.
*
- * @param childId The ID of the child to check for visibility.
+ * @param child The child (including nested) to check for visibility.
* @return {@code true} if the child with the given ID could be shown on screen; {@code false}
* otherwise.
*/
- default boolean showOnScreen(RemoteContext context, int childId) {
+ default boolean showOnScreen(RemoteContext context, Component child) {
return false;
}
@@ -108,4 +119,13 @@
return mCanScrollBackwards;
}
}
+
+ enum ScrollDirection {
+ FORWARD,
+ BACKWARD,
+ UP,
+ DOWN,
+ LEFT,
+ RIGHT,
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index e1f2924..575a6b2 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -22,7 +22,6 @@
import android.graphics.Canvas;
import android.graphics.Paint;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.TouchListener;
import com.android.internal.widget.remotecompose.core.VariableSupport;
@@ -43,7 +42,6 @@
*
* <p>This is used to play the RemoteCompose operations on Android.
*/
-@VisibleForTesting
public class AndroidRemoteContext extends RemoteContext {
public void useCanvas(Canvas canvas) {
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 0bc99ab..17f4fc8 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -102,6 +102,7 @@
mDocument = value;
mDocument.initializeContext(mARContext);
mDisable = false;
+ mARContext.setDocLoadTime();
mARContext.setAnimationEnabled(true);
mARContext.setDensity(mDensity);
mARContext.setUseChoreographer(true);
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index dec724b..e1b3479 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -558,8 +558,7 @@
delete parcel;
}
-static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
-{
+static Parcel* parcel_for_marshall(JNIEnv* env, jlong nativePtr) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel == NULL) {
return NULL;
@@ -577,6 +576,16 @@
return NULL;
}
+ return parcel;
+}
+
+static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
+{
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return NULL;
+ }
+
jbyteArray ret = env->NewByteArray(parcel->dataSize());
if (ret != NULL)
@@ -592,6 +601,56 @@
return ret;
}
+static long ensure_capacity(JNIEnv* env, Parcel* parcel, jint remaining) {
+ long dataSize = parcel->dataSize();
+ if (remaining < dataSize) {
+ jnihelp::ThrowException(env, "java/nio/BufferOverflowException", "()V");
+ return -1;
+ }
+ return dataSize;
+}
+
+static int android_os_Parcel_marshall_array(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jbyteArray data, jint offset, jint remaining)
+{
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return 0;
+ }
+
+ long data_size = ensure_capacity(env, parcel, remaining);
+ if (data_size < 0) {
+ return 0;
+ }
+
+ jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
+ if (array != NULL)
+ {
+ memcpy(array + offset, parcel->data(), data_size);
+ env->ReleasePrimitiveArrayCritical(data, array, 0);
+ }
+ return data_size;
+}
+
+static int android_os_Parcel_marshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject javaBuffer, jint offset, jint remaining) {
+ Parcel* parcel = parcel_for_marshall(env, nativePtr);
+ if (parcel == NULL) {
+ return 0;
+ }
+
+ long data_size = ensure_capacity(env, parcel, remaining);
+ if (data_size < 0) {
+ return 0;
+ }
+
+ jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+ if (buffer != NULL) {
+ memcpy(buffer + offset, parcel->data(), data_size);
+ }
+ return data_size;
+}
+
static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
jbyteArray data, jint offset, jint length)
{
@@ -613,6 +672,25 @@
}
}
+static void android_os_Parcel_unmarshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject javaBuffer, jint offset, jint length)
+{
+ Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+ if (parcel == NULL || length < 0) {
+ return;
+ }
+
+ jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+ if (buffer)
+ {
+ parcel->setDataSize(length);
+ parcel->setDataPosition(0);
+
+ void* raw = parcel->writeInplace(length);
+ memcpy(raw, (buffer + offset), length);
+ }
+}
+
static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr,
jlong otherNativePtr)
{
@@ -911,7 +989,10 @@
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
+ {"nativeMarshallArray", "(J[BII)I", (void*)android_os_Parcel_marshall_array},
+ {"nativeMarshallBuffer", "(JLjava/nio/ByteBuffer;II)I", (void*)android_os_Parcel_marshall_buffer},
{"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
+ {"nativeUnmarshallBuffer", "(JLjava/nio/ByteBuffer;II)V", (void*)android_os_Parcel_unmarshall_buffer},
{"nativeCompareData", "(JJ)I", (void*)android_os_Parcel_compareData},
{"nativeCompareDataInRange", "(JIJII)Z", (void*)android_os_Parcel_compareDataInRange},
{"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 59e01bf..9df351f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -35,7 +35,6 @@
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
import "frameworks/base/core/proto/android/server/alarm/alarmmanagerservice.proto";
-import "frameworks/base/core/proto/android/server/bluetooth_manager_service.proto";
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
import "frameworks/base/core/proto/android/server/location/context_hub.proto";
@@ -483,10 +482,8 @@
(section).args = "connmetrics --proto"
];
- optional com.android.server.BluetoothManagerServiceDumpProto bluetooth_manager = 3050 [
- (section).type = SECTION_DUMPSYS,
- (section).args = "bluetooth_manager --proto"
- ];
+ // Deprecated BluetoothManagerServiceDumpProto
+ reserved 3050;
optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
(section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 325790c..8393f8b 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -290,7 +290,16 @@
optional SettingProto apply_ramping_ringer = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ message Display {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto cv_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional Display display = 39;
+
+
+
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 39;
+ // Next tag = 40;
}
diff --git a/core/proto/android/server/Android.bp b/core/proto/android/server/Android.bp
deleted file mode 100644
index 362daa7..0000000
--- a/core/proto/android/server/Android.bp
+++ /dev/null
@@ -1,28 +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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "srcs_bluetooth_manager_service_proto",
- srcs: [
- "bluetooth_manager_service.proto",
- ],
- visibility: ["//packages/modules/Bluetooth:__subpackages__"],
-}
-
diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto
deleted file mode 100644
index c33f66a..0000000
--- a/core/proto/android/server/bluetooth_manager_service.proto
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-package com.android.server;
-
-import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/bluetooth/enums.proto";
-
-option java_multiple_files = true;
-
-message BluetoothManagerServiceDumpProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- message ActiveLog {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int64 timestamp_ms = 1;
- optional bool enable = 2;
- optional string package_name = 3;
- optional .android.bluetooth.EnableDisableReasonEnum reason = 4;
- }
-
- optional bool enabled = 1;
- optional int32 state = 2;
- optional string state_name = 3;
- optional string address = 4 [(.android.privacy).dest = DEST_EXPLICIT];
- optional string name = 5 [(.android.privacy).dest = DEST_EXPLICIT];
- optional int64 last_enabled_time_ms = 6;
- optional int64 curr_timestamp_ms = 7;
- repeated ActiveLog active_logs = 8;
- optional int32 num_crashes = 9;
- optional bool crash_log_maxed = 10;
- repeated int64 crash_timestamps_ms = 11;
- optional int32 num_ble_apps = 12;
- repeated string ble_app_package_names = 13;
-}
\ No newline at end of file
diff --git a/core/res/res/layout/accessibility_autoclick_type_panel.xml b/core/res/res/layout/accessibility_autoclick_type_panel.xml
index 902ef7f..615af6f 100644
--- a/core/res/res/layout/accessibility_autoclick_type_panel.xml
+++ b/core/res/res/layout/accessibility_autoclick_type_panel.xml
@@ -49,7 +49,8 @@
android:id="@+id/accessibility_autoclick_drag_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_drag"
- android:src="@drawable/accessibility_autoclick_drag" />
+ android:src="@drawable/accessibility_autoclick_drag"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -60,7 +61,8 @@
android:id="@+id/accessibility_autoclick_double_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_double_click"
- android:src="@drawable/accessibility_autoclick_double_click" />
+ android:src="@drawable/accessibility_autoclick_double_click"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -71,7 +73,8 @@
android:id="@+id/accessibility_autoclick_right_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_right_click"
- android:src="@drawable/accessibility_autoclick_right_click" />
+ android:src="@drawable/accessibility_autoclick_right_click"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -82,7 +85,8 @@
android:id="@+id/accessibility_autoclick_scroll_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_scroll"
- android:src="@drawable/accessibility_autoclick_scroll" />
+ android:src="@drawable/accessibility_autoclick_scroll"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -93,7 +97,8 @@
android:id="@+id/accessibility_autoclick_left_click_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_left_click"
- android:src="@drawable/accessibility_autoclick_left_click" />
+ android:src="@drawable/accessibility_autoclick_left_click"
+ android:clickable="false" />
</LinearLayout>
</LinearLayout>
@@ -114,7 +119,8 @@
android:id="@+id/accessibility_autoclick_pause_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_pause"
- android:src="@drawable/accessibility_autoclick_pause" />
+ android:src="@drawable/accessibility_autoclick_pause"
+ android:clickable="false" />
</LinearLayout>
<LinearLayout
@@ -125,7 +131,8 @@
android:id="@+id/accessibility_autoclick_position_button"
style="@style/AccessibilityAutoclickPanelImageButtonStyle"
android:contentDescription="@string/accessibility_autoclick_position"
- android:src="@drawable/accessibility_autoclick_position" />
+ android:src="@drawable/accessibility_autoclick_position"
+ android:clickable="false" />
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_action_list.xml b/core/res/res/layout/notification_2025_action_list.xml
index 053aca0..6c07ec1 100644
--- a/core/res/res/layout/notification_2025_action_list.xml
+++ b/core/res/res/layout/notification_2025_action_list.xml
@@ -22,6 +22,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/notification_2025_action_list_margin_bottom"
+ android:minHeight="@dimen/notification_2025_action_list_min_height"
>
<LinearLayout
diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml
index 7ec2450..16c9500 100644
--- a/core/res/res/layout/notification_2025_conversation_icon_container.xml
+++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml
@@ -29,7 +29,8 @@
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="@dimen/notification_2025_margin"
+ android:layout_marginHorizontal="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_gravity="top|center_horizontal"
diff --git a/core/res/res/layout/notification_2025_reply_history_container.xml b/core/res/res/layout/notification_2025_reply_history_container.xml
index 6923b59..256f7b8 100644
--- a/core/res/res/layout/notification_2025_reply_history_container.xml
+++ b/core/res/res/layout/notification_2025_reply_history_container.xml
@@ -28,16 +28,16 @@
android:layout_width="match_parent"
android:layout_height="1dip"
android:id="@+id/action_divider"
- android:layout_marginTop="@dimen/notification_content_margin"
- android:layout_marginBottom="@dimen/notification_content_margin"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_2025_margin"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:background="@drawable/notification_template_divider" />
<TextView
android:id="@+id/notification_material_reply_text_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:visibility="gone"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
@@ -46,7 +46,7 @@
android:id="@+id/notification_material_reply_text_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:visibility="gone"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
@@ -56,13 +56,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:layout_marginEnd="@dimen/notification_content_margin_end">
+ android:layout_marginEnd="@dimen/notification_2025_margin">
<TextView
android:id="@+id/notification_material_reply_text_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_gravity="center"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
android:singleLine="true" />
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 63f32e3..57c89b9 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -74,7 +74,6 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
android:orientation="vertical"
@@ -157,7 +156,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index 732021c6..c57196e 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -35,7 +35,6 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
android:clipChildren="false"
android:layout_weight="1"
>
@@ -78,9 +77,7 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_margin"
android:clipChildren="false"
android:orientation="vertical"
@@ -152,7 +149,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
@@ -173,21 +170,13 @@
</FrameLayout>
- <LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ <include layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="-20dp"
- android:clipChildren="false"
- android:orientation="vertical">
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
- <include layout="@layout/notification_2025_action_list" />
- </LinearLayout>
+ android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
+ <include layout="@layout/notification_2025_action_list" />
</LinearLayout>
</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
index 1ee7ddc..1c6fcb5 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
@@ -35,7 +35,6 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
android:clipChildren="false"
android:layout_weight="1"
>
@@ -78,17 +77,11 @@
android:clipChildren="false"
>
- <!--
- NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
- have the id/notification_headerless_view_column, as that is used for modifying
- vertical margins to accommodate the single-line state that base supports
- -->
<LinearLayout
+ android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:clipChildren="false"
@@ -150,7 +143,6 @@
android:layout_height="@dimen/notification_right_icon_size"
android:layout_gravity="center_vertical|end"
android:layout_marginTop="@dimen/notification_2025_margin"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
android:forceHasOverlappingRendering="false"
android:spacing="0dp"
@@ -175,7 +167,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
@@ -196,20 +188,13 @@
</FrameLayout>
- <LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ <include layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="-20dp"
- android:clipChildren="false"
- android:orientation="vertical">
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
+
</LinearLayout>
-</LinearLayout>
</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 629af77..de82f9f 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -76,7 +76,6 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
android:orientation="vertical"
@@ -178,7 +177,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index af66025..8e2cb23 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -38,7 +38,6 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
android:clipChildren="false"
android:layout_weight="1"
>
@@ -61,7 +60,8 @@
android:layout_width="@dimen/notification_2025_icon_circle_size"
android:layout_height="@dimen/notification_2025_icon_circle_size"
android:layout_alignParentStart="true"
- android:layout_margin="@dimen/notification_2025_margin"
+ android:layout_marginHorizontal="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_2025_icon_circle_padding"
/>
@@ -89,17 +89,11 @@
android:clipChildren="false"
>
- <!--
- NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
- have the id/notification_headerless_view_column, as that is used for modifying
- vertical margins to accommodate the single-line state that base supports
- -->
<LinearLayout
+ android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:clipChildren="false"
@@ -161,7 +155,6 @@
android:layout_height="@dimen/notification_right_icon_size"
android:layout_gravity="center_vertical|end"
android:layout_marginTop="@dimen/notification_2025_margin"
- android:layout_marginBottom="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
android:forceHasOverlappingRendering="false"
android:spacing="0dp"
@@ -186,7 +179,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
@@ -207,20 +200,13 @@
</FrameLayout>
- <LinearLayout
- android:id="@+id/notification_action_list_margin_target"
+ <include layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="-20dp"
- android:clipChildren="false"
- android:orientation="vertical">
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
+
</LinearLayout>
-</LinearLayout>
</com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
index 52bc7b8..b32a778 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
@@ -76,7 +76,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
index be640460..268396f 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
@@ -103,7 +103,7 @@
android:id="@+id/expand_button_touch_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:minWidth="@dimen/notification_2025_margin"
>
<include layout="@layout/notification_2025_expand_button"
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml
index 76a8581..2871107 100644
--- a/core/res/res/layout/notification_2025_template_expanded_base.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_base.xml
@@ -24,10 +24,8 @@
>
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_content_margin"
android:orientation="vertical"
>
@@ -47,7 +45,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
@@ -78,7 +76,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
index 999afa6..ead6d4c 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
@@ -28,7 +28,6 @@
<include layout="@layout/notification_2025_right_icon" />
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
@@ -43,7 +42,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
@@ -67,7 +66,7 @@
android:layout_weight="1"
android:layout_marginTop="13dp"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:background="@drawable/notification_big_picture_outline"
android:clipToOutline="true"
android:scaleType="centerCrop"
@@ -85,7 +84,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
index c9206ed..de5e71d 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_text.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
@@ -26,11 +26,9 @@
<include layout="@layout/notification_2025_template_header" />
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
- android:layout_marginBottom="@dimen/notification_content_margin"
android:clipToPadding="false"
android:orientation="vertical"
>
@@ -43,7 +41,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingStart="@dimen/notification_2025_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
+ android:paddingEnd="@dimen/notification_2025_margin"
android:clipToPadding="false"
android:orientation="vertical"
android:layout_weight="1"
@@ -84,7 +82,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
index ec21455..c096bc8 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -30,7 +30,6 @@
<include layout="@layout/notification_2025_conversation_header"/>
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
@@ -46,7 +45,7 @@
android:layout_gravity="top"
android:layout_weight="1"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
android:clipChildren="false"
>
@@ -62,7 +61,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
index 6ee82fa..6eea8cc 100644
--- a/core/res/res/layout/notification_2025_template_expanded_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
@@ -29,7 +29,6 @@
<include layout="@layout/notification_2025_conversation_header"/>
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
@@ -44,7 +43,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_weight="1"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
android:clipChildren="false"
>
@@ -64,7 +63,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
index 1eaef22..327cd7a 100644
--- a/core/res/res/layout/notification_2025_template_expanded_inbox.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
@@ -24,7 +24,6 @@
>
<include layout="@layout/notification_2025_template_header" />
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
@@ -39,7 +38,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingStart="@dimen/notification_2025_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
+ android:paddingEnd="@dimen/notification_2025_margin"
android:layout_weight="1"
android:clipToPadding="false"
android:orientation="vertical"
@@ -126,7 +125,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin" />
<include layout="@layout/notification_2025_action_list" />
</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml
index 801e339..565a558a 100644
--- a/core/res/res/layout/notification_2025_template_expanded_media.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_media.xml
@@ -41,7 +41,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
<include layout="@layout/notification_template_part_line1"/>
@@ -53,7 +53,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_media_actions_margin_start"
- android:minHeight="@dimen/notification_content_margin"
+ android:minHeight="@dimen/notification_2025_margin"
>
<!-- Nesting in FrameLayout is required to ensure that the marginStart actually applies
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
index 62059af..df48479 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -29,7 +29,6 @@
<include layout="@layout/notification_2025_template_header"/>
<com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
@@ -44,7 +43,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_weight="1"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
android:clipChildren="false"
>
@@ -64,7 +63,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ android:layout_marginEnd="@dimen/notification_2025_margin" />
<include layout="@layout/notification_2025_action_list" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml
index cf39d8b..b929b9e 100644
--- a/core/res/res/layout/notification_2025_template_expanded_progress.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml
@@ -25,10 +25,8 @@
>
<LinearLayout
- android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_content_margin"
android:orientation="vertical"
>
@@ -48,7 +46,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
@@ -114,7 +112,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/layout/notification_2025_template_heads_up_base.xml b/core/res/res/layout/notification_2025_template_heads_up_base.xml
index 4d3b245..e416c50 100644
--- a/core/res/res/layout/notification_2025_template_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_heads_up_base.xml
@@ -56,7 +56,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginEnd="@dimen/notification_2025_margin"
android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7a38dce..43486f8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2831,6 +2831,10 @@
<!-- Whether dreams are disabled when ambient mode is suppressed. -->
<bool name="config_dreamsDisabledByAmbientModeSuppressionConfig">false</bool>
+ <!-- The default for the setting that controls when to auto-start hub mode.
+ 0 means "never" -->
+ <integer name="config_whenToStartHubModeDefault">0</integer>
+
<!-- The duration in milliseconds of the dream opening animation. -->
<integer name="config_dreamOpenAnimationDuration">250</integer>
<!-- The duration in milliseconds of the dream closing animation. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6e54083..b5ed28f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -295,6 +295,11 @@
<!-- The margin of the notification action list at the bottom in the 2025 redesign -->
<dimen name="notification_2025_action_list_margin_bottom">6dp</dimen>
+ <!-- The minimum height of the notification action container, to act as a bottom padding for the
+ notification when there are no actions. This should always be equal to
+ notification_2025_margin - notification_2025_action_list_margin_bottom. -->
+ <dimen name="notification_2025_action_list_min_height">10dp</dimen>
+
<!-- The overall height of the emphasized notification action -->
<dimen name="notification_action_emphasized_height">48dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 922d59d..bab4a3d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2072,6 +2072,7 @@
<java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
<java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
<java-symbol type="bool" name="config_glanceableHubEnabled" />
+ <java-symbol type="integer" name="config_whenToStartHubModeDefault" />
<java-symbol type="integer" name="config_keyguardDrawnTimeout" />
<java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
index e8b295b..0287e6c 100644
--- a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -22,7 +22,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.usb.UsbDevice;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -118,6 +118,6 @@
verify(mBrailleDisplayCallback).onConnectionFailed(
BrailleDisplayController.BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS);
- verifyZeroInteractions(mAccessibilityServiceConnection);
+ verifyNoMoreInteractions(mAccessibilityServiceConnection);
}
}
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index dccbf40..70b4150 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -233,7 +233,7 @@
/**
* Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
*/
- private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
+ public class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
static final String ATTRIBUTES_NAME = "test";
@@ -245,12 +245,6 @@
SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
}
- TestServicesCache(Injector<TestServiceType> injector,
- XmlSerializerAndParser<TestServiceType> serializerAndParser) {
- super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
- serializerAndParser);
- }
-
@Override
public TestServiceType parseServiceAttributes(Resources res, String packageName,
AttributeSet attrs) {
@@ -338,7 +332,7 @@
}
}
- static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
+ public static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException {
out.attribute(null, "type", item.type);
@@ -353,7 +347,7 @@
}
}
- static class TestServiceType implements Parcelable {
+ public static class TestServiceType implements Parcelable {
final String type;
final String value;
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
new file mode 100644
index 0000000..8349659
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.pm.Flags.FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.RegisteredServicesCacheTest.TestServiceType;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.os.BackgroundThread;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.content.pm.RegisteredServicesCache}
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE)
+public class RegisteredServicesCacheUnitTest {
+ private static final String TAG = "RegisteredServicesCacheUnitTest";
+ private static final int U0 = 0;
+ private static final int U1 = 1;
+ private static final int UID1 = 1;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private final ResolveInfo mResolveInfo1 = new ResolveInfo();
+ private final ResolveInfo mResolveInfo2 = new ResolveInfo();
+ private final TestServiceType mTestServiceType1 = new TestServiceType("t1", "value1");
+ private final TestServiceType mTestServiceType2 = new TestServiceType("t2", "value2");
+ @Mock
+ RegisteredServicesCache.Injector<TestServiceType> mMockInjector;
+ @Mock
+ Context mMockContext;
+ Handler mMockBackgroundHandler;
+ @Mock
+ PackageManager mMockPackageManager;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mMockInjector.getContext()).thenReturn(mMockContext);
+ mMockBackgroundHandler = spy(BackgroundThread.getHandler());
+ when(mMockInjector.getBackgroundHandler()).thenReturn(mMockBackgroundHandler);
+ doReturn(mock(Intent.class)).when(mMockContext).registerReceiverAsUser(any(), any(), any(),
+ any(), any());
+ doReturn(mock(Intent.class)).when(mMockContext).registerReceiver(any(), any(), any(),
+ any());
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+
+ addServiceInfoIntoResolveInfo(mResolveInfo1, "r1.package.name" /* packageName */,
+ "r1.service.name" /* serviceName */);
+ addServiceInfoIntoResolveInfo(mResolveInfo2, "r2.package.name" /* packageName */,
+ "r2.service.name" /* serviceName */);
+ }
+
+ @Test
+ public void testSaveServiceInfoIntoCaches() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+ PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo2.serviceInfo.packageName),
+ anyInt(), eq(U1))).thenReturn(packageInfo2);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ int u1uid = UserHandle.getUid(U1, UID1);
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+ mTestServiceType2, u1uid, mResolveInfo2.serviceInfo.getComponentName(),
+ 2000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U1, mResolveInfo2, serviceInfo2);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.invalidateCache(U1);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ }
+
+ @Test
+ public void testClearServiceInfoCachesAfterRemoveUserId() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.onUserRemoved(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ }
+
+ @Test
+ public void testGetServiceInfoCachesForMultiUser() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U1))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.clearServicesForQuerying();
+ int u1uid = UserHandle.getUid(U1, UID1);
+ assertThat(u1uid).isNotEqualTo(UID1);
+
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+ mTestServiceType1, u1uid, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U1, mResolveInfo1, serviceInfo2);
+
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.invalidateCache(U1);
+
+ // There is a bug to return the same info from the cache for different users. Make sure it
+ // will return the different info from the cache for different users.
+ Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> serviceInfos;
+ serviceInfos = testServicesCache.getAllServices(U0);
+ // Make sure the service info is retrieved from the cache for U0.
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+ assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(UID1);
+ }
+
+ serviceInfos = testServicesCache.getAllServices(U1);
+ // Make sure the service info is retrieved from the cache for U1.
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+ assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(u1uid);
+ }
+ }
+
+ @Test
+ public void testUpdateServiceInfoIntoCachesWhenPackageInfoNotFound() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+ reset(mMockPackageManager);
+
+ doThrow(new SecurityException("")).when(mMockPackageManager).getPackageInfoAsUser(
+ eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0));
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), anyLong());
+ }
+
+ @Test
+ public void testUpdateServiceInfoIntoCachesWhenTheApplicationHasBeenUpdated() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ reset(testServicesCache);
+ reset(mMockPackageManager);
+
+ PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo2);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(2000L));
+ }
+
+ @Test
+ public void testClearServiceInfoCachesAfterTimeout() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ // Immediately invoke run on the Runnable posted to the handler
+ doAnswer(invocation -> {
+ Message message = invocation.getArgument(0);
+ message.getCallback().run();
+ return true;
+ }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ }
+
+ private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
+ TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) {
+ final ComponentInfo info = new ComponentInfo();
+ info.applicationInfo = new ApplicationInfo();
+ info.applicationInfo.uid = uid;
+ return new RegisteredServicesCache.ServiceInfo<>(type, info, componentName, lastUpdateTime);
+ }
+
+ private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName,
+ String serviceName) {
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = packageName;
+ serviceInfo.name = serviceName;
+ resolveInfo.serviceInfo = serviceInfo;
+ }
+
+ private PackageInfo createPackageInfo(long lastUpdateTime) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.lastUpdateTime = lastUpdateTime;
+ return packageInfo;
+ }
+
+ /**
+ * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
+ */
+ public class TestRegisteredServicesCache extends RegisteredServicesCache<TestServiceType> {
+ static final String SERVICE_INTERFACE = "RegisteredServicesCacheUnitTest";
+ static final String SERVICE_META_DATA = "RegisteredServicesCacheUnitTest";
+ static final String ATTRIBUTES_NAME = "test";
+ private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices =
+ new SparseArray<>();
+
+ public TestRegisteredServicesCache(Injector<TestServiceType> injector,
+ XmlSerializerAndParser<TestServiceType> serializerAndParser) {
+ super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
+ serializerAndParser);
+ }
+
+ @Override
+ public TestServiceType parseServiceAttributes(Resources res, String packageName,
+ AttributeSet attrs) {
+ return null;
+ }
+
+ @Override
+ protected List<ResolveInfo> queryIntentServices(int userId) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId,
+ new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
+ return new ArrayList<>(map.keySet());
+ }
+
+ void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
+ ServiceInfo<TestServiceType> serviceInfo) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
+ if (map == null) {
+ map = new HashMap<>();
+ mServices.put(userId, map);
+ }
+ map.put(resolveInfo, serviceInfo);
+ }
+
+ void clearServicesForQuerying() {
+ mServices.clear();
+ }
+
+ @Override
+ protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo,
+ long lastUpdateTime) throws XmlPullParserException, IOException {
+ int size = mServices.size();
+ for (int i = 0; i < size; i++) {
+ Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
+ ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
+ if (serviceInfo != null) {
+ return serviceInfo;
+ }
+ }
+ throw new IllegalArgumentException("Unexpected service " + resolveInfo);
+ }
+
+ @Override
+ public void onUserRemoved(int userId) {
+ super.onUserRemoved(userId);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
index 8b513cb..524e355 100644
--- a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
@@ -136,9 +136,11 @@
SystemFeaturesCache cache = new SystemFeaturesCache(features);
SystemFeaturesCache.clearInstance();
+ assertThat(SystemFeaturesCache.hasInstance()).isFalse();
assertThrows(IllegalStateException.class, () -> SystemFeaturesCache.getInstance());
SystemFeaturesCache.setInstance(cache);
+ assertThat(SystemFeaturesCache.hasInstance()).isTrue();
assertThat(SystemFeaturesCache.getInstance()).isEqualTo(cache);
assertThrows(
@@ -149,6 +151,7 @@
@Test
public void testSingletonAutomaticallySetWithFeatureEnabled() {
assumeTrue(android.content.pm.Flags.cacheSdkSystemFeatures());
+ assertThat(SystemFeaturesCache.hasInstance()).isTrue();
assertThat(SystemFeaturesCache.getInstance()).isNotNull();
}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index de5f0ff..34650be 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -264,7 +264,7 @@
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
@@ -272,7 +272,7 @@
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
@@ -280,7 +280,7 @@
/* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
- Mockito.verifyZeroInteractions(mDisplayListener);
+ Mockito.verifyNoMoreInteractions(mDisplayListener);
}
@Test
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index 3e652010..bb05910 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,6 +29,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -416,4 +418,63 @@
int binderEndPos = pA.dataPosition();
assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
}
+
+ private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
+
+ // Allow for some Parcel overhead
+ private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
+
+ @Test
+ public void testMarshall_ByteBuffer_wrapped() {
+ ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
+ testMarshall_ByteBuffer(bb);
+ }
+
+ @Test
+ public void testMarshall_DirectByteBuffer() {
+ ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
+ testMarshall_ByteBuffer(bb);
+ }
+
+ private void testMarshall_ByteBuffer(ByteBuffer bb) {
+ // Ensure that Parcel respects the starting offset by not starting at 0
+ bb.position(1);
+ bb.mark();
+
+ // Parcel test data, then marshall into the ByteBuffer
+ Parcel p1 = Parcel.obtain();
+ p1.writeByteArray(TEST_DATA);
+ p1.marshall(bb);
+ p1.recycle();
+
+ assertTrue(bb.position() > 1);
+ bb.reset();
+
+ // Unmarshall test data into a new Parcel
+ Parcel p2 = Parcel.obtain();
+ bb.reset();
+ p2.unmarshall(bb);
+ assertTrue(bb.position() > 1);
+ p2.setDataPosition(0);
+ byte[] marshalled = p2.marshall();
+
+ bb.reset();
+ for (int i = 0; i < TEST_DATA.length; i++) {
+ assertEquals(bb.get(), marshalled[i]);
+ }
+
+ byte[] testDataCopy = new byte[TEST_DATA.length];
+ p2.setDataPosition(0);
+ p2.readByteArray(testDataCopy);
+ for (int i = 0; i < TEST_DATA.length; i++) {
+ assertEquals(TEST_DATA[i], testDataCopy[i]);
+ }
+
+ // Test that overflowing the buffer throws an exception
+ bb.reset();
+ // Leave certainly not enough room for the test data
+ bb.limit(bb.position() + TEST_DATA.length - 1);
+ assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
+ p2.recycle();
+ }
}
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 8ac9292..50cd4c0 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -29,7 +29,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.CancellationSignal;
@@ -230,7 +230,7 @@
InsetsController secondController = mock(InsetsController.class);
mPendingInsetsController.replayAndAttach(secondController);
verify(mReplayedController).show(eq(systemBars()));
- verifyZeroInteractions(secondController);
+ verifyNoMoreInteractions(secondController);
}
@Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index eb482f2e..f811d8e 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -20,7 +20,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.MockitoAnnotations.initMocks;
import android.os.Bundle;
@@ -74,7 +74,7 @@
MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
assertEquals("Node got lost along the way", nodeFromConnection, node);
- verifyZeroInteractions(mMockCache);
+ verifyNoMoreInteractions(mMockCache);
}
@Test
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index 5f89f9c..8bbe81d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -27,7 +27,7 @@
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -130,7 +130,7 @@
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -151,7 +151,7 @@
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -172,7 +172,7 @@
mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
}
@Test
@@ -197,7 +197,7 @@
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -227,7 +227,7 @@
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNotNull();
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -255,7 +255,7 @@
session.sendEvent(EVENT);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -272,8 +272,8 @@
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
- verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -289,8 +289,8 @@
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
- verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
assertThat(session.mEvents).containsExactly(EVENT);
}
@@ -307,7 +307,7 @@
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
assertEventFlushedContentCapture(options);
}
@@ -325,7 +325,7 @@
session.flush(REASON);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
assertEventFlushedContentCapture(options);
}
@@ -339,7 +339,7 @@
mTestableLooper.processAllMessages();
verify(mMockSystemServerInterface).finishSession(anyInt());
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mDirectServiceInterface).isNull();
assertThat(session.mContentProtectionEventProcessor).isNull();
}
@@ -352,8 +352,8 @@
session.resetSession(/* newState= */ 0);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockSystemServerInterface);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockSystemServerInterface);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mDirectServiceInterface).isNull();
assertThat(session.mContentProtectionEventProcessor).isNull();
}
@@ -370,8 +370,8 @@
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -388,8 +388,8 @@
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
@@ -407,8 +407,8 @@
notifyContentCaptureEvents(session);
mTestableLooper.processAllMessages();
- verifyZeroInteractions(mMockContentCaptureDirectManager);
- verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+ verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index ba0dbf4..e75452c 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -26,7 +26,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -443,7 +442,7 @@
mTestLooper.dispatchAll();
verify(mMockEventBuffer, never()).clear();
verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ verifyNoMoreInteractions(mMockContentCaptureManager);
}
private void assertLoginDetected() throws Exception {
diff --git a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
index b61d868..3570c2e 100644
--- a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
@@ -33,7 +33,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
@@ -159,7 +158,7 @@
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
}
@Test
@@ -180,19 +179,19 @@
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build();
mDefaultReceiver.onReceiveContent(mEditText, payload);
- verifyZeroInteractions(ic.mMock);
+ verifyNoMoreInteractions(ic.mMock);
}
private static class MyInputConnection extends InputConnectionWrapper {
diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
index 381b566..ad68e38 100644
--- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
+++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
@@ -37,7 +37,7 @@
fun generatesProgress_onStart() {
val linearTracker = linearTouchTracker()
linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- val event = linearTracker.createStartEvent(null)
+ val event = linearTracker.createStartEvent()
assertEquals(0f, event.progress, 0f)
}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 215c162..66524d1 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -695,8 +695,7 @@
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT);
}
private void verifyImeCallackRegistrations() throws RemoteException {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 8900745..1977ff5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -44,7 +44,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -522,7 +522,7 @@
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
+ verifyNoMoreInteractions(mAlertDialogBuilder, mAlertDialog);
verify(mToast).show();
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
@@ -615,7 +615,7 @@
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mToast);
+ verifyNoMoreInteractions(mToast);
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@@ -632,7 +632,7 @@
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verifyZeroInteractions(mToast);
+ verifyNoMoreInteractions(mToast);
verify(mAccessibilityManagerService).performAccessibilityShortcut(
Display.DEFAULT_DISPLAY, HARDWARE, null);
}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 17fe15c..21ef391 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -28,7 +28,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.annotation.EnforcePermission;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
@@ -207,7 +207,7 @@
mService.setSupportedStates(List.of(OTHER_DEVICE_STATE));
mService.setBaseState(OTHER_DEVICE_STATE);
- verifyZeroInteractions(callback);
+ verifyNoMoreInteractions(callback);
}
@Test
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 3b40148..f25ceb1 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -139,4 +139,10 @@
<install-in-user-type package="com.android.multiuser">
<install-in user-type="FULL" />
</install-in-user-type>
+
+ <!-- PrivateSpace App, only install in private profile -->
+ <install-in-user-type package="com.android.privatespace">
+ <install-in user-type="android.os.usertype.profile.PRIVATE" />
+ </install-in-user-type>
+
</config>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index cd5a54c..a4ba2b3 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -755,6 +755,9 @@
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
b.mDensity = mDensity;
+ if (hasGainmap()) {
+ b.setGainmap(getGainmap().asShared());
+ }
}
return b;
}
@@ -767,7 +770,8 @@
*/
@NonNull
public Bitmap asShared() {
- if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)) {
+ if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)
+ && (!hasGainmap() || getGainmap().asShared() == getGainmap())) {
return this;
}
Bitmap shared = createAshmemBitmap();
@@ -2091,7 +2095,7 @@
*/
public void setGainmap(@Nullable Gainmap gainmap) {
checkRecycled("Bitmap is recycled");
- mGainmap = null;
+ mGainmap = gainmap;
nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr);
}
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index 7fc13db..2417a12 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -161,6 +161,18 @@
}
/**
+ * @hide
+ */
+ public Gainmap asShared() {
+ final Bitmap sharedContents = mGainmapContents.asShared();
+ if (sharedContents == mGainmapContents) {
+ return this;
+ } else {
+ return new Gainmap(sharedContents, nCreateCopy(mNativePtr));
+ }
+ }
+
+ /**
* @return Returns the image data of the gainmap represented as a Bitmap. This is represented
* as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored
* such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 4daaf9c..225303b 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -103,4 +103,35 @@
</LinearLayout>
+ <!-- Menu option to move a bubble to fullscreen; only visible if bubble anything is enabled. -->
+ <LinearLayout
+ android:id="@+id/bubble_manage_menu_fullscreen_container"
+ android:background="@drawable/bubble_manage_menu_row"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:minHeight="@dimen/bubble_menu_item_height"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/bubble_menu_padding"
+ android:paddingEnd="@dimen/bubble_menu_padding"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/bubble_manage_menu_fullscreen_icon"
+ android:layout_width="@dimen/bubble_menu_icon_size"
+ android:layout_height="@dimen/bubble_menu_icon_size"
+ android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
+ android:tint="@color/bubbles_icon_tint"/>
+
+ <TextView
+ android:id="@+id/bubble_manage_menu_fullscreen_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:text="@string/bubble_fullscreen_text"
+ android:textColor="@androidprv:color/materialColorOnSurface"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+
+ </LinearLayout>
+
</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index e11babe..bfaa407 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -22,8 +22,8 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation"
- android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation"
+ android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
+ android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
android:orientation="vertical">
<LinearLayout
@@ -43,8 +43,7 @@
android:layout_height="@dimen/desktop_mode_handle_menu_icon_radius"
android:layout_marginStart="10dp"
android:layout_marginEnd="12dp"
- android:contentDescription="@string/app_icon_text"
- android:importantForAccessibility="no"/>
+ android:contentDescription="@string/app_icon_text" />
<com.android.wm.shell.windowdecor.MarqueedTextView
android:id="@+id/application_name"
@@ -142,7 +141,6 @@
android:contentDescription="@string/screenshot_text"
android:text="@string/screenshot_text"
android:src="@drawable/desktop_mode_ic_handle_menu_screenshot"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
<com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -150,7 +148,6 @@
android:contentDescription="@string/new_window_text"
android:text="@string/new_window_text"
android:src="@drawable/desktop_mode_ic_handle_menu_new_window"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
<com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -158,7 +155,6 @@
android:contentDescription="@string/manage_windows_text"
android:text="@string/manage_windows_text"
android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
<com.android.wm.shell.windowdecor.HandleMenuActionButton
@@ -166,7 +162,6 @@
android:contentDescription="@string/change_aspect_ratio_text"
android:text="@string/change_aspect_ratio_text"
android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
- android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout>
@@ -186,7 +181,6 @@
android:text="@string/open_in_browser_text"
android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
style="@style/DesktopModeHandleMenuActionButton"
- android:importantForAccessibility="no"
android:layout_width="0dp"
android:layout_weight="1"/>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
index de38e6f..35e7de0 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
@@ -22,8 +22,7 @@
android:layout_height="match_parent"
android:gravity="start|center_vertical"
android:paddingHorizontal="16dp"
- android:clickable="true"
- android:focusable="true"
+ android:importantForAccessibility="yes"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground">
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 9ebbf71..e1bf663 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -291,6 +291,9 @@
<!-- Corner radius for expanded view drop target -->
<dimen name="bubble_bar_expanded_view_drop_target_corner">28dp</dimen>
<dimen name="bubble_bar_expanded_view_drop_target_padding">24dp</dimen>
+ <dimen name="bubble_bar_expanded_view_drop_target_padding_top">60dp</dimen>
+ <dimen name="bubble_bar_expanded_view_drop_target_padding_bottom">24dp</dimen>
+ <dimen name="bubble_bar_expanded_view_drop_target_padding_horizontal">48dp</dimen>
<!-- Width of the box around bottom center of the screen where drag only leads to dismiss -->
<dimen name="bubble_bar_dismiss_zone_width">192dp</dimen>
<!-- Height of the box around bottom center of the screen where drag only leads to dismiss -->
@@ -525,17 +528,21 @@
<!-- The radius of the Maximize menu shadow. -->
<dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
- <!-- The width of the handle menu in desktop mode. -->
- <dimen name="desktop_mode_handle_menu_width">216dp</dimen>
+ <!-- The width of the handle menu in desktop mode plus the 2dp added for padding to account for
+ pill elevation. -->
+ <dimen name="desktop_mode_handle_menu_width">218dp</dimen>
- <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
- additional actions pill 208dp, plus 2dp spacing between them plus 4dp top padding.
- 52*3 + 52*4 + (4-1)*2 + 4 = 374 -->
- <dimen name="desktop_mode_handle_menu_height">374dp</dimen>
+ <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
+ additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
+ plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
+ <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
<!-- The elevation set on the handle menu pills. -->
<dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
+ <!-- The padding added to account for the handle menu's pills' elevation. -->
+ <dimen name="desktop_mode_handle_menu_pill_elevation_padding">2dp</dimen>
+
<!-- The height of the handle menu's "App Info" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_app_info_pill_height">52dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 637b47a..5f1db83 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -45,6 +45,7 @@
<item name="android:layout_height">52dp</item>
<item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:importantForAccessibility">no</item>
</style>
<style name="DesktopModeHandleMenuActionButtonImage">
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index b87c205..529203f 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -23,6 +23,7 @@
import android.window.DesktopModeFlags
import com.android.internal.R
import com.android.internal.policy.DesktopModeCompatUtils
+import java.util.function.Supplier
/**
* Class to decide whether to apply app compat policies in desktop mode.
@@ -34,9 +35,11 @@
private val pkgManager: PackageManager
get() = context.getPackageManager()
private val defaultHomePackage: String?
- get() = pkgManager.getHomeActivities(ArrayList())?.packageName
+ get() = defaultHomePackageSupplier?.get()
+ ?: pkgManager.getHomeActivities(ArrayList())?.packageName
private val packageInfoCache = mutableMapOf<String, Boolean>()
+ var defaultHomePackageSupplier: Supplier<String?>? = null
/**
* If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -46,15 +49,21 @@
*/
fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
- task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent)
+ task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent,
+ task.userId)
- fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
- numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
+ fun isTopActivityExemptFromDesktopWindowing(
+ packageName: String?,
+ numActivities: Int,
+ isTopActivityNoDisplay: Boolean,
+ isActivityStackTransparent: Boolean,
+ userId: Int
+ ) =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
((isSystemUiTask(packageName) ||
isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
(isTransparentTask(isActivityStackTransparent, numActivities) &&
- hasFullscreenTransparentPermission(packageName))) &&
+ hasFullscreenTransparentPermission(packageName, userId))) &&
!isTopActivityNoDisplay)
/** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */
@@ -77,16 +86,17 @@
private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
// Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
- private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
+ private fun hasFullscreenTransparentPermission(packageName: String?, userId: Int): Boolean {
if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
if (packageName == null) {
return false
}
- return packageInfoCache.getOrPut(packageName) {
+ return packageInfoCache.getOrPut("$userId@$packageName") {
try {
- val packageInfo = pkgManager.getPackageInfo(
+ val packageInfo = pkgManager.getPackageInfoAsUser(
packageName,
- PackageManager.GET_PERMISSIONS
+ PackageManager.GET_PERMISSIONS,
+ userId
)
packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
} catch (e: PackageManager.NameNotFoundException) {
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 53dede6..7f8cfae 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
@@ -448,7 +448,7 @@
final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
if (!shouldDispatchToAnimator && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
- tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent());
if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -604,7 +604,7 @@
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
- tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent());
if (!isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -1041,7 +1041,7 @@
() -> mShellExecutor.execute(this::onBackAnimationFinished));
if (mApps.length >= 1) {
- BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
+ BackMotionEvent startEvent = mCurrentTracker.createStartEvent();
dispatchOnBackStarted(mActiveCallback, startEvent);
if (startEvent.getSwipeEdge() == EDGE_NONE) {
// TODO(b/373544911): onBackStarted is dispatched here so that
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 8ac9230..cbd1e96 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
@@ -606,6 +606,10 @@
updateManageButtonIfExists();
}
+ public float getCornerRadius() {
+ return mCornerRadius;
+ }
+
/**
* Updates the size and visuals of the pointer if {@link #mPointerView} is initialized.
* Does nothing otherwise.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 70340d7..03d6b0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -103,7 +103,9 @@
private int mManageButtonHeight;
private int mOverflowHeight;
private int mMinimumFlyoutWidthLargeScreen;
- private int mBubbleBarExpandedViewDropTargetPadding;
+ private int mBarExpViewDropTargetPaddingTop;
+ private int mBarExpViewDropTargetPaddingBottom;
+ private int mBarExpViewDropTargetPaddingHorizontal;
private PointF mRestingStackPosition;
@@ -173,8 +175,12 @@
res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
mPositionRect.width() - 2 * mExpandedViewPadding
);
- mBubbleBarExpandedViewDropTargetPadding = res.getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_drop_target_padding);
+ mBarExpViewDropTargetPaddingTop = res.getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_drop_target_padding_top);
+ mBarExpViewDropTargetPaddingBottom = res.getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_drop_target_padding_bottom);
+ mBarExpViewDropTargetPaddingHorizontal = res.getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_drop_target_padding_horizontal);
if (mShowingInBubbleBar) {
mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth;
@@ -986,8 +992,15 @@
public Rect getBubbleBarExpandedViewDropTargetBounds(boolean onLeft) {
Rect bounds = new Rect();
getBubbleBarExpandedViewBounds(onLeft, false, bounds);
- bounds.inset(mBubbleBarExpandedViewDropTargetPadding,
- mBubbleBarExpandedViewDropTargetPadding);
+ // Drop target bounds are based on expanded view bounds with some padding added
+ int leftPadding = onLeft ? 0 : mBarExpViewDropTargetPaddingHorizontal;
+ int rightPadding = onLeft ? mBarExpViewDropTargetPaddingHorizontal : 0;
+ bounds.inset(
+ leftPadding,
+ mBarExpViewDropTargetPaddingTop,
+ rightPadding,
+ mBarExpViewDropTargetPaddingBottom
+ );
return bounds;
}
}
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 9272417..dd5a23a 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
@@ -91,6 +91,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
@@ -1319,7 +1320,7 @@
mBubbleContainer.bringToFront();
}
- // TODO: Create ManageMenuView and move setup / animations there
+ // TODO (b/402196554) : Create ManageMenuView and move setup / animations there
private void setUpManageMenu() {
if (mManageMenu != null) {
removeView(mManageMenu);
@@ -1377,6 +1378,22 @@
mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon);
mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name);
+ View fullscreenView = mManageMenu.findViewById(
+ R.id.bubble_manage_menu_fullscreen_container);
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ fullscreenView.setVisibility(VISIBLE);
+ fullscreenView.setOnClickListener(
+ view -> {
+ showManageMenu(false /* show */);
+ BubbleExpandedView expandedView = getExpandedView();
+ if (expandedView != null && expandedView.getTaskView() != null) {
+ expandedView.getTaskView().moveToFullscreen();
+ }
+ });
+ } else {
+ fullscreenView.setVisibility(GONE);
+ }
+
// The menu itself should respect locale direction so the icons are on the correct side.
mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
addView(mManageMenu);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index 8cd6ce0..c1841c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -612,8 +612,7 @@
mTaskLeash = taskChg.getLeash();
mRootLeash = info.getRoot(0).getLeash();
- SurfaceControl dest =
- mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl();
+ SurfaceControl dest = getExpandedView(mBubble).getViewRootImpl().getSurfaceControl();
final Runnable onPlucked = () -> {
// Need to remove the taskview AFTER applying the startTransaction because
// it isn't synchronized.
@@ -623,12 +622,12 @@
mBubbleData.setExpanded(false /* expanded */);
};
if (dest != null) {
- pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest,
+ pluck(mTaskLeash, getExpandedView(mBubble), dest,
taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x,
taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y,
- mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction,
+ getCornerRadius(mBubble), startTransaction,
onPlucked);
- mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition(
+ getExpandedView(mBubble).post(() -> mTransitions.dispatchTransition(
mTransition, info, startTransaction, finishTransaction, finishCallback,
null));
} else {
@@ -649,6 +648,20 @@
t.reparent(mTaskLeash, mRootLeash);
t.apply();
}
+
+ private View getExpandedView(@NonNull Bubble bubble) {
+ if (bubble.getBubbleBarExpandedView() != null) {
+ return bubble.getBubbleBarExpandedView();
+ }
+ return bubble.getExpandedView();
+ }
+
+ private float getCornerRadius(@NonNull Bubble bubble) {
+ if (bubble.getBubbleBarExpandedView() != null) {
+ return bubble.getBubbleBarExpandedView().getCornerRadius();
+ }
+ return bubble.getExpandedView().getCornerRadius();
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index b7761ec..69009fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -28,9 +28,9 @@
import android.view.ViewGroup;
import com.android.app.animation.Interpolators;
-import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import java.util.ArrayList;
@@ -263,7 +263,7 @@
}
));
- if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
menuActions.add(new BubbleBarMenuView.MenuAction(
Icon.createWithResource(resources,
R.drawable.desktop_mode_ic_handle_menu_fullscreen),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index c4696d5..a8e6b59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -20,17 +20,14 @@
import android.annotation.BinderThread;
import android.annotation.NonNull;
-import android.os.RemoteException;
import android.util.Slog;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.shared.TransactionPool;
-import com.android.wm.shell.transition.LegacyTransitions;
import java.util.ArrayList;
@@ -87,25 +84,6 @@
}
/**
- * Queues a legacy transition to be sent serially to WM
- */
- public void queue(LegacyTransitions.ILegacyTransition transition,
- @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
- if (wct.isEmpty()) {
- if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
- return;
- }
- SyncCallback cb = new SyncCallback(transition, type, wct);
- synchronized (mQueue) {
- if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
- mQueue.add(cb);
- if (mQueue.size() == 1) {
- cb.send();
- }
- }
- }
-
- /**
* Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
* Otherwise just returns without queueing.
* @return {@code true} if queued, {@code false} if not.
@@ -168,17 +146,9 @@
private class SyncCallback extends WindowContainerTransactionCallback {
int mId = -1;
final WindowContainerTransaction mWCT;
- final LegacyTransitions.LegacyTransition mLegacyTransition;
SyncCallback(WindowContainerTransaction wct) {
mWCT = wct;
- mLegacyTransition = null;
- }
-
- SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
- @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
- mWCT = wct;
- mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
}
// Must be sychronized on mQueue
@@ -194,12 +164,7 @@
}
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
try {
- if (mLegacyTransition != null) {
- mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
- mLegacyTransition.getAdapter(), this, mWCT);
- } else {
- mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
- }
+ mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
} catch (RuntimeException e) {
Slog.e(TAG, "Send failed", e);
// Finish current sync callback immediately.
@@ -228,18 +193,10 @@
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
mQueue.remove(this);
onTransactionReceived(t);
- if (mLegacyTransition != null) {
- try {
- mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
- }
- } else {
- ProtoLog.v(WM_SHELL,
- "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
- t.apply();
- t.close();
- }
+ ProtoLog.v(WM_SHELL,
+ "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
+ t.apply();
+ t.close();
if (!mQueue.isEmpty()) {
mQueue.get(0).send();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 318cdee..f62fd819 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -95,6 +95,7 @@
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.desktopmode.common.DefaultHomePackageSupplier;
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
@@ -260,8 +261,14 @@
@WMSingleton
@Provides
- static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(Context context) {
- return new DesktopModeCompatPolicy(context);
+ static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(
+ Context context,
+ ShellInit shellInit,
+ @ShellMainThread Handler mainHandler) {
+ final DesktopModeCompatPolicy policy = new DesktopModeCompatPolicy(context);
+ policy.setDefaultHomePackageSupplier(new DefaultHomePackageSupplier(
+ context, shellInit, mainHandler));
+ return policy;
}
@WMSingleton
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 3dbc151..67a4d6c 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
@@ -825,7 +825,7 @@
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionObserver.get(),
- desktopPipTransitionObserver.get(),
+ desktopPipTransitionObserver,
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -1241,7 +1241,7 @@
transitions,
shellTaskOrganizer,
desktopMixedTransitionHandler.get(),
- desktopPipTransitionObserver.get(),
+ desktopPipTransitionObserver,
backAnimationController.get(),
desktopWallpaperActivityTokenProvider,
shellInit)));
@@ -1266,7 +1266,7 @@
static Optional<DesktopPipTransitionObserver> provideDesktopPipTransitionObserver(
Context context
) {
- if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)
+ if (DesktopModeStatus.canEnterDesktopMode(context)
&& DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
return Optional.of(
new DesktopPipTransitionObserver());
@@ -1492,6 +1492,7 @@
ShellTaskOrganizer shellTaskOrganizer,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
InputManager inputManager,
+ DisplayController displayController,
@ShellMainThread Handler mainHandler
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
@@ -1506,6 +1507,7 @@
shellTaskOrganizer,
desktopWallpaperActivityTokenProvider,
inputManager,
+ displayController,
mainHandler));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index 904d862..489e4f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -35,9 +35,11 @@
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
/** Controls the display windowing mode in desktop mode */
@@ -49,6 +51,7 @@
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
private val inputManager: InputManager,
+ private val displayController: DisplayController,
@ShellMainThread private val mainHandler: Handler,
) {
@@ -111,37 +114,70 @@
transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
}
- @VisibleForTesting
- fun getTargetWindowingModeForDefaultDisplay(): Int {
- if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
- return WINDOWING_MODE_FREEFORM
- }
- if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- if (isInClamshellMode()) {
- return WINDOWING_MODE_FREEFORM
+ // Do not directly use this method to check the state of desktop-first mode. Check the display
+ // windowing mode instead.
+ private fun canDesktopFirstModeBeEnabledOnDefaultDisplay(): Boolean {
+ if (isDefaultDisplayDesktopEligible()) {
+ if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
+ return true
}
- return WINDOWING_MODE_FULLSCREEN
+ if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+ if (isInClamshellMode()) {
+ return true
+ }
+ }
}
-
- // If form factor-based desktop first switch is disabled, use the default display windowing
- // mode here to keep the freeform mode for some form factors (e.g., FEATURE_PC).
- return windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ return false
}
- // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
- private fun isExtendedDisplayEnabled() =
- 0 !=
+ @VisibleForTesting
+ fun getTargetWindowingModeForDefaultDisplay(): Int {
+ if (canDesktopFirstModeBeEnabledOnDefaultDisplay()) {
+ return WINDOWING_MODE_FREEFORM
+ }
+
+ return if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+ WINDOWING_MODE_FULLSCREEN
+ } else {
+ // If form factor-based desktop first switch is disabled, use the default display
+ // windowing mode here to keep the freeform mode for some form factors (e.g.,
+ // FEATURE_PC).
+ windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ }
+ }
+
+ private fun isExtendedDisplayEnabled(): Boolean {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) {
+ return rootTaskDisplayAreaOrganizer
+ .getDisplayIds()
+ .filter { it != DEFAULT_DISPLAY }
+ .any { displayId ->
+ displayController.getDisplay(displayId)?.let { display ->
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ } ?: false
+ }
+ }
+
+ return 0 !=
Settings.Global.getInt(
context.contentResolver,
DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
0,
)
+ }
private fun hasExternalDisplay() =
rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
+ private fun isDefaultDisplayDesktopEligible(): Boolean {
+ val display = requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
+ "Display object of DEFAULT_DISPLAY must be non-null."
+ }
+ return DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ }
+
private fun logV(msg: String, vararg arguments: Any?) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 0c2ee46..6cb26b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -332,7 +332,7 @@
val affectedDisplays = mutableSetOf<Int>()
desktopData
.desksSequence()
- .filter { desk -> desk.displayId != excludedDeskId }
+ .filter { desk -> desk.deskId != excludedDeskId }
.forEach { desk ->
val removed = removeActiveTaskFromDesk(desk.deskId, taskId, notifyListeners = false)
if (removed) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 1f0774c..50f5beb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -32,8 +32,6 @@
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
import android.content.Intent
-import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -45,6 +43,7 @@
import android.os.SystemProperties
import android.os.UserHandle
import android.util.Slog
+import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.MotionEvent
@@ -215,7 +214,7 @@
private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
private val desksOrganizer: DesksOrganizer,
private val desksTransitionObserver: DesksTransitionObserver,
- private val desktopPipTransitionObserver: DesktopPipTransitionObserver,
+ private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val userProfileContexts: UserProfileContexts,
private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
private val dragToDisplayTransitionHandler: DragToDisplayTransitionHandler,
@@ -466,6 +465,10 @@
/** Creates a new desk in the given display. */
fun createDesk(displayId: Int) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ logW("createDesk attempt with invalid displayId", displayId)
+ return
+ }
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desksOrganizer.createDesk(displayId) { deskId ->
taskRepository.addDesk(displayId = displayId, deskId = deskId)
@@ -654,6 +657,7 @@
taskInfo: RunningTaskInfo,
dragToDesktopValueAnimator: MoveToDesktopAnimator,
taskSurface: SurfaceControl,
+ dragInterruptedCallback: Runnable,
) {
logV("startDragToDesktop taskId=%d", taskInfo.taskId)
val jankConfigBuilder =
@@ -669,6 +673,7 @@
taskInfo,
dragToDesktopValueAnimator,
visualIndicator,
+ dragInterruptedCallback,
)
}
@@ -811,6 +816,7 @@
}
val isMinimizingToPip =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
+ desktopPipTransitionObserver.isPresent &&
(taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)
// If task is going to PiP, start a PiP transition instead of a minimize transition
@@ -827,7 +833,7 @@
val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
wct.merge(requestRes.second, true)
- desktopPipTransitionObserver.addPendingPipTransition(
+ desktopPipTransitionObserver.get().addPendingPipTransition(
DesktopPipTransitionObserver.PendingPipTransition(
token = freeformTaskTransitionStarter.startPipTransition(wct),
taskId = taskInfo.taskId,
@@ -1262,8 +1268,7 @@
wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
}
- // TODO: b/391485148 - pass in the moving-to-desk |task| here to apply task-limit policy.
- val activationRunnable = addDeskActivationChanges(destinationDeskId, wct)
+ val activationRunnable = addDeskActivationChanges(destinationDeskId, wct, task)
if (Flags.enableDisplayFocusInShellTransitions()) {
// Bring the destination display to top with includingParents=true, so that the
@@ -2981,6 +2986,11 @@
removeDesk(displayId = displayId, deskId = deskId)
}
+ /** Removes all the available desks on all displays. */
+ fun removeAllDesks() {
+ taskRepository.getAllDeskIds().forEach { deskId -> removeDesk(deskId) }
+ }
+
private fun removeDesk(displayId: Int, deskId: Int) {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
logV("removeDesk deskId=%d from displayId=%d", deskId, displayId)
@@ -3747,6 +3757,18 @@
}
}
+ override fun removeDesk(deskId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "removeDesk") { c ->
+ c.removeDesk(deskId)
+ }
+ }
+
+ override fun removeAllDesks() {
+ executeRemoteCallWithTaskPermission(controller, "removeAllDesks") { c ->
+ c.removeAllDesks()
+ }
+ }
+
override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
executeRemoteCallWithTaskPermission(controller, "activateDesk") { c ->
c.activateDesk(deskId, remoteTransition)
@@ -3810,8 +3832,8 @@
}
}
- override fun removeDesktop(displayId: Int) {
- executeRemoteCallWithTaskPermission(controller, "removeDesktop") { c ->
+ override fun removeDefaultDeskInDisplay(displayId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "removeDefaultDeskInDisplay") { c ->
c.removeDefaultDeskInDisplay(displayId)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index c670ac3..df4d18f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -37,9 +37,12 @@
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.TransitionUtil.isClosingMode
+import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
+import java.util.Optional
/**
* A [Transitions.TransitionObserver] that observes shell transitions and updates the
@@ -52,7 +55,7 @@
private val transitions: Transitions,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
- private val desktopPipTransitionObserver: DesktopPipTransitionObserver,
+ private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val backAnimationController: BackAnimationController,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
shellInit: ShellInit,
@@ -94,7 +97,7 @@
removeTaskIfNeeded(info)
}
removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
- desktopPipTransitionObserver.onTransitionReady(transition, info)
+ desktopPipTransitionObserver.ifPresent { it.onTransitionReady(transition, info) }
}
private fun removeTaskIfNeeded(info: TransitionInfo) {
@@ -303,18 +306,29 @@
}
private fun updateTopTransparentFullscreenTaskId(info: TransitionInfo) {
- info.changes.forEach { change ->
- change.taskInfo?.let { task ->
- val desktopRepository = desktopUserRepositories.getProfile(task.userId)
- val displayId = task.displayId
- // Clear `topTransparentFullscreenTask` information from repository if task
- // is closed or sent to back.
- if (
- TransitionUtil.isClosingMode(change.mode) &&
- task.taskId ==
- desktopRepository.getTopTransparentFullscreenTaskId(displayId)
- ) {
- desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+ run forEachLoop@{
+ info.changes.forEach { change ->
+ change.taskInfo?.let { task ->
+ val desktopRepository = desktopUserRepositories.getProfile(task.userId)
+ val displayId = task.displayId
+ val transparentTaskId =
+ desktopRepository.getTopTransparentFullscreenTaskId(displayId)
+ if (transparentTaskId == null) return@forEachLoop
+ val changeMode = change.mode
+ val taskId = task.taskId
+ val isTopTransparentFullscreenTaskClosing =
+ taskId == transparentTaskId && isClosingMode(changeMode)
+ val isNonTopTransparentFullscreenTaskOpening =
+ taskId != transparentTaskId && isOpeningMode(changeMode)
+ // Clear `topTransparentFullscreenTask` information from repository if task
+ // is closed, sent to back or if a different task is opened, brought to front.
+ if (
+ isTopTransparentFullscreenTaskClosing ||
+ isNonTopTransparentFullscreenTaskOpening
+ ) {
+ desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+ return@forEachLoop
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 24b2e48..c6f74728 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -2,6 +2,7 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
import android.animation.RectEvaluator
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
@@ -23,6 +24,7 @@
import android.os.SystemClock
import android.os.SystemProperties
import android.os.UserHandle
+import android.view.Choreographer
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_CLOSE
@@ -48,6 +50,7 @@
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
@@ -122,6 +125,7 @@
taskInfo: RunningTaskInfo,
dragToDesktopAnimator: MoveToDesktopAnimator,
visualIndicator: DesktopModeVisualIndicator?,
+ dragCancelCallback: Runnable,
) {
if (inProgress) {
logV("Drag to desktop transition already in progress.")
@@ -168,6 +172,7 @@
startTransitionToken = startTransitionToken,
otherSplitTask = otherTask,
visualIndicator = visualIndicator,
+ dragCancelCallback = dragCancelCallback,
)
} else {
TransitionState.FromFullscreen(
@@ -175,6 +180,7 @@
dragAnimator = dragToDesktopAnimator,
startTransitionToken = startTransitionToken,
visualIndicator = visualIndicator,
+ dragCancelCallback = dragCancelCallback,
)
}
}
@@ -203,8 +209,9 @@
}
if (state.startInterrupted) {
logV("finishDragToDesktop: start was interrupted, returning")
- // We should only have interrupted the start transition after receiving a cancel/end
- // request, let that existing request play out and just return here.
+ // If start was interrupted we've either already requested a cancel/end transition - so
+ // we should let that request play out, or we're cancelling the drag-to-desktop
+ // transition altogether, so just return here.
return null
}
state.endTransitionToken =
@@ -221,6 +228,7 @@
*/
fun cancelDragToDesktopTransition(cancelState: CancelState) {
if (!inProgress) {
+ logV("cancelDragToDesktop: not in progress, returning")
// Don't attempt to cancel a drag to desktop transition since there is no transition in
// progress which means that the drag to desktop transition was never successfully
// started.
@@ -228,14 +236,17 @@
}
val state = requireTransitionState()
if (state.startAborted) {
+ logV("cancelDragToDesktop: start was aborted, clearing state")
// Don't attempt to cancel the drag-to-desktop since the start transition didn't
// succeed as expected. Just reset the state as if nothing happened.
clearState()
return
}
if (state.startInterrupted) {
- // We should only have interrupted the start transition after receiving a cancel/end
- // request, let that existing request play out and just return here.
+ logV("cancelDragToDesktop: start was interrupted, returning")
+ // If start was interrupted we've either already requested a cancel/end transition - so
+ // we should let that request play out, or we're cancelling the drag-to-desktop
+ // transition altogether, so just return here.
return
}
state.cancelState = cancelState
@@ -706,11 +717,7 @@
// end-transition, or if the end-transition is running on its own, then just wait until that
// finishes instead. If we've merged the cancel-transition we've finished the
// start-transition and won't reach this code.
- if (
- mergeTarget == state.startTransitionToken &&
- isCancelOrEndTransitionRequested(state) &&
- !state.mergedEndTransition
- ) {
+ if (mergeTarget == state.startTransitionToken && !state.mergedEndTransition) {
interruptStartTransition(state)
}
}
@@ -722,9 +729,23 @@
if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue) {
return
}
- logV("interruptStartTransition")
- state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
- state.dragAnimator.cancelAnimator()
+ if (isCancelOrEndTransitionRequested(state)) {
+ logV("interruptStartTransition, bookend requested -> finish start transition")
+ // Finish the start-drag transition, we will finish the overall transition properly when
+ // receiving #startAnimation for Cancel/End.
+ state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+ state.dragAnimator.cancelAnimator()
+ } else {
+ logV("interruptStartTransition, bookend not requested -> animate to Home")
+ // Animate to Home, and then finish the start-drag transition. Since there is no other
+ // (end/cancel) transition requested that will be the end of the overall transition.
+ state.dragAnimator.cancelAnimator()
+ state.dragCancelCallback?.run()
+ createInterruptToHomeAnimator(transactionSupplier.get(), state) {
+ state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+ clearState()
+ }
+ }
state.activeCancelAnimation?.removeAllListeners()
state.activeCancelAnimation?.cancel()
state.activeCancelAnimation = null
@@ -738,6 +759,46 @@
.onActionCancel(LatencyTracker.ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG)
}
+ private fun createInterruptToHomeAnimator(
+ transaction: Transaction,
+ state: TransitionState,
+ endCallback: Runnable,
+ ) {
+ val homeLeash = state.homeChange?.leash ?: error("Expected home leash to be non-null")
+ val draggedTaskLeash =
+ state.draggedTaskChange?.leash ?: error("Expected dragged leash to be non-null")
+ val homeAnimator = createInterruptAlphaAnimator(transaction, homeLeash, toShow = true)
+ val draggedTaskAnimator =
+ createInterruptAlphaAnimator(transaction, draggedTaskLeash, toShow = false)
+ val animatorSet = AnimatorSet()
+ animatorSet.playTogether(homeAnimator, draggedTaskAnimator)
+ animatorSet.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ endCallback.run()
+ }
+ }
+ )
+ animatorSet.start()
+ }
+
+ private fun createInterruptAlphaAnimator(
+ transaction: Transaction,
+ leash: SurfaceControl,
+ toShow: Boolean,
+ ) =
+ ValueAnimator.ofFloat(if (toShow) 0f else 1f, if (toShow) 1f else 0f).apply {
+ transaction.show(leash)
+ duration = DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
+ interpolator = Interpolators.LINEAR
+ addUpdateListener { animation ->
+ transaction
+ .setAlpha(leash, animation.animatedValue as Float)
+ .setFrameTimeline(Choreographer.getInstance().vsyncId)
+ .apply()
+ }
+ }
+
protected open fun setupEndDragToDesktop(
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
@@ -1060,6 +1121,7 @@
abstract var endTransitionToken: IBinder?
abstract var mergedEndTransition: Boolean
abstract var activeCancelAnimation: Animator?
+ abstract var dragCancelCallback: Runnable?
data class FromFullscreen(
override val draggedTaskId: Int,
@@ -1079,6 +1141,7 @@
override var endTransitionToken: IBinder? = null,
override var mergedEndTransition: Boolean = false,
override var activeCancelAnimation: Animator? = null,
+ override var dragCancelCallback: Runnable? = null,
var otherRootChanges: MutableList<Change> = mutableListOf(),
) : TransitionState()
@@ -1100,6 +1163,7 @@
override var endTransitionToken: IBinder? = null,
override var mergedEndTransition: Boolean = false,
override var activeCancelAnimation: Animator? = null,
+ override var dragCancelCallback: Runnable? = null,
var splitRootChange: Change? = null,
var otherSplitTask: Int,
) : TransitionState()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 44f7e16..5f7fbd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -35,6 +35,12 @@
/** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
+ /** Removes the desk with the given `deskId`. */
+ oneway void removeDesk(int deskId);
+
+ /** Removes all the available desks on all displays. */
+ oneway void removeAllDesks();
+
/** Show apps on the desktop on the given display */
void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
@@ -64,8 +70,11 @@
in @nullable RemoteTransition remoteTransition,
in @nullable IMoveToDesktopCallback callback);
- /** Remove desktop on the given display */
- oneway void removeDesktop(int displayId);
+ /**
+ * Removes the default desktop on the given display.
+ * @deprecated with multi-desks, we should use `removeDesk()`.
+ */
+ oneway void removeDefaultDeskInDisplay(int displayId);
/** Move a task with given `taskId` to external display */
void moveToExternalDisplay(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
new file mode 100644
index 0000000..8ce624e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Handler
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import java.util.function.Supplier
+
+/**
+ * This supplies the package name of default home in an efficient way. The query to package manager
+ * only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ */
+class DefaultHomePackageSupplier(
+ private val context: Context,
+ shellInit: ShellInit,
+ @ShellMainThread private val mainHandler: Handler,
+) : BroadcastReceiver(), Supplier<String?> {
+
+ private var defaultHomePackage: String? = null
+
+ init {
+ shellInit.addInitCallback({ onInit() }, this)
+ }
+
+ private fun onInit() {
+ context.registerReceiver(
+ this,
+ IntentFilter(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED),
+ null /* broadcastPermission */,
+ mainHandler,
+ )
+ }
+
+ private fun updateDefaultHomePackage(): String? {
+ defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+ return defaultHomePackage
+ }
+
+ override fun onReceive(contxt: Context?, intent: Intent?) {
+ updateDefaultHomePackage()
+ }
+
+ override fun get(): String? {
+ return defaultHomePackage ?: updateDefaultHomePackage()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index f89ba0a..0bf2ea6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -174,7 +174,7 @@
SurfaceControl.Transaction finishT) {
mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
private void onToFrontTransitionReady(
@@ -184,7 +184,7 @@
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToFront(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
private void onToBackTransitionReady(
@@ -194,7 +194,7 @@
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
+ change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 75c09829..a7cba76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -123,6 +123,7 @@
import android.view.WindowManager;
import android.widget.Toast;
import android.window.DesktopExperienceFlags;
+import android.window.DesktopModeFlags;
import android.window.DisplayAreaInfo;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -675,7 +676,8 @@
if (!enteredSplitSelect) {
return null;
}
- if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()
+ && !DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
mTaskOrganizer.applyTransaction(wct);
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 938885c..23dfb41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DesktopModeFlags.ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
@@ -50,6 +51,7 @@
private @NonNull final Context mContext;
private @NonNull final ShellExecutor mMainExecutor;
+ private IBinder mPendingStartDragTransition;
private Boolean mPendingHomeVisibilityUpdate;
public HomeTransitionObserver(@NonNull Context context,
@@ -63,31 +65,42 @@
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
- if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
- handleTransitionReadyWithBubbleAnything(info);
- } else {
- handleTransitionReady(info);
+ Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info);
+
+ if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
+ // Do not apply at the start of desktop drag as that updates launcher UI visibility.
+ // Store the value and apply with a next transition or when cancelling the
+ // desktop-drag transition.
+ storePendingHomeVisibilityUpdate(transition, homeVisibilityUpdate);
+ return;
+ }
+
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ && info.getType() == TRANSIT_CONVERT_TO_BUBBLE
+ && homeVisibilityUpdate == null) {
+ // We are converting to bubble and we did not get a change to home visibility in this
+ // transition. Apply the value from start of drag.
+ homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
+ }
+
+ if (homeVisibilityUpdate != null) {
+ mPendingHomeVisibilityUpdate = null;
+ mPendingStartDragTransition = null;
+ notifyHomeVisibilityChanged(homeVisibilityUpdate);
}
}
- private void handleTransitionReady(@NonNull TransitionInfo info) {
- for (TransitionInfo.Change change : info.getChanges()) {
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo == null
- || info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
- || taskInfo.displayId != DEFAULT_DISPLAY
- || taskInfo.taskId == -1
- || !taskInfo.isRunning) {
- continue;
- }
- Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info, change, taskInfo);
- if (homeVisibilityUpdate != null) {
- notifyHomeVisibilityChanged(homeVisibilityUpdate);
- }
+ private void storePendingHomeVisibilityUpdate(
+ IBinder transition, Boolean homeVisibilityUpdate) {
+ if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ && !ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+ return;
}
+ mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
+ mPendingStartDragTransition = transition;
}
- private void handleTransitionReadyWithBubbleAnything(@NonNull TransitionInfo info) {
+ private Boolean getHomeVisibilityUpdate(TransitionInfo info) {
Boolean homeVisibilityUpdate = null;
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -97,29 +110,12 @@
|| !taskInfo.isRunning) {
continue;
}
-
Boolean update = getHomeVisibilityUpdate(info, change, taskInfo);
if (update != null) {
homeVisibilityUpdate = update;
}
}
-
- if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
- // Do not apply at the start of desktop drag as that updates launcher UI visibility.
- // Store the value and apply with a next transition if needed.
- mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
- return;
- }
-
- if (info.getType() == TRANSIT_CONVERT_TO_BUBBLE && homeVisibilityUpdate == null) {
- // We are converting to bubble and we did not get a change to home visibility in this
- // transition. Apply the value from start of drag.
- homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
- }
- if (homeVisibilityUpdate != null) {
- mPendingHomeVisibilityUpdate = null;
- notifyHomeVisibilityChanged(homeVisibilityUpdate);
- }
+ return homeVisibilityUpdate;
}
private Boolean getHomeVisibilityUpdate(TransitionInfo info,
@@ -146,7 +142,24 @@
@Override
public void onTransitionFinished(@NonNull IBinder transition,
- boolean aborted) {}
+ boolean aborted) {
+ if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+ return;
+ }
+ // Handle the case where the DragToDesktop START transition is interrupted and we never
+ // receive a CANCEL/END transition.
+ if (mPendingStartDragTransition == null
+ || mPendingStartDragTransition != transition) {
+ return;
+ }
+ mPendingStartDragTransition = null;
+ if (aborted) return;
+
+ if (mPendingHomeVisibilityUpdate != null) {
+ notifyHomeVisibilityChanged(mPendingHomeVisibilityUpdate);
+ mPendingHomeVisibilityUpdate = null;
+ }
+ }
/**
* Sets the home transition listener that receives any transitions resulting in a change of
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
deleted file mode 100644
index 978b8da..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
+++ /dev/null
@@ -1,139 +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.wm.shell.transition;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerTransactionCallback;
-
-import com.android.internal.protolog.ProtoLog;
-
-/**
- * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
- * synctransaction tools.
- */
-public class LegacyTransitions {
-
- /**
- * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
- * into one callback.
- */
- public interface ILegacyTransition {
- /**
- * Called when both the associated sync transaction finishes and the remote animation is
- * ready.
- */
- void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
- }
-
- /**
- * Makes sure that a remote animation and corresponding sync callback are called together
- * such that the sync callback is called first. This assumes that both the callback receiver
- * and the remoteanimation are in the same process so that order is preserved on both ends.
- */
- public static class LegacyTransition {
- private final ILegacyTransition mLegacyTransition;
- private int mSyncId = -1;
- private SurfaceControl.Transaction mTransaction;
- private int mTransit;
- private RemoteAnimationTarget[] mApps;
- private RemoteAnimationTarget[] mWallpapers;
- private RemoteAnimationTarget[] mNonApps;
- private IRemoteAnimationFinishedCallback mFinishCallback = null;
- private boolean mCancelled = false;
- private final SyncCallback mSyncCallback = new SyncCallback();
- private final RemoteAnimationAdapter mAdapter =
- new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
-
- public LegacyTransition(@WindowManager.TransitionType int type,
- @NonNull ILegacyTransition legacyTransition) {
- mLegacyTransition = legacyTransition;
- mTransit = type;
- }
-
- public @WindowManager.TransitionType int getType() {
- return mTransit;
- }
-
- public IWindowContainerTransactionCallback getSyncCallback() {
- return mSyncCallback;
- }
-
- public RemoteAnimationAdapter getAdapter() {
- return mAdapter;
- }
-
- private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
- @Override
- public void onTransactionReady(int id, SurfaceControl.Transaction t)
- throws RemoteException {
- ProtoLog.v(WM_SHELL_TRANSITIONS,
- "LegacyTransitions.onTransactionReady(): syncId=%d", id);
- mSyncId = id;
- mTransaction = t;
- checkApply(true /* log */);
- }
- }
-
- private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
- mTransit = transit;
- mApps = apps;
- mWallpapers = wallpapers;
- mNonApps = nonApps;
- mFinishCallback = finishedCallback;
- checkApply(false /* log */);
- }
-
- @Override
- public void onAnimationCancelled() throws RemoteException {
- mCancelled = true;
- mApps = mWallpapers = mNonApps = null;
- checkApply(false /* log */);
- }
- }
-
-
- private void checkApply(boolean log) throws RemoteException {
- if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) {
- if (log) {
- ProtoLog.v(WM_SHELL_TRANSITIONS, "\tSkipping hasFinishedCb=%b canceled=%b",
- mFinishCallback != null, mCancelled);
- }
- return;
- }
- if (log) {
- ProtoLog.v(WM_SHELL_TRANSITIONS, "\tapply");
- }
- mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
- mNonApps, mFinishCallback, mTransaction);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 7871179..42321e56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -49,6 +49,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.window.DisplayAreaInfo;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -233,7 +234,8 @@
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 2b2cdf8..4511fbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -31,6 +31,7 @@
import android.view.KeyEvent;
import android.view.SurfaceControl;
import android.view.View;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -159,7 +160,8 @@
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index a1d2774..f2ff396 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -27,6 +27,7 @@
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
@@ -79,6 +80,7 @@
import android.view.ViewRootImpl;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -150,18 +152,19 @@
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
-import org.jetbrains.annotations.NotNull;
-
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.MainCoroutineDispatcher;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -206,6 +209,7 @@
private final AppToWebEducationController mAppToWebEducationController;
private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+ private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -331,6 +335,7 @@
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
new AppHeaderViewHolder.Factory(),
+ new AppHandleViewHolder.Factory(),
rootTaskDisplayAreaOrganizer,
new SparseArray<>(),
interactionJankMonitor,
@@ -380,6 +385,7 @@
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
InteractionJankMonitor interactionJankMonitor,
@@ -422,6 +428,7 @@
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+ mAppHandleViewHolderFactory = appHandleViewHolderFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
mInputManager = mContext.getSystemService(InputManager.class);
@@ -486,7 +493,8 @@
new DesktopModeOnTaskResizeAnimationListener());
mDesktopTasksController.setOnTaskRepositionAnimationListener(
new DesktopModeOnTaskRepositionAnimationListener());
- if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()
+ || DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
mRecentsTransitionHandler.addTransitionStateListener(
new DesktopModeRecentsTransitionStateListener());
}
@@ -587,7 +595,8 @@
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
if (decoration != null) {
@@ -601,8 +610,8 @@
} else {
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion,
+ /*isMovingToBack= */ changeMode == TRANSIT_TO_BACK);
}
}
@@ -617,7 +626,7 @@
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mExclusionRegion, /* isMovingToBack= */ false);
}
@Override
@@ -817,9 +826,6 @@
return;
}
decoration.closeHandleMenu();
- // When the app enters split-select, the handle will no longer be visible, meaning
- // we shouldn't receive input for it any longer.
- decoration.disposeStatusBarInputLayer();
mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN);
@@ -979,6 +985,7 @@
private boolean mIsCustomHeaderGesture;
private boolean mIsResizeGesture;
private boolean mIsDragging;
+ private boolean mDragInterrupted;
private boolean mLongClickDisabled;
private int mDragPointerId = -1;
private MotionEvent mMotionEvent;
@@ -1216,7 +1223,12 @@
View v, MotionEvent e) {
final int id = v.getId();
if (id == R.id.caption_handle) {
- handleCaptionThroughStatusBar(e, decoration);
+ handleCaptionThroughStatusBar(e, decoration,
+ /* interruptDragCallback= */
+ () -> {
+ mDragInterrupted = true;
+ setIsDragging(decoration, /* isDragging= */ false);
+ });
final boolean wasDragging = mIsDragging;
updateDragStatus(decoration, e);
final boolean upOrCancel = e.getActionMasked() == ACTION_UP
@@ -1333,11 +1345,14 @@
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
+ mDragInterrupted = false;
setIsDragging(decor, false /* isDragging */);
break;
}
case MotionEvent.ACTION_MOVE: {
- setIsDragging(decor, true /* isDragging */);
+ if (!mDragInterrupted) {
+ setIsDragging(decor, true /* isDragging */);
+ }
break;
}
}
@@ -1458,7 +1473,8 @@
if (!mInImmersiveMode && (relevantDecor == null
|| relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
|| mTransitionDragActive)) {
- handleCaptionThroughStatusBar(ev, relevantDecor);
+ handleCaptionThroughStatusBar(ev, relevantDecor,
+ /* interruptDragCallback= */ () -> {});
}
}
handleEventOutsideCaption(ev, relevantDecor);
@@ -1498,7 +1514,7 @@
* Turn on desktop mode if handle is dragged below status bar.
*/
private void handleCaptionThroughStatusBar(MotionEvent ev,
- DesktopModeWindowDecoration relevantDecor) {
+ DesktopModeWindowDecoration relevantDecor, Runnable interruptDragCallback) {
if (relevantDecor == null) {
if (ev.getActionMasked() == ACTION_UP) {
mMoveToDesktopAnimator = null;
@@ -1599,7 +1615,16 @@
mContext, mDragToDesktopAnimationStartBounds,
relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
- mMoveToDesktopAnimator, relevantDecor.mTaskSurface);
+ mMoveToDesktopAnimator, relevantDecor.mTaskSurface,
+ /* dragInterruptedCallback= */ () -> {
+ // Don't call into DesktopTasksController to cancel the
+ // transition here - the transition handler already handles
+ // that (including removing the visual indicator).
+ mTransitionDragActive = false;
+ mMoveToDesktopAnimator = null;
+ relevantDecor.handleDragInterrupted();
+ interruptDragCallback.run();
+ });
}
}
if (mMoveToDesktopAnimator != null) {
@@ -1761,6 +1786,7 @@
mMainChoreographer,
mSyncQueue,
mAppHeaderViewHolderFactory,
+ mAppHandleViewHolderFactory,
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
mAssistContentRequester,
@@ -1852,12 +1878,13 @@
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion);
+ mExclusionRegion, /* isMovingToBack= */ false);
if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
incrementEventReceiverTasks(taskInfo.displayId);
}
}
+ @Nullable
private RunningTaskInfo getOtherSplitTask(int taskId) {
@SplitPosition int remainingTaskPosition = mSplitScreenController
.getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index bd21f8d..bcf9396 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -190,6 +190,7 @@
private ExclusionRegionListener mExclusionRegionListener;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+ private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final MaximizeMenuFactory mMaximizeMenuFactory;
private final HandleMenuFactory mHandleMenuFactory;
@@ -212,6 +213,7 @@
private boolean mIsDragging = false;
private Runnable mLoadAppInfoRunnable;
private Runnable mSetAppInfoRunnable;
+ private boolean mIsMovingToBack;
public DesktopModeWindowDecoration(
Context context,
@@ -231,6 +233,7 @@
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -242,10 +245,10 @@
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
- appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
- assistContentRequester, SurfaceControl.Builder::new,
- SurfaceControl.Transaction::new, WindowContainerTransaction::new,
- SurfaceControl::new, new WindowManagerWrapper(
+ appHeaderViewHolderFactory, appHandleViewHolderFactory,
+ rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
context.getSystemService(WindowManager.class)),
new SurfaceControlViewHostFactory() {},
windowDecorViewHostSupplier,
@@ -273,6 +276,7 @@
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -302,6 +306,7 @@
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+ mAppHandleViewHolderFactory = appHandleViewHolderFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
mAssistContentRequester = assistContentRequester;
@@ -462,7 +467,7 @@
// causes flickering. See b/270202228.
final boolean applyTransactionOnDraw = taskInfo.isFreeform();
relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus, displayExclusionRegion);
+ hasGlobalFocus, displayExclusionRegion, mIsMovingToBack);
if (!applyTransactionOnDraw) {
t.apply();
}
@@ -489,7 +494,8 @@
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
+ boolean hasGlobalFocus, @NonNull Region displayExclusionRegion,
+ boolean isMovingToBack) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()) {
@@ -512,12 +518,17 @@
final boolean inFullImmersive = mDesktopUserRepositories.getProfile(taskInfo.userId)
.isTaskInFullImmersiveState(taskInfo.taskId);
+ mIsMovingToBack = isMovingToBack;
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, mSplitScreenController,
applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
mIsDragging, mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
- displayExclusionRegion, mIsRecentsTransitionRunning,
- mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo));
+ displayExclusionRegion,
+ /* shouldIgnoreCornerRadius= */ mIsRecentsTransitionRunning
+ && DesktopModeFlags
+ .ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue(),
+ mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo),
+ mIsRecentsTransitionRunning, mIsMovingToBack);
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -542,7 +553,9 @@
return;
}
- if (oldRootView != mResult.mRootView) {
+ if (DesktopModeFlags.SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX.isTrue()
+ ? (oldRootView != mResult.mRootView && taskInfo.isVisibleRequested)
+ : oldRootView != mResult.mRootView) {
disposeStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
// Load these only when first creating the view.
@@ -558,29 +571,15 @@
});
}
- final Point position = new Point();
- if (isAppHandle(mWindowDecorViewHolder)) {
- position.set(determineHandlePosition());
- }
if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
notifyCaptionStateChanged();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
if (isAppHandle(mWindowDecorViewHolder)) {
- mWindowDecorViewHolder.bindData(new AppHandleViewHolder.HandleData(
- mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
- isCaptionVisible()
- ));
+ updateAppHandleViewHolder();
} else {
- mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
- mTaskInfo,
- DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
- inFullImmersive,
- hasGlobalFocus,
- /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
- /* animatingTaskResizeOrReposition= */ false)
- ));
+ updateAppHeaderViewHolder(inFullImmersive, hasGlobalFocus);
}
Trace.endSection();
@@ -857,9 +856,31 @@
asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
}
+ /** Update the view holder for app handle. */
+ private void updateAppHandleViewHolder() {
+ if (!isAppHandle(mWindowDecorViewHolder)) return;
+ asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
+ mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
+ mResult.mCaptionHeight, isCaptionVisible()
+ ));
+ }
+
+ /** Update the view holder for app header. */
+ private void updateAppHeaderViewHolder(boolean inFullImmersive, boolean hasGlobalFocus) {
+ if (!isAppHeader(mWindowDecorViewHolder)) return;
+ asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
+ mTaskInfo,
+ DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
+ inFullImmersive,
+ hasGlobalFocus,
+ /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
+ /* animatingTaskResizeOrReposition= */ false)
+ ));
+ }
+
private WindowDecorationViewHolder createViewHolder() {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
- return new AppHandleViewHolder(
+ return mAppHandleViewHolderFactory.create(
mResult.mRootView,
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
@@ -886,6 +907,10 @@
return viewHolder instanceof AppHandleViewHolder;
}
+ private boolean isAppHeader(WindowDecorationViewHolder viewHolder) {
+ return viewHolder instanceof AppHeaderViewHolder;
+ }
+
@Nullable
private AppHandleViewHolder asAppHandle(WindowDecorationViewHolder viewHolder) {
if (viewHolder instanceof AppHandleViewHolder) {
@@ -918,7 +943,9 @@
boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion,
boolean shouldIgnoreCornerRadius,
- boolean shouldExcludeCaptionFromAppBounds) {
+ boolean shouldExcludeCaptionFromAppBounds,
+ boolean isRecentsTransitionRunning,
+ boolean isMovingToBack) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -935,11 +962,20 @@
// the first frame.
relayoutParams.mAsyncViewHost = isAppHandle;
- final boolean showCaption;
- if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
+ boolean showCaption;
+ // If this relayout is occurring from an observed TRANSIT_TO_BACK transition, do not
+ // show caption (this includes split select transition).
+ if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+ && isMovingToBack && !isDragging) {
+ showCaption = false;
+ } else if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
// If the task is being dragged, the caption should not be hidden so that it continues
// receiving input
showCaption = true;
+ } else if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+ && isRecentsTransitionRunning) {
+ // Caption should not be visible in recents.
+ showCaption = false;
} else if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
if (inFullImmersiveMode) {
showCaption = (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
@@ -1683,6 +1719,17 @@
}
}
+ /**
+ * Indicates that an app handle drag has been interrupted, this can happen e.g. if we receive an
+ * unknown transition during the drag-to-desktop transition.
+ */
+ void handleDragInterrupted() {
+ if (mResult.mRootView == null) return;
+ final View handle = mResult.mRootView.findViewById(R.id.caption_handle);
+ handle.setHovered(false);
+ handle.setPressed(false);
+ }
+
private boolean pointInView(View v, float x, float y) {
return v != null && v.getLeft() <= x && v.getRight() >= x
&& v.getTop() <= y && v.getBottom() >= y;
@@ -1800,9 +1847,18 @@
* <p> When a Recents transition is active we allow that transition to take ownership of the
* corner radius of its task surfaces, so each window decoration should stop updating the corner
* radius of its task surface during that time.
+ *
+ * We should not allow input to reach the input layer during a Recents transition, so
+ * update the handle view holder accordingly if transition status changes.
*/
void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
- mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+ if (mIsRecentsTransitionRunning != isRecentsTransitionRunning) {
+ mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+ if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
+ // We don't relayout decor on recents transition, so we need to call it directly.
+ relayout(mTaskInfo, mHasGlobalFocus, mRelayoutParams.mDisplayExclusionRegion);
+ }
+ }
}
/**
@@ -1867,6 +1923,7 @@
Choreographer choreographer,
SyncTransactionQueue syncQueue,
AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+ AppHandleViewHolder.Factory appHandleViewHolderFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
AssistContentRequester assistContentRequester,
@@ -1894,6 +1951,7 @@
choreographer,
syncQueue,
appHeaderViewHolderFactory,
+ appHandleViewHolderFactory,
rootTaskDisplayAreaOrganizer,
genericLinksParser,
assistContentRequester,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 7a4a834..0b86d1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -23,12 +23,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
-import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen;
@@ -127,7 +127,9 @@
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
DisplayController displayController,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ InputChannel inputChannel,
+ InputChannel sinkInputChannel) {
mContext = context;
mWindowSession = windowSession;
mBgExecutor = bgExecutor;
@@ -136,7 +138,11 @@
mHandler = handler;
mChoreographer = choreographer;
mDisplayId = displayId;
- mDecorationSurface = decorationSurface;
+ // Creates a new SurfaceControl pointing the same underlying surface with decorationSurface
+ // to ensure that mDecorationSurface will not be released while it's used on the background
+ // thread. Note that the empty name will be overridden by the next copyFrom call.
+ mDecorationSurface = surfaceControlBuilderSupplier.get().setName("").build();
+ mDecorationSurface.copyFrom(decorationSurface, "DragResizeInputListener");
mDragPositioningCallback = callback;
mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
@@ -154,9 +160,13 @@
final InputSetUpResult result = setUpInputChannels(mDisplayId, mWindowSession,
mDecorationSurface, mClientToken, mSinkClientToken,
mSurfaceControlBuilderSupplier,
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier, inputChannel, sinkInputChannel);
mainExecutor.execute(() -> {
if (mClosed) {
+ result.mInputChannel.dispose();
+ result.mSinkInputChannel.dispose();
+ mSurfaceControlTransactionSupplier.get().remove(
+ result.mInputSinkSurface).apply();
return;
}
mInputSinkSurface = result.mInputSinkSurface;
@@ -208,7 +218,7 @@
new DefaultTaskResizeInputEventReceiverFactory(), taskInfo,
handler, choreographer, displayId, decorationSurface, callback,
surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
- displayController, desktopModeEventLogger);
+ displayController, desktopModeEventLogger, new InputChannel(), new InputChannel());
}
DragResizeInputListener(
@@ -251,11 +261,11 @@
@NonNull IBinder clientToken,
@NonNull IBinder sinkClientToken,
@NonNull Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
- @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
+ @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+ @NonNull InputChannel inputChannel,
+ @NonNull InputChannel sinkInputChannel) {
Trace.beginSection("DragResizeInputListener#setUpInputChannels");
final InputTransferToken inputTransferToken = new InputTransferToken();
- final InputChannel inputChannel = new InputChannel();
- final InputChannel sinkInputChannel = new InputChannel();
try {
windowSession.grantInputChannel(
displayId,
@@ -421,6 +431,9 @@
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ // Removing this surface on the background thread to ensure that mInitInputChannels has
+ // already been finished.
+ mSurfaceControlTransactionSupplier.get().remove(mDecorationSurface).apply();
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 2d6f745..732f042 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -173,6 +173,9 @@
return new Rect(mRepositionTaskBounds);
}
+ @Override
+ public void close() {}
+
private boolean isResizing() {
return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index c544468..9cc64ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -108,19 +108,19 @@
private val isViewAboveStatusBar: Boolean
get() = (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && !taskInfo.isFreeform)
- private val pillElevation: Int = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_pill_elevation)
private val pillTopMargin: Int = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
- private val menuWidth = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_width) + pillElevation
+ R.dimen.desktop_mode_handle_menu_pill_spacing_margin
+ )
+ private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_width)
private val menuHeight = getHandleMenuHeight()
private val marginMenuTop = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_margin_top)
private val marginMenuStart = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_margin_start)
+ R.dimen.desktop_mode_handle_menu_margin_start
+ )
@VisibleForTesting
var handleMenuViewContainer: AdditionalViewContainer? = null
+
@VisibleForTesting
var handleMenuView: HandleMenuView? = null
@@ -139,7 +139,7 @@
private val shouldShowMoreActionsPill: Boolean
get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
- shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+ shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
private var loadAppInfoJob: Job? = null
@@ -243,7 +243,8 @@
val y = handleMenuPosition.y.toInt()
handleMenuViewContainer =
if ((!taskInfo.isFreeform && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue())
- || forceShowSystemBars) {
+ || forceShowSystemBars
+ ) {
AdditionalSystemViewContainer(
windowManagerWrapper = windowManagerWrapper,
taskId = taskInfo.taskId,
@@ -254,7 +255,11 @@
flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
view = handleMenuView.rootView,
- forciblyShownTypes = if (forceShowSystemBars) { systemBars() } else { 0 },
+ forciblyShownTypes = if (forceShowSystemBars) {
+ systemBars()
+ } else {
+ 0
+ },
ignoreCutouts = Flags.showAppHandleLargeScreens()
|| BubbleAnythingFlagHelper.enableBubbleToFullscreen()
)
@@ -372,7 +377,8 @@
inputPoint.y - globalMenuPosition.y
)
if (splitScreenController.getSplitPosition(taskInfo.taskId)
- == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ) {
val leftStageBounds = Rect()
splitScreenController.getStageBounds(leftStageBounds, Rect())
inputRelativeToMenu.x += leftStageBounds.width().toFloat()
@@ -398,11 +404,11 @@
* Determines handle menu height based the max size and the visibility of pills.
*/
private fun getHandleMenuHeight(): Int {
- var menuHeight = loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_height) + pillElevation
+ var menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_height)
if (!shouldShowWindowingPill) {
menuHeight -= loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_windowing_pill_height)
+ R.dimen.desktop_mode_handle_menu_windowing_pill_height
+ )
menuHeight -= pillTopMargin
}
if (!SHOULD_SHOW_SCREENSHOT_BUTTON) {
@@ -422,14 +428,16 @@
}
if (!shouldShowChangeAspectRatioButton) {
menuHeight -= loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height)
+ R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height
+ )
}
if (!shouldShowMoreActionsPill) {
menuHeight -= pillTopMargin
}
if (!shouldShowBrowserPill) {
menuHeight -= loadDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height)
+ R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height
+ )
menuHeight -= pillTopMargin
}
return menuHeight
@@ -472,48 +480,66 @@
// Insets for ripple effect of App Info Pill. and Windowing Pill. buttons
val iconButtondrawableShiftInset = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift)
+ R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift
+ )
val iconButtondrawableBaseInset = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base)
+ R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base
+ )
private val iconButtonRippleRadius = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius)
- private val iconButtonDrawableInsetsBase = DrawableInsets(t = iconButtondrawableBaseInset,
+ R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius
+ )
+ private val iconButtonDrawableInsetsBase = DrawableInsets(
+ t = iconButtondrawableBaseInset,
b = iconButtondrawableBaseInset, l = iconButtondrawableBaseInset,
- r = iconButtondrawableBaseInset)
- private val iconButtonDrawableInsetsLeft = DrawableInsets(t = iconButtondrawableBaseInset,
- b = iconButtondrawableBaseInset, l = iconButtondrawableShiftInset, r = 0)
- private val iconButtonDrawableInsetsRight = DrawableInsets(t = iconButtondrawableBaseInset,
- b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset)
+ r = iconButtondrawableBaseInset
+ )
+ private val iconButtonDrawableInsetsLeft = DrawableInsets(
+ t = iconButtondrawableBaseInset,
+ b = iconButtondrawableBaseInset, l = iconButtondrawableShiftInset, r = 0
+ )
+ private val iconButtonDrawableInsetsRight = DrawableInsets(
+ t = iconButtondrawableBaseInset,
+ b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset
+ )
// App Info Pill.
private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
- R.id.collapse_menu_button)
+ R.id.collapse_menu_button
+ )
+
@VisibleForTesting
val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
+
@VisibleForTesting
val appNameView = appInfoPill.requireViewById<MarqueedTextView>(R.id.application_name)
// Windowing Pill.
private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
private val fullscreenBtn = windowingPill.requireViewById<ImageButton>(
- R.id.fullscreen_button)
+ R.id.fullscreen_button
+ )
private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
- R.id.split_screen_button)
+ R.id.split_screen_button
+ )
private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
private val floatingBtnSpace = windowingPill.requireViewById<Space>(
- R.id.floating_button_space)
+ R.id.floating_button_space
+ )
private val desktopBtn = windowingPill.requireViewById<ImageButton>(R.id.desktop_button)
private val desktopBtnSpace = windowingPill.requireViewById<Space>(
- R.id.desktop_button_space)
+ R.id.desktop_button_space
+ )
// More Actions Pill.
private val moreActionsPill = rootView.requireViewById<View>(R.id.more_actions_pill)
private val screenshotBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
- R.id.screenshot_button)
+ R.id.screenshot_button
+ )
private val newWindowBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
- R.id.new_window_button)
+ R.id.new_window_button
+ )
private val manageWindowBtn = moreActionsPill
.requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
private val changeAspectRatioBtn = moreActionsPill
@@ -521,11 +547,14 @@
// Open in Browser/App Pill.
private val openInAppOrBrowserPill = rootView.requireViewById<View>(
- R.id.open_in_app_or_browser_pill)
+ R.id.open_in_app_or_browser_pill
+ )
private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<View>(
- R.id.open_in_app_or_browser_button)
+ R.id.open_in_app_or_browser_button
+ )
private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>(
- R.id.open_by_default_button)
+ R.id.open_by_default_button
+ )
private val decorThemeUtil = DecorThemeUtil(context)
private val animator = HandleMenuAnimator(rootView, menuWidth, captionHeight.toFloat())
@@ -734,9 +763,9 @@
desktopBtn.imageTintList = style.windowingButtonColor
val startInsets = if (context.isRtl) iconButtonDrawableInsetsRight
- else iconButtonDrawableInsetsLeft
+ else iconButtonDrawableInsetsLeft
val endInsets = if (context.isRtl) iconButtonDrawableInsetsLeft
- else iconButtonDrawableInsetsRight
+ else iconButtonDrawableInsetsRight
fullscreenBtn.apply {
background = createBackgroundDrawable(
@@ -808,9 +837,11 @@
getString(R.string.open_in_browser_text)
}
+ val buttonRoot = openInAppOrBrowserBtn.requireViewById<LinearLayout>(R.id.action_button)
val label = openInAppOrBrowserBtn.requireViewById<MarqueedTextView>(R.id.label)
val image = openInAppOrBrowserBtn.requireViewById<ImageView>(R.id.image)
openInAppOrBrowserBtn.contentDescription = btnText
+ buttonRoot.contentDescription = btnText
label.apply {
text = btnText
setTextColor(style.textColor)
@@ -841,7 +872,7 @@
*/
fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
- taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+ taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
index 4b2e473..a723a7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
@@ -23,9 +23,7 @@
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
-import android.widget.TextView
import androidx.core.content.withStyledAttributes
-import androidx.core.view.isGone
import com.android.wm.shell.R
/**
@@ -54,6 +52,7 @@
context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) {
textView.text = getString(R.styleable.HandleMenuActionButton_android_text)
+ rootElement.contentDescription = getString(R.styleable.HandleMenuActionButton_android_text)
textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0))
iconView.setImageResource(getResourceId(
R.styleable.HandleMenuActionButton_android_src, 0))
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index 1b0e0f70..eb324f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -294,6 +294,10 @@
return Rect(repositionTaskBounds)
}
+ override fun close() {
+ displayController.removeDisplayWindowListener(this)
+ }
+
private fun resetVeilIfVisible() {
if (isResizingOrAnimatingResize) {
desktopWindowDecoration.hideResizeVeil()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index 63b288d..06e5380 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -41,4 +41,10 @@
*/
void removeDragEventListener(
DragPositioningCallbackUtility.DragEventListener dragEventListener);
+
+ /**
+ * Releases any resources associated with this TaskDragResizer. This should be called when the
+ * associated window is closed.
+ */
+ void close();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index d2c79d7..7e941ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -205,6 +205,9 @@
return new Rect(mRepositionTaskBounds);
}
+ @Override
+ public void close() {}
+
private boolean isResizing() {
return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 1563259..5e4a0a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.view.SurfaceControl;
+import android.window.TransitionInfo;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -83,12 +84,14 @@
* @param taskSurface the surface of the task
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
+ * @param changeMode the type of change to the task
*/
void onTaskChanging(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT);
+ SurfaceControl.Transaction finishT,
+ @TransitionInfo.TransitionMode int changeMode);
/**
* Notifies that the given task is about to close to give the window decoration a chance to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index bde46a1..91a899c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -699,6 +699,9 @@
public void close() {
Trace.beginSection("WindowDecoration#close");
mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
+ if (mTaskDragResizer != null) {
+ mTaskDragResizer.close();
+ }
final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
releaseViews(wct);
mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 9c55f0e..7c5f34f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -152,6 +152,8 @@
endBounds = destinationBounds,
callback,
)
+ } else {
+ callback.invoke()
}
}
return isTiled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index d9df899f..0985587 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -49,7 +49,7 @@
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
* It hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
*/
-internal class AppHandleViewHolder(
+class AppHandleViewHolder(
rootView: View,
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: OnClickListener,
@@ -66,7 +66,7 @@
val position: Point,
val width: Int,
val height: Int,
- val isCaptionVisible: Boolean
+ val showInputLayer: Boolean
) : Data()
private lateinit var taskInfo: RunningTaskInfo
@@ -101,7 +101,7 @@
}
override fun bindData(data: HandleData) {
- bindData(data.taskInfo, data.position, data.width, data.height, data.isCaptionVisible)
+ bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer)
}
private fun bindData(
@@ -109,14 +109,13 @@
position: Point,
width: Int,
height: Int,
- isCaptionVisible: Boolean
+ showInputLayer: Boolean
) {
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
// If handle is not in status bar region(i.e., bottom stage in vertical split),
// do not create an input layer
- if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
- if (!isCaptionVisible) {
+ if (position.y >= SystemBarUtils.getStatusBarHeight(context) || !showInputLayer) {
disposeStatusBarInputLayer()
return
}
@@ -276,4 +275,25 @@
}
override fun close() {}
+
+ /** Factory class for creating [AppHandleViewHolder] objects. */
+ class Factory {
+ /**
+ * Create a [AppHandleViewHolder] object to handle caption view and status bar
+ * input layer logic.
+ */
+ fun create(
+ rootView: View,
+ onCaptionTouchListener: View.OnTouchListener,
+ onCaptionButtonClickListener: OnClickListener,
+ windowManagerWrapper: WindowManagerWrapper,
+ handler: Handler,
+ ): AppHandleViewHolder = AppHandleViewHolder(
+ rootView,
+ onCaptionTouchListener,
+ onCaptionButtonClickListener,
+ windowManagerWrapper,
+ handler,
+ )
+ }
}
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
new file mode 100644
index 0000000..68f7ef0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeoutOrNull
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A TestRule to manage multiple simulated connected overlay displays.
+ */
+class SimulatedConnectedDisplayTestRule : TestRule {
+
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
+ private val displayManager = context.getSystemService(DisplayManager::class.java)
+ private val addedDisplays = mutableListOf<Int>()
+
+ override fun apply(base: Statement, description: Description): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ try {
+ base.evaluate()
+ } finally {
+ teardown()
+ }
+ }
+ }
+
+ private fun teardown() {
+ cleanupTestDisplays()
+ }
+
+ /**
+ * Adds multiple overlay displays with specified dimensions. Any existing overlay displays
+ * will be removed before adding the new ones.
+ *
+ * @param displays A list of [Point] objects, where each [Point] represents the
+ * width and height of a simulated display.
+ * @return List of displayIds of added displays.
+ */
+ fun setupTestDisplays(displays: List<Point>): List<Int> = runBlocking {
+ // Cleanup any existing overlay displays.
+ cleanupTestDisplays()
+
+ if (displays.isEmpty()) {
+ Log.w(TAG, "setupTestDisplays called with an empty list. No displays created.")
+ return@runBlocking emptyList()
+ }
+
+ val displayAddedFlow: Flow<Int> = callbackFlow {
+ val listener = object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {
+ trySend(displayId)
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {}
+ override fun onDisplayChanged(displayId: Int) {}
+ }
+
+ val handler = Handler(Looper.getMainLooper())
+ displayManager.registerDisplayListener(listener, handler)
+
+ awaitClose {
+ displayManager.unregisterDisplayListener(listener)
+ }
+ }
+
+ val displaySettings = displays.joinToString(separator = ";") { size ->
+ "${size.x}x${size.y}/$DEFAULT_DENSITY"
+ }
+
+ // Add the overlay displays
+ Settings.Global.putString(
+ InstrumentationRegistry.getInstrumentation().context.contentResolver,
+ Settings.Global.OVERLAY_DISPLAY_DEVICES, displaySettings
+ )
+ withTimeoutOrNull(TIMEOUT) {
+ displayAddedFlow.take(displays.size).collect { displayId ->
+ addedDisplays.add(displayId)
+ }
+ } ?: error("Timed out waiting for displays to be added.")
+ addedDisplays
+ }
+
+ /**
+ * Adds multiple overlay displays with default dimensions. Any existing overlay displays
+ * will be removed before adding the new ones.
+ *
+ * @param count number of displays to add.
+ * @return List of displayIds of added displays.
+ */
+ fun setupTestDisplays(count: Int): List<Int> {
+ val displays = List(count) { Point(DEFAULT_WIDTH, DEFAULT_HEIGHT) }
+ return setupTestDisplays(displays)
+ }
+
+ private fun cleanupTestDisplays() = runBlocking {
+ if (addedDisplays.isEmpty()) {
+ return@runBlocking
+ }
+
+ val displayRemovedFlow: Flow<Int> = callbackFlow {
+ val listener = object : DisplayListener {
+ override fun onDisplayAdded(displayId: Int) {}
+ override fun onDisplayRemoved(displayId: Int) {
+ trySend(displayId)
+ }
+
+ override fun onDisplayChanged(displayId: Int) {}
+ }
+ val handler = Handler(Looper.getMainLooper())
+ displayManager.registerDisplayListener(listener, handler)
+
+ awaitClose {
+ displayManager.unregisterDisplayListener(listener)
+ }
+ }
+
+ // Remove overlay displays
+ Settings.Global.putString(
+ InstrumentationRegistry.getInstrumentation().context.contentResolver,
+ Settings.Global.OVERLAY_DISPLAY_DEVICES, null)
+
+ withTimeoutOrNull(TIMEOUT) {
+ displayRemovedFlow.take(addedDisplays.size).collect { displayId ->
+ addedDisplays.remove(displayId)
+ }
+ } ?: error("Timed out waiting for displays to be removed.")
+ }
+
+ private companion object {
+ const val DEFAULT_WIDTH = 1280
+ const val DEFAULT_HEIGHT = 720
+ const val DEFAULT_DENSITY = 160
+ const val TAG = "SimulatedConnectedDisplayTestRule"
+ val TIMEOUT = 10.seconds
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 05750a5..b139c00 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -889,8 +889,8 @@
*/
private void doStartEvents(int startX, int moveX) {
doMotionEvent(MotionEvent.ACTION_DOWN, startX);
- mController.onThresholdCrossed();
doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
+ mController.onThresholdCrossed();
}
private void simulateRemoteAnimationStart() throws RemoteException {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 2ef6c55..43bcc3b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -58,8 +58,7 @@
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT);
}
@Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 2cc52c5..9d4cc49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -224,8 +224,7 @@
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT,
- /* departingAnimationTarget = */ null
+ /* swipeEdge = */ BackEvent.EDGE_LEFT
)
private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 7a7d88b..b3d2db6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -804,7 +803,7 @@
mBubbleData.setListener(mListener);
changeExpandedStateAtTime(true, 2000L);
- verifyZeroInteractions(mListener);
+ verifyNoMoreInteractions(mListener);
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index 25f17fe..b136bed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -311,4 +311,39 @@
verify(startT).apply();
assertFalse(mTaskViewTransitions.hasPending());
}
+
+ @Test
+ public void convertFloatingBubbleToFullscreen() {
+ final BubbleExpandedView bev = mock(BubbleExpandedView.class);
+ final ViewRootImpl vri = mock(ViewRootImpl.class);
+ when(bev.getViewRootImpl()).thenReturn(vri);
+ when(mBubble.getBubbleBarExpandedView()).thenReturn(null);
+ when(mBubble.getExpandedView()).thenReturn(bev);
+
+ ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+ final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertFromBubble(
+ mBubble, taskInfo);
+ final BubbleTransitions.ConvertFromBubble cfb = (BubbleTransitions.ConvertFromBubble) bt;
+ verify(mTransitions).startTransition(anyInt(), any(), eq(cfb));
+ verify(mBubble).setPreparingTransition(eq(bt));
+ assertTrue(mTaskViewTransitions.hasPending());
+
+ final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+ final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+ mock(SurfaceControl.class));
+ chg.setMode(TRANSIT_CHANGE);
+ chg.setTaskInfo(taskInfo);
+ info.addChange(chg);
+ info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+ SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCb = wct -> {};
+ cfb.startAnimation(cfb.mTransition, info, startT, finishT, finishCb);
+
+ // Can really only verify that it interfaces with the taskViewTransitions queue.
+ // The actual functioning of this is tightly-coupled with SurfaceFlinger and renderthread
+ // in order to properly synchronize surface manipulation with drawing and thus can't be
+ // directly tested.
+ assertFalse(mTaskViewTransitions.hasPending());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
index c4b9c9b..b4b9679 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -25,7 +25,6 @@
import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
@@ -84,7 +83,7 @@
verify(mMotionEventListener).onMove(0, -600);
// Check that velocity up is about 5000
verify(mMotionEventListener).onUp(eq(0f), floatThat(f -> Math.round(f) == -5000));
- verifyZeroInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mMotionEventListener);
verify(mInterceptTouchRunnable).run();
}
@@ -94,8 +93,8 @@
mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 100));
mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 100));
- verifyZeroInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
@Test
@@ -107,7 +106,7 @@
verify(mMotionEventListener).onDown(0, 990);
verify(mMotionEventListener).onMove(100, 0);
verify(mMotionEventListener).onUp(0, 0);
- verifyZeroInteractions(mMotionEventListener);
+ verifyNoMoreInteractions(mMotionEventListener);
verify(mInterceptTouchRunnable).run();
}
@@ -119,7 +118,7 @@
verify(mMotionEventListener).onDown(0, 990);
verifyNoMoreInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
@Test
@@ -129,7 +128,7 @@
verify(mMotionEventListener).onDown(0, 990);
verify(mMotionEventListener).onCancel();
verifyNoMoreInteractions(mMotionEventListener);
- verifyZeroInteractions(mInterceptTouchRunnable);
+ verifyNoMoreInteractions(mInterceptTouchRunnable);
}
private MotionEvent newEvent(int actionDown, float x, float y) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
index 3323740..1472464 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -22,7 +22,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
@@ -105,6 +105,6 @@
int sameDevicePosture = mDevicePostureCaptor.getValue();
mDevicePostureController.onDevicePostureChanged(sameDevicePosture);
- verifyZeroInteractions(mOnDevicePostureChangedListener);
+ verifyNoMoreInteractions(mOnDevicePostureChangedListener);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index ee9d177..a53277a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -32,7 +32,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.graphics.Insets;
import android.graphics.Point;
@@ -108,26 +108,26 @@
public void insetsControlChanged_schedulesNoWorkOnExecutor() {
Looper.prepare();
mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void insetsChanged_schedulesNoWorkOnExecutor() {
Looper.prepare();
mPerDisplay.insetsChanged(insetsStateWithIme(false));
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void showInsets_schedulesNoWorkOnExecutor() {
mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
@Test
public void hideInsets_schedulesNoWorkOnExecutor() {
mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
- verifyZeroInteractions(mExecutor);
+ verifyNoMoreInteractions(mExecutor);
}
// With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore
@@ -135,7 +135,7 @@
@Test
@RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void reappliesVisibilityToChangedLeash() {
- verifyZeroInteractions(mT);
+ verifyNoMoreInteractions(mT);
mPerDisplay.mImeShowing = false;
mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
index 96d202c..7d18669 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
@@ -29,7 +29,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -145,7 +145,7 @@
mConfiguration.windowConfiguration.setDisplayRotation(Surface.ROTATION_0);
mPipTabletopController.onDisplayConfigurationChanged(DEFAULT_DISPLAY, mConfiguration);
- verifyZeroInteractions(mOnTabletopModeChangedListener);
+ verifyNoMoreInteractions(mOnTabletopModeChangedListener);
}
// Test cases starting from folded state (DEVICE_POSTURE_CLOSED)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
index e92e243..a2066db 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java
@@ -21,7 +21,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -157,7 +157,7 @@
opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
packageName);
- verifyZeroInteractions(mMockExecutor);
+ verifyNoMoreInteractions(mMockExecutor);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index 450989d..da6a67c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -26,28 +26,36 @@
import android.os.Handler
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.UsesFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.server.display.feature.flags.Flags as DisplayFlags
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,6 +68,7 @@
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
/**
* Test class for [DesktopDisplayModeController]
@@ -68,6 +77,7 @@
*/
@SmallTest
@RunWith(TestParameterInjector::class)
+@UsesFlags(com.android.server.display.feature.flags.Flags::class)
class DesktopDisplayModeControllerTest(
@TestParameter(valuesProvider = FlagsParameterizationProvider::class)
flags: FlagsParameterization
@@ -79,6 +89,7 @@
private val desktopWallpaperActivityTokenProvider =
mock<DesktopWallpaperActivityTokenProvider>()
private val inputManager = mock<InputManager>()
+ private val displayController = mock<DisplayController>()
private val mainHandler = mock<Handler>()
private lateinit var controller: DesktopDisplayModeController
@@ -90,6 +101,13 @@
TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
private val wallpaperToken = MockToken().token()
+ private val defaultDisplay = mock<Display>()
+ private val externalDisplay = mock<Display>()
+
+ private lateinit var extendedDisplaySettingsRestoreSession:
+ ExtendedDisplaySettingsRestoreSession
+
+ private lateinit var mockitoSession: StaticMockitoSession
init {
mSetFlagsRule.setFlagsParameterization(flags)
@@ -97,6 +115,13 @@
@Before
fun setUp() {
+ mockitoSession =
+ mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ extendedDisplaySettingsRestoreSession =
+ ExtendedDisplaySettingsRestoreSession(context.contentResolver)
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
.thenReturn(defaultTDA)
@@ -109,42 +134,57 @@
shellTaskOrganizer,
desktopWallpaperActivityTokenProvider,
inputManager,
+ displayController,
mainHandler,
)
runningTasks.add(freeformTask)
runningTasks.add(fullscreenTask)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+ whenever(displayController.getDisplay(DEFAULT_DISPLAY)).thenReturn(defaultDisplay)
+ whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
setTabletModeStatus(SwitchState.UNKNOWN)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
+ )
+ ).thenReturn(true)
+ }
+
+ @After
+ fun tearDown() {
+ extendedDisplaySettingsRestoreSession.restore()
+ mockitoSession.finishMocking()
}
private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
- if (expectToSwitch) {
- // Assumes [connectExternalDisplay] properly triggered the switching transition.
- // Will verify the transition later along with [disconnectExternalDisplay].
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- }
- disconnectExternalDisplay()
+ setExtendedMode(true)
- if (expectToSwitch) {
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(2))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- } else {
- verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
- }
+ connectExternalDisplay()
+ if (expectToSwitch) {
+ // Assumes [connectExternalDisplay] properly triggered the switching transition.
+ // Will verify the transition later along with [disconnectExternalDisplay].
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ }
+ disconnectExternalDisplay()
+
+ if (expectToSwitch) {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(2))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ } else {
+ verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
}
}
@@ -176,15 +216,16 @@
disconnectExternalDisplay()
}
setTabletModeStatus(tabletModeStatus)
-
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (param.extendedDisplayEnabled) 1 else 0,
+ setExtendedMode(param.extendedDisplayEnabled)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
)
- .use {
- assertThat(controller.getTargetWindowingModeForDefaultDisplay())
- .isEqualTo(param.expectedWindowingMode)
- }
+ ).thenReturn(param.isDefaultDisplayDesktopEligible)
+
+ assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+ .isEqualTo(param.expectedWindowingMode)
}
@Test
@@ -199,15 +240,16 @@
disconnectExternalDisplay()
}
setTabletModeStatus(param.tabletModeStatus)
-
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (param.extendedDisplayEnabled) 1 else 0,
+ setExtendedMode(param.extendedDisplayEnabled)
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ defaultDisplay
)
- .use {
- assertThat(controller.getTargetWindowingModeForDefaultDisplay())
- .isEqualTo(param.expectedWindowingMode)
- }
+ ).thenReturn(param.isDefaultDisplayDesktopEligible)
+
+ assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+ .isEqualTo(param.expectedWindowingMode)
}
@Test
@@ -215,18 +257,16 @@
fun displayWindowingModeSwitch_existingTasksOnConnected() {
defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+ setExtendedMode(true)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
+ connectExternalDisplay()
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- }
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@Test
@@ -236,18 +276,16 @@
whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
WINDOWING_MODE_FULLSCREEN
}
+ setExtendedMode(true)
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- disconnectExternalDisplay()
+ disconnectExternalDisplay()
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
}
private fun connectExternalDisplay() {
@@ -266,18 +304,30 @@
whenever(inputManager.isInTabletMode()).thenReturn(status.value)
}
- private class ExtendedDisplaySettingsSession(
- private val contentResolver: ContentResolver,
- private val overrideValue: Int,
- ) : AutoCloseable {
+ private fun setExtendedMode(enabled: Boolean) {
+ if (DisplayFlags.enableDisplayContentModeManagement()) {
+ whenever(
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+ context,
+ externalDisplay
+ )
+ ).thenReturn(enabled)
+ } else {
+ Settings.Global.putInt(
+ context.contentResolver,
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+ if (enabled) 1 else 0,
+ )
+ }
+ }
+
+ private class ExtendedDisplaySettingsRestoreSession(
+ private val contentResolver: ContentResolver
+ ) {
private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
- init {
- Settings.Global.putInt(contentResolver, settingName, overrideValue)
- }
-
- override fun close() {
+ fun restore() {
Settings.Global.putInt(contentResolver, settingName, initialValue)
}
}
@@ -287,7 +337,8 @@
context: TestParameterValuesProvider.Context
): List<FlagsParameterization> {
return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH
+ Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
+ DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
)
}
}
@@ -305,54 +356,119 @@
val defaultWindowingMode: Int,
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
+ val isDefaultDisplayDesktopEligible: Boolean,
val expectedWindowingMode: Int,
) {
- FREEFORM_EXTERNAL_EXTENDED(
+ FREEFORM_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = true,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_EXTERNAL_EXTENDED(
+ FULLSCREEN_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = true,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FREEFORM_NO_EXTERNAL_EXTENDED(
+ FREEFORM_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = false,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_NO_EXTERNAL_EXTENDED(
+ FULLSCREEN_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = false,
extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- FREEFORM_EXTERNAL_MIRROR(
+ FREEFORM_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = true,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_EXTERNAL_MIRROR(
+ FULLSCREEN_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = true,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- FREEFORM_NO_EXTERNAL_MIRROR(
+ FREEFORM_NO_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FREEFORM,
hasExternalDisplay = false,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- FULLSCREEN_NO_EXTERNAL_MIRROR(
+ FULLSCREEN_NO_EXTERNAL_MIRROR_NO_PROJECTED(
defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
hasExternalDisplay = false,
extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_NO_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_NO_EXTERNAL_EXTENDED_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ FREEFORM_NO_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ FULLSCREEN_NO_EXTERNAL_MIRROR_PROJECTED(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
@@ -361,78 +477,175 @@
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
val tabletModeStatus: SwitchState,
+ val isDefaultDisplayDesktopEligible: Boolean,
val expectedWindowingMode: Int,
) {
- EXTERNAL_EXTENDED_TABLET(
+ EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_TABLET(
+ NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET(
+ EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET(
+ NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL(
+ EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL(
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_MIRROR_CLAMSHELL(
+ EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL(
+ NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_EXTENDED_UNKNOWN(
+ EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN(
+ NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN(
+ EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN(
+ NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_TABLET_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_TABLET_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index 8a5acfa..695cf60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -23,7 +23,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
-import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags
@@ -102,7 +102,7 @@
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -127,7 +127,7 @@
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -135,7 +135,7 @@
desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -157,7 +157,7 @@
eq(sessionId),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
}
@@ -166,7 +166,7 @@
desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -205,7 +205,7 @@
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -213,7 +213,7 @@
desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -252,7 +252,7 @@
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -260,7 +260,7 @@
desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -302,7 +302,7 @@
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -346,7 +346,7 @@
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -390,7 +390,7 @@
eq(UNSET_FOCUS_REASON),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -434,7 +434,7 @@
eq(FocusReason.UNKNOWN.reason),
)
}
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -446,7 +446,7 @@
)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@@ -485,7 +485,7 @@
)
verifyNoLogging()
- verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index b7d25b5..bd37610 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -83,7 +83,7 @@
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -596,7 +596,7 @@
.logTaskRemoved(
eq(DEFAULT_TASK_UPDATE.copy(minimizeReason = MinimizeReason.MINIMIZE_BUTTON))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -668,7 +668,7 @@
.logTaskInfoChanged(
eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -701,7 +701,7 @@
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -729,7 +729,7 @@
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -753,7 +753,7 @@
.logTaskInfoChanged(
eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
// task 2 resize
val newTaskInfo2 =
@@ -781,7 +781,7 @@
)
)
)
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
@Test
@@ -892,14 +892,14 @@
eq(taskUpdate.visibleTaskCount.toString()),
)
}
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
private fun verifyTaskRemovedAndExitLogging(exitReason: ExitReason, taskUpdate: TaskUpdate) {
assertFalse(transitionObserver.isSessionActive)
verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyNoMoreInteractions(desktopModeEventLogger)
}
private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 3813f75..a10aeca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -275,6 +275,18 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeActiveTask_excludingDesk_leavesTaskInDesk() {
+ repo.addDesk(displayId = 2, deskId = 11)
+ repo.addDesk(displayId = 3, deskId = 12)
+ repo.addTaskToDesk(displayId = 3, deskId = 12, taskId = 100, isVisible = true)
+
+ repo.removeActiveTask(taskId = 100, excludedDeskId = 12)
+
+ assertThat(repo.getActiveTaskIdsInDesk(12)).contains(100)
+ }
+
+ @Test
fun isActiveTask_nonExistingTask_returnsFalse() {
assertThat(repo.isActiveTask(99)).isFalse()
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 14af573..7efcd4f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -57,6 +57,7 @@
import android.testing.TestableContext
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display.INVALID_DISPLAY
import android.view.DragEvent
import android.view.Gravity
import android.view.MotionEvent
@@ -458,7 +459,7 @@
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionsObserver,
- desktopPipTransitionObserver,
+ Optional.of(desktopPipTransitionObserver),
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -3126,6 +3127,36 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_toDesktopInOtherDisplay_appliesTaskLimit() {
+ val transition = Binder()
+ val sourceDeskId = 0
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ taskRepository.setDeskInactive(deskId = targetDeskId)
+ val targetDeskTasks =
+ (1..MAX_TASK_LIMIT + 1).map { _ ->
+ setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ }
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+ whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+ .thenReturn(transition)
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+
+ controller.moveToNextDisplay(task.taskId)
+
+ val wct = getLatestTransition()
+ assertNotNull(wct)
+ verify(desksOrganizer).minimizeTask(wct, targetDeskId, targetDeskTasks[0])
+ }
+
+ @Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
@@ -3194,10 +3225,11 @@
verify(desksOrganizer).activateDesk(any(), eq(targetDeskId))
verify(desksTransitionsObserver)
.addPendingTransition(
- DeskTransition.ActivateDesk(
+ DeskTransition.ActiveDeskWithTask(
token = transition,
displayId = SECOND_DISPLAY,
deskId = targetDeskId,
+ enterTaskId = task.taskId,
)
)
}
@@ -5072,6 +5104,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun handleRequest_closeTransition_singleTaskNoToken_secondaryDisplay_launchesHome() {
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -7409,6 +7442,14 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testCreateDesk_invalidDisplay_dropsRequest() {
+ controller.createDesk(INVALID_DISPLAY)
+
+ verify(desksOrganizer, never()).createDesk(any(), any())
+ }
+
+ @Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index ec64c2f..5ef1ace 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -50,6 +50,7 @@
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import java.util.Optional
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -110,7 +111,7 @@
transitions,
shellTaskOrganizer,
mixedHandler,
- pipTransitionObserver,
+ Optional.of(pipTransitionObserver),
backAnimationController,
desktopWallpaperActivityTokenProvider,
shellInit,
@@ -333,6 +334,50 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun nonTopTransparentTaskOpened_clearTopTransparentTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ val nonTopTransparentTask = createTaskInfo(2)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = mockTransition,
+ info = createOpenChangeTransition(nonTopTransparentTask),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun nonTopTransparentTaskSentToFront_clearTopTransparentTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ val nonTopTransparentTask = createTaskInfo(2)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = mockTransition,
+ info = createToFrontTransition(nonTopTransparentTask),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+ }
+
+ @Test
fun transitCloseWallpaper_wallpaperActivityVisibilitySaved() {
val wallpaperTask = createWallpaperTaskInfo()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 6e7adf3..4e2994c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -54,7 +54,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.MockitoSession
@@ -64,7 +66,7 @@
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -85,8 +87,17 @@
@Mock private lateinit var desktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var bubbleController: BubbleController
@Mock private lateinit var visualIndicator: DesktopModeVisualIndicator
+ @Mock private lateinit var dragCancelCallback: Runnable
+ @Mock
+ private lateinit var dragToDesktopStateListener:
+ DragToDesktopTransitionHandler.DragToDesktopStateListener
- private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
+ private val transactionSupplier = Supplier {
+ val transaction = mock<SurfaceControl.Transaction>()
+ whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
+ transaction
+ }
private lateinit var defaultHandler: DragToDesktopTransitionHandler
private lateinit var springHandler: SpringDragToDesktopTransitionHandler
@@ -104,7 +115,11 @@
Optional.of(bubbleController),
transactionSupplier,
)
- .apply { setSplitScreenController(splitScreenController) }
+ .apply {
+ setSplitScreenController(splitScreenController)
+ dragToDesktopStateListener =
+ this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+ }
springHandler =
SpringDragToDesktopTransitionHandler(
context,
@@ -115,7 +130,11 @@
Optional.of(bubbleController),
transactionSupplier,
)
- .apply { setSplitScreenController(splitScreenController) }
+ .apply {
+ setSplitScreenController(splitScreenController)
+ dragToDesktopStateListener =
+ this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+ }
mockitoSession =
ExtendedMockito.mockitoSession()
.strictness(Strictness.LENIENT)
@@ -438,7 +457,7 @@
)
// No need to animate the cancel since the start animation couldn't even start.
- verifyZeroInteractions(dragAnimator)
+ verifyNoMoreInteractions(dragAnimator)
}
@Test
@@ -489,7 +508,7 @@
)
// Should NOT have any transaction changes
- verifyZeroInteractions(mergedStartTransaction)
+ verifyNoMoreInteractions(mergedStartTransaction)
// Should NOT merge animation
verify(finishCallback, never()).onTransitionFinished(any())
}
@@ -706,8 +725,8 @@
}
@Test
- @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
- fun mergeOtherTransition_cancelAndEndNotYetRequested_doesntInterruptsStartDrag() {
+ @DisableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun mergeOtherTransition_flagDisabled_cancelAndEndNotYetRequested_doesNotInterruptStartDrag() {
val finishCallback = mock<Transitions.TransitionFinishCallback>()
val task = createTask()
defaultHandler.onTaskResizeAnimationListener = mock()
@@ -721,6 +740,39 @@
@Test
@EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun mergeOtherTransition_cancelAndEndNotYetRequested_interruptsStartDrag() {
+ val finishCallback = mock<Transitions.TransitionFinishCallback>()
+ val task = createTask()
+ defaultHandler.onTaskResizeAnimationListener = mock()
+ val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+ mergeInterruptingTransition(mergeTarget = startTransition)
+
+ verify(dragAnimator).cancelAnimator()
+ verify(dragCancelCallback).run()
+ verify(dragToDesktopStateListener).onTransitionInterrupted()
+ assertThat(defaultHandler.inProgress).isTrue()
+ // Doesn't finish start transition yet
+ verify(finishCallback, never()).onTransitionFinished(/* wct= */ anyOrNull())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun mergeOtherTransition_cancelAndEndNotYetRequested_finishesStartAfterAnimation() {
+ val finishCallback = mock<Transitions.TransitionFinishCallback>()
+ val task = createTask()
+ defaultHandler.onTaskResizeAnimationListener = mock()
+ val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+ mergeInterruptingTransition(mergeTarget = startTransition)
+ mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+
+ verify(finishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+ assertThat(defaultHandler.inProgress).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
fun mergeOtherTransition_endDragAlreadyMerged_doesNotInterruptStartDrag() {
val startDragFinishCallback = mock<Transitions.TransitionFinishCallback>()
val task = createTask()
@@ -795,6 +847,35 @@
verify(dragAnimator, times(2)).startAnimation()
}
+ @Test
+ @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+ fun startCancelAnimation_otherTransitionInterruptingAfterCancelRequest_finishImmediately() {
+ val task1 = createTask()
+ val startTransition = startDrag(defaultHandler, task1)
+ val cancelTransition =
+ cancelDragToDesktopTransition(defaultHandler, CancelState.STANDARD_CANCEL)
+ mergeInterruptingTransition(mergeTarget = startTransition)
+ val cancelFinishCallback = mock<Transitions.TransitionFinishCallback>()
+ val startTransaction = mock<SurfaceControl.Transaction>()
+
+ val didAnimate =
+ defaultHandler.startAnimation(
+ transition = requireNotNull(cancelTransition),
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP,
+ draggedTask = task1,
+ ),
+ startTransaction = startTransaction,
+ finishTransaction = mock(),
+ finishCallback = cancelFinishCallback,
+ )
+
+ assertThat(didAnimate).isTrue()
+ verify(startTransaction).apply()
+ verify(cancelFinishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+ }
+
private fun mergeInterruptingTransition(mergeTarget: IBinder) {
defaultHandler.mergeAnimation(
transition = mock<IBinder>(),
@@ -942,7 +1023,12 @@
)
)
.thenReturn(token)
- handler.startDragToDesktopTransition(task, dragAnimator, visualIndicator)
+ handler.startDragToDesktopTransition(
+ task,
+ dragAnimator,
+ visualIndicator,
+ dragCancelCallback,
+ )
return token
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index c7518d5..3983bfb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -54,7 +54,7 @@
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -111,7 +111,7 @@
eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
)
// Assert fadeIn, fadeOut, and animateIndicatorType were not called.
- verifyZeroInteractions(spyViewContainer)
+ verifyNoMoreInteractions(spyViewContainer)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index 7560945..dc973d0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -90,7 +90,7 @@
whenever(packageManager.getHomeActivities(ArrayList())).thenReturn(componentName)
desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext)
transitionHandler = createTransitionHandler()
- allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
}
private fun createTransitionHandler() =
@@ -200,10 +200,16 @@
.isTrue()
}
- fun allowOverlayPermission(permissions: Array<String>) {
+ fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
val packageInfo = mock<PackageInfo>()
packageInfo.requestedPermissions = permissions
- whenever(packageManager.getPackageInfo(anyString(), eq(PackageManager.GET_PERMISSIONS)))
+ whenever(
+ packageManager.getPackageInfoAsUser(
+ anyString(),
+ eq(PackageManager.GET_PERMISSIONS),
+ anyInt(),
+ )
+ )
.thenReturn(packageInfo)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
index 14f9ffc..2bd9afc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -24,7 +24,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -107,7 +107,7 @@
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index 72c4666..fa7ab952 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -24,7 +24,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -117,7 +117,7 @@
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
// Check corner and shadow radii were set
verify(mMockAnimateTransaction, atLeastOnce())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index b816f0e..97133be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -21,7 +21,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -143,7 +143,7 @@
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index 23fbad0..c99ca6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -22,7 +22,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.kotlin.MatchersKt.eq;
@@ -118,7 +118,7 @@
});
verify(mMockStartCallback).run();
- verifyZeroInteractions(mMockEndCallback);
+ verifyNoMoreInteractions(mMockEndCallback);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 5029371..b6894fd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -30,7 +30,7 @@
import static org.mockito.kotlin.VerificationKt.clearInvocations;
import static org.mockito.kotlin.VerificationKt.times;
import static org.mockito.kotlin.VerificationKt.verify;
-import static org.mockito.kotlin.VerificationKt.verifyZeroInteractions;
+import static org.mockito.kotlin.VerificationKt.verifyNoMoreInteractions;
import android.app.ActivityManager;
import android.app.PendingIntent;
@@ -176,7 +176,7 @@
mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
aspectRatio, action1));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
}
@Test
@@ -193,7 +193,7 @@
clearInvocations(mMockPipParamsChangedCallback);
mPipTaskListener.onTaskInfoChanged(new ActivityManager.RunningTaskInfo());
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
verify(mMockPipTransitionState, times(0))
.setOnIdlePipTransitionStateRunnable(any(Runnable.class));
}
@@ -245,7 +245,7 @@
mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
verify(mMockPipTransitionState).setOnIdlePipTransitionStateRunnable(any(Runnable.class));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
}
@Test
@@ -262,7 +262,7 @@
clearInvocations(mMockPipParamsChangedCallback);
mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
- verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verifyNoMoreInteractions(mMockPipParamsChangedCallback);
verify(mMockPipTransitionState, times(0))
.setOnIdlePipTransitionStateRunnable(any(Runnable.class));
}
@@ -319,7 +319,7 @@
PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
extras);
- verifyZeroInteractions(mMockPipScheduler);
+ verifyNoMoreInteractions(mMockPipScheduler);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
index 82cdfd5..51de50d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
@@ -20,7 +20,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Flags;
@@ -82,7 +82,7 @@
mPipUiStateChangeController.onPipTransitionStateChanged(
PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
- verifyZeroInteractions(mPictureInPictureUiStateConsumer);
+ verifyNoMoreInteractions(mPictureInPictureUiStateConsumer);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index 5ac6800..12785c0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -42,6 +42,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
@@ -87,7 +88,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
- allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -101,7 +102,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
- allowOverlayPermission(arrayOf())
+ allowOverlayPermissionForAllUsers(arrayOf())
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -115,7 +116,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptCachedPermissionCheckIsUsed() {
- allowOverlayPermission(arrayOf())
+ allowOverlayPermissionForAllUsers(arrayOf())
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
.apply {
@@ -123,6 +124,7 @@
isTopActivityNoDisplay = false
numActivities = 1
baseActivity = baseActivityTest
+ userId = 10
}))
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
@@ -131,10 +133,26 @@
isTopActivityNoDisplay = false
numActivities = 1
baseActivity = baseActivityTest
+ userId = 10
}))
- verify(packageManager, times(1)).getPackageInfo(
+ assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ baseActivity = baseActivityTest
+ userId = 0
+ }))
+ verify(packageManager, times(1)).getPackageInfoAsUser(
eq("com.test.dummypackage"),
- eq(PackageManager.GET_PERMISSIONS)
+ eq(PackageManager.GET_PERMISSIONS),
+ eq(10)
+ )
+ verify(packageManager, times(1)).getPackageInfoAsUser(
+ eq("com.test.dummypackage"),
+ eq(PackageManager.GET_PERMISSIONS),
+ eq(0)
)
}
@@ -284,13 +302,14 @@
}
}
- fun allowOverlayPermission(permissions: Array<String>) {
+ fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
val packageInfo = mock<PackageInfo>()
packageInfo.requestedPermissions = permissions
whenever(
- packageManager.getPackageInfo(
+ packageManager.getPackageInfoAsUser(
anyString(),
- eq(PackageManager.GET_PERMISSIONS)
+ eq(PackageManager.GET_PERMISSIONS),
+ anyInt(),
)
).thenReturn(packageInfo)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 3099b0f..a122c38 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -27,6 +27,7 @@
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
@@ -44,6 +45,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -196,6 +198,73 @@
}
@Test
+ @DisableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+ public void startDragToDesktopFinished_flagDisabled_doesNotTriggerCallback()
+ throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+ IBinder transition = mock(IBinder.class);
+ mHomeTransitionObserver.onTransitionReady(
+ transition,
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+
+ mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+ verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+ }
+
+ @Test
+ @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+ public void startDragToDesktopAborted_doesNotTriggerCallback() throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+ IBinder transition = mock(IBinder.class);
+ mHomeTransitionObserver.onTransitionReady(
+ transition,
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+
+ mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ true);
+
+ verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+ }
+
+ @Test
+ @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+ public void startDragToDesktopFinished_triggersCallback() throws RemoteException {
+ TransitionInfo info = mock(TransitionInfo.class);
+ TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ when(change.getTaskInfo()).thenReturn(taskInfo);
+ when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+ when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+ setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+ IBinder transition = mock(IBinder.class);
+ mHomeTransitionObserver.onTransitionReady(
+ transition,
+ info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+
+ mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+ verify(mListener).onHomeVisibilityChanged(/* isVisible= */ true);
+ }
+
+ @Test
@EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
public void testDragTaskToBubbleOverHome_notifiesHomeIsVisible() throws RemoteException {
ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index 067dcec..b1f9241 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -28,6 +28,7 @@
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -109,7 +110,7 @@
onTaskOpening(task, taskSurface)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -165,7 +166,7 @@
setLargeScreen(false)
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
}
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 d69509f..ad3426e 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
@@ -51,6 +51,7 @@
import android.view.View
import android.view.ViewRootImpl
import android.view.WindowInsets.Type.statusBars
+import android.view.WindowManager.TRANSIT_CHANGE
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
@@ -134,7 +135,7 @@
task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -149,12 +150,12 @@
val taskSurface = SurfaceControl()
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
task.setWindowingMode(WINDOWING_MODE_FREEFORM)
task.setActivityType(ACTIVITY_TYPE_STANDARD)
- onTaskChanging(task, taskSurface)
+ onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
}
@@ -758,20 +759,6 @@
}
@Test
- fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
- val toSplitScreenListenerCaptor = forClass(Function0::class.java)
- as ArgumentCaptor<Function0<Unit>>
- val decor = createOpenTaskDecoration(
- windowingMode = WINDOWING_MODE_MULTI_WINDOW,
- onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
- )
-
- toSplitScreenListenerCaptor.value.invoke()
-
- verify(decor).disposeStatusBarInputLayer()
- }
-
- @Test
fun testDecor_onClickToOpenBrowser_closeMenus() {
val openInBrowserListenerCaptor = forClass(Consumer::class.java)
as ArgumentCaptor<Consumer<Intent>>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index a1f40fd..4c9c2f1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -84,6 +84,7 @@
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
import org.junit.After
import org.mockito.Mockito
@@ -125,6 +126,7 @@
protected val mockShellController = mock<ShellController>()
protected val testShellExecutor = TestShellExecutor()
protected val mockAppHeaderViewHolderFactory = mock<AppHeaderViewHolder.Factory>()
+ protected val mockAppHandleViewHolderFactory = mock<AppHandleViewHolder.Factory>()
protected val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
protected val mockShellCommandHandler = mock<ShellCommandHandler>()
protected val mockWindowManager = mock<IWindowManager>()
@@ -222,6 +224,7 @@
mockInputMonitorFactory,
transactionFactory,
mockAppHeaderViewHolderFactory,
+ mockAppHandleViewHolderFactory,
mockRootTaskDisplayAreaOrganizer,
windowDecorByTaskIdSpy,
mockInteractionJankMonitor,
@@ -331,7 +334,7 @@
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any(), any())
+ any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
@@ -353,12 +356,17 @@
)
}
- protected fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+ protected fun onTaskChanging(
+ task: RunningTaskInfo,
+ leash: SurfaceControl = SurfaceControl(),
+ changeMode: Int
+ ) {
desktopModeWindowDecorViewModel.onTaskChanging(
task,
leash,
StubTransaction(),
- StubTransaction()
+ StubTransaction(),
+ changeMode
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 8783249..f37f2fb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -115,6 +115,7 @@
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Unit;
@@ -171,6 +172,9 @@
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
private static final boolean DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS = false;
+ private static final boolean DEFAULT_IS_RECENTS_TRANSITION_RUNNING = false;
+ private static final boolean DEFAULT_IS_MOVING_TO_BACK = false;
+
@Mock
private DisplayController mMockDisplayController;
@@ -191,8 +195,12 @@
@Mock
private AppHeaderViewHolder.Factory mMockAppHeaderViewHolderFactory;
@Mock
+ private AppHandleViewHolder.Factory mMockAppHandleViewHolderFactory;
+ @Mock
private AppHeaderViewHolder mMockAppHeaderViewHolder;
@Mock
+ private AppHandleViewHolder mMockAppHandleViewHolder;
+ @Mock
private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
@Mock
private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
@@ -301,6 +309,9 @@
when(mMockAppHeaderViewHolderFactory
.create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHeaderViewHolder);
+ when(mMockAppHandleViewHolderFactory
+ .create(any(), any(), any(), any(), any()))
+ .thenReturn(mMockAppHandleViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
@@ -421,7 +432,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
/* shouldIgnoreCornerRadius= */ true,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
}
@@ -623,7 +636,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- /* shouldExcludeCaptionFromAppBounds */ true);
+ /* shouldExcludeCaptionFromAppBounds */ true,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Force consuming flags are disabled.
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
@@ -658,7 +673,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
assertThat(
@@ -737,7 +754,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -765,7 +784,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -792,7 +813,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -819,7 +842,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -845,7 +870,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -871,7 +898,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -898,7 +927,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -917,7 +948,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -944,7 +977,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -971,7 +1006,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1002,6 +1039,65 @@
assertThat(relayoutParams.mAsyncViewHost).isFalse();
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+ public void updateRelayoutParams_handle_movingToBack_captionNotVisible() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ /* isMovingToBack= */ true);
+
+ assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+ public void updateRelayoutParams_handle_inRecentsTransition_captionNotVisible() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ /* isRecentsTransitionRunning= */ true,
+ DEFAULT_IS_MOVING_TO_BACK);
+
+ assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+ }
+
@Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1633,7 +1729,9 @@
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
}
private DesktopModeWindowDecoration createWindowDecoration(
@@ -1676,9 +1774,9 @@
taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
- mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser,
- mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier,
- WindowContainerTransaction::new, SurfaceControl::new,
+ mMockAppHandleViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
+ mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+ mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
index 7341e09..3600997 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
@@ -40,11 +40,13 @@
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
import java.util.function.Supplier
+import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -63,6 +65,15 @@
private val testBgExecutor = TestShellExecutor()
private val mockWindowSession = mock<IWindowSession>()
private val mockInputEventReceiver = mock<TaskResizeInputEventReceiver>()
+ private val inputChannel = mock<InputChannel>()
+ private val sinkInputChannel = mock<InputChannel>()
+ private val decorationSurface = SurfaceControl.Builder().setName("decoration surface").build()
+ private val createdSurfaces = ArrayList<SurfaceControl>()
+
+ @After
+ fun tearDown() {
+ decorationSurface.release()
+ }
@Test
fun testGrantInputChannelOffMainThread() {
@@ -73,6 +84,35 @@
}
@Test
+ fun testGrantInputChannelAfterDecorSurfaceReleased() {
+ // Keep tracking the underlying surface that the decorationSurface points to.
+ val forVerification = SurfaceControl(decorationSurface, "forVerification")
+ try {
+ create()
+ decorationSurface.release()
+ testBgExecutor.flushAll()
+
+ verify(mockWindowSession)
+ .grantInputChannel(
+ anyInt(),
+ argThat<SurfaceControl> { isValid && isSameSurface(forVerification) },
+ any(),
+ anyOrNull(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyOrNull(),
+ any(),
+ any(),
+ any(),
+ )
+ } finally {
+ forVerification.release()
+ }
+ }
+
+ @Test
fun testInitializationCallback_waitsForBgSetup() {
val inputListener = create()
@@ -143,6 +183,40 @@
verify(mockWindowSession).remove(inputListener.mSinkClientToken)
}
+ @Test
+ fun testClose_afterBgSetup_disposesOfInputChannels() {
+ val inputListener = create()
+ testBgExecutor.flushAll()
+ inputListener.close()
+ testMainExecutor.flushAll()
+ verify(inputChannel).dispose()
+ verify(sinkInputChannel).dispose()
+ }
+
+ @Test
+ fun testClose_beforeBgSetup_releaseSurfaces() {
+ val inputListener = create()
+ inputListener.close()
+ testBgExecutor.flushAll()
+ testMainExecutor.flushAll()
+
+ assertThat(createdSurfaces).hasSize(1)
+ assertThat(createdSurfaces[0].isValid).isFalse()
+ }
+
+ @Test
+ fun testClose_afterBgSetup_releaseSurfaces() {
+ val inputListener = create()
+ testBgExecutor.flushAll()
+ inputListener.close()
+ testMainExecutor.flushAll()
+ testBgExecutor.flushAll()
+
+ assertThat(createdSurfaces).hasSize(2)
+ assertThat(createdSurfaces[0].isValid).isFalse()
+ assertThat(createdSurfaces[1].isValid).isFalse()
+ }
+
private fun verifyNoInputChannelGrantRequests() {
verify(mockWindowSession, never())
.grantInputChannel(
@@ -172,12 +246,26 @@
TestHandler(Looper.getMainLooper()),
mock<Choreographer>(),
Display.DEFAULT_DISPLAY,
- mock<SurfaceControl>(),
+ decorationSurface,
mock<DragPositioningCallback>(),
- { SurfaceControl.Builder() },
- { StubTransaction() },
+ {
+ object : SurfaceControl.Builder() {
+ override fun build(): SurfaceControl {
+ return super.build().also { createdSurfaces.add(it) }
+ }
+ }
+ },
+ {
+ object : StubTransaction() {
+ override fun remove(sc: SurfaceControl): SurfaceControl.Transaction {
+ return super.remove(sc).also { sc.release() }
+ }
+ }
+ },
mock<DisplayController>(),
mock<DesktopModeEventLogger>(),
+ inputChannel,
+ sinkInputChannel,
)
private class TestInitializationCallback : Runnable {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index f984f6d..2e46f63 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -98,8 +98,6 @@
private lateinit var handleMenu: HandleMenu
- private val menuWidthWithElevation = MENU_WIDTH + MENU_PILL_ELEVATION
-
@Before
fun setUp() {
val mockAdditionalViewHostViewContainer = AdditionalViewHostViewContainer(
@@ -126,7 +124,6 @@
addOverride(R.dimen.desktop_mode_handle_menu_height, MENU_HEIGHT)
addOverride(R.dimen.desktop_mode_handle_menu_margin_top, MENU_TOP_MARGIN)
addOverride(R.dimen.desktop_mode_handle_menu_margin_start, MENU_START_MARGIN)
- addOverride(R.dimen.desktop_mode_handle_menu_pill_elevation, MENU_PILL_ELEVATION)
addOverride(
R.dimen.desktop_mode_handle_menu_pill_spacing_margin, MENU_PILL_SPACING_MARGIN)
}
@@ -141,7 +138,7 @@
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of display.
- val expected = Point(DISPLAY_BOUNDS.centerX() - menuWidthWithElevation / 2, MENU_TOP_MARGIN)
+ val expected = Point(DISPLAY_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
}
@@ -165,7 +162,7 @@
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split left task.
val expected = Point(
- SPLIT_LEFT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+ SPLIT_LEFT_BOUNDS.centerX() - MENU_WIDTH / 2,
MENU_TOP_MARGIN
)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -180,7 +177,7 @@
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split right task.
val expected = Point(
- SPLIT_RIGHT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+ SPLIT_RIGHT_BOUNDS.centerX() - MENU_WIDTH / 2,
MENU_TOP_MARGIN
)
assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -323,7 +320,6 @@
private const val MENU_HEIGHT = 400
private const val MENU_TOP_MARGIN = 10
private const val MENU_START_MARGIN = 20
- private const val MENU_PILL_ELEVATION = 2
private const val MENU_PILL_SPACING_MARGIN = 4
private const val HANDLE_WIDTH = 80
private const val APP_NAME = "Test App"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index a6b0770..0798613 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -559,6 +559,17 @@
}
@Test
+ fun testClose() = runOnUiThread {
+ verify(mockDisplayController, times(1))
+ .addDisplayWindowListener(eq(taskPositioner))
+
+ taskPositioner.close()
+
+ verify(mockDisplayController, times(1))
+ .removeDisplayWindowListener(eq(taskPositioner))
+ }
+
+ @Test
fun testIsResizingOrAnimatingResizeSet() = runOnUiThread {
Assert.assertFalse(taskPositioner.isResizingOrAnimating)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index fa3d3e4..011c8f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -52,7 +52,7 @@
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@@ -216,7 +216,7 @@
veil.hideVeil()
- verifyZeroInteractions(mockTransaction)
+ verifyNoMoreInteractions(mockTransaction)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 9a2e2fa..2e95a97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -826,6 +826,18 @@
}
@Test
+ public void testClose_withTaskDragResizerSet_callResizerClose() {
+ final TestWindowDecoration windowDecor = createWindowDecoration(
+ new TestRunningTaskInfoBuilder().build());
+ final TaskDragResizer taskDragResizer = mock(TaskDragResizer.class);
+ windowDecor.setTaskDragResizer(taskDragResizer);
+
+ windowDecor.close();
+
+ verify(taskDragResizer).close();
+ }
+
+ @Test
public void testRelayout_captionFrameChanged_insetsReapplied() {
final Display defaultDisplay = mock(Display.class);
doReturn(defaultDisplay).when(mMockDisplayController)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
index c8ccac3..714d062 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/WindowDecorTaskResourceLoaderTest.kt
@@ -54,7 +54,7 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/**
@@ -125,7 +125,7 @@
loader.getName(task)
- verifyZeroInteractions(
+ verifyNoMoreInteractions(
mockPackageManager,
mockIconProvider,
mockHeaderIconFactory,
@@ -165,7 +165,7 @@
loader.getHeaderIcon(task)
- verifyZeroInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
+ verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
}
@Test
@@ -187,7 +187,7 @@
loader.getVeilIcon(task)
- verifyZeroInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
+ verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
}
@Test
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index f8eb418..496ba50 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -186,3 +186,11 @@
bug: "398254728"
is_fixed_read_only: true
}
+
+flag {
+ name: "gnss_assistance_interface_jni"
+ namespace: "location"
+ description: "Flag for GNSS assistance interface JNI"
+ bug: "209078566"
+}
+
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b57476f..6e0821f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -183,7 +183,17 @@
appContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
- mDisplayService.registerDisplayListener(this, mHandler);
+ if (com.android.server.display.feature.flags.Flags
+ .displayListenerPerformanceImprovements()
+ && com.android.server.display.feature.flags.Flags
+ .delayImplicitRrRegistrationUntilRrAccessed()) {
+ mDisplayService.registerDisplayListener(this, mHandler,
+ DisplayManager.EVENT_TYPE_DISPLAY_ADDED
+ | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
+ | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED);
+ } else {
+ mDisplayService.registerDisplayListener(this, mHandler);
+ }
AudioRoutesInfo newAudioRoutes = null;
try {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
index 6089f42..f65c7ef 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
@@ -28,7 +28,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.companion.virtual.VirtualDeviceManager;
@@ -56,7 +56,7 @@
audioManager.playSoundEffect(FX_KEY_CLICK);
// We expect no interactions with VDM when running on default device.
- verifyZeroInteractions(mockVdm);
+ verifyNoMoreInteractions(mockVdm);
}
@Test
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index b2c1e60..964268e 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -65,6 +65,7 @@
import android.net.MacAddress;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.text.Spanned;
@@ -621,8 +622,10 @@
Slog.w(TAG, "Already selected.");
return;
}
- // Notify the adapter to highlight the selected item.
- mDeviceAdapter.setSelectedPosition(position);
+ // Delay highlighting the selected item by posting to the main thread.
+ // This helps avoid flicker in the user consent dialog after device selection.
+ new Handler(
+ Looper.getMainLooper()).post(() -> mDeviceAdapter.setSelectedPosition(position));
mSelectedDevice = requireNonNull(selectedDevice);
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 5033101..716845c 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -23,6 +23,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
@@ -120,6 +142,28 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"options":[
{
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
index 472ffa9..6dec2f9 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.datastore
import android.content.SharedPreferences
+import android.util.Log
/** Interface of key-value store. */
interface KeyValueStore : KeyedObservable<String> {
@@ -80,6 +81,27 @@
fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
}
+/** Delegation of [KeyValueStore]. */
+interface KeyValueStoreDelegate : KeyValueStore, KeyedObservableDelegate<String> {
+
+ /** [KeyValueStore] to delegate. */
+ val keyValueStoreDelegate: KeyValueStore
+
+ override val keyedObservableDelegate
+ get() = keyValueStoreDelegate
+
+ override fun contains(key: String) = keyValueStoreDelegate.contains(key)
+
+ override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
+ keyValueStoreDelegate.getDefaultValue(key, valueType)
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+ keyValueStoreDelegate.getValue(key, valueType) ?: getDefaultValue(key, valueType)
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
+ keyValueStoreDelegate.setValue(key, valueType, value)
+}
+
/** [SharedPreferences] based [KeyValueStore]. */
interface SharedPreferencesKeyValueStore : KeyValueStore {
@@ -103,11 +125,11 @@
@Suppress("UNCHECKED_CAST")
override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ val edit = sharedPreferences.edit()
if (value == null) {
- sharedPreferences.edit().remove(key).apply()
+ edit.remove(key).apply()
return
}
- val edit = sharedPreferences.edit()
when (valueType) {
Boolean::class.javaObjectType -> edit.putBoolean(key, value as Boolean)
Float::class.javaObjectType -> edit.putFloat(key, value as Float)
@@ -115,7 +137,7 @@
Long::class.javaObjectType -> edit.putLong(key, value as Long)
String::class.javaObjectType -> edit.putString(key, value as String)
Set::class.javaObjectType -> edit.putStringSet(key, value as Set<String>)
- else -> {}
+ else -> Log.e(LOG_TAG, "Unsupported $valueType for $key: $value")
}
edit.apply()
}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 07b1c9e..ff58bf7 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -116,8 +116,28 @@
}
/** Delegation of [KeyedObservable]. */
-open class KeyedObservableDelegate<K>(delegate: KeyedObservable<K>) :
- KeyedObservable<K> by delegate
+interface KeyedObservableDelegate<K> : KeyedObservable<K> {
+
+ /** [KeyedObservable] to delegate. */
+ val keyedObservableDelegate: KeyedObservable<K>
+
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean =
+ keyedObservableDelegate.addObserver(observer, executor)
+
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean =
+ keyedObservableDelegate.addObserver(key, observer, executor)
+
+ override fun removeObserver(observer: KeyedObserver<K?>): Boolean =
+ keyedObservableDelegate.removeObserver(observer)
+
+ override fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean =
+ keyedObservableDelegate.removeObserver(key, observer)
+
+ override fun notifyChange(reason: Int): Unit = keyedObservableDelegate.notifyChange(reason)
+
+ override fun notifyChange(key: K, reason: Int): Unit =
+ keyedObservableDelegate.notifyChange(key, reason)
+}
/** A thread safe implementation of [KeyedObservable]. */
open class KeyedDataObservable<K> : KeyedObservable<K> {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
new file mode 100644
index 0000000..fdde3d3
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+
+/**
+ * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
+ *
+ * It provides methods to register/unregister listeners for setting changes, update the setting for
+ * specific device states, retrieve the setting value, and check if rotation is locked for specific
+ * or all device states.
+ */
+interface DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
+
+ /** Listener for changes in device-state based auto rotate setting. */
+ interface DeviceStateAutoRotateSettingListener {
+ /** Called whenever the setting has changed. */
+ fun onSettingsChanged()
+ }
+
+ /** Register listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun registerListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /** Unregister listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /**
+ * Write [deviceState]'s setting value as [autoRotate], for [DEVICE_STATE_ROTATION_LOCK] setting.
+ */
+ fun updateSetting(deviceState: Int, autoRotate: Boolean)
+
+ /** Get [DEVICE_STATE_ROTATION_LOCK] setting value for [deviceState]. */
+ fun getRotationLockSetting(deviceState: Int): Int
+
+ /** Returns true if auto-rotate setting is OFF for [deviceState]. */
+ fun isRotationLocked(deviceState: Int): Boolean
+
+ /** Returns true if the auto-rotate setting value for all device states is OFF. */
+ fun isRotationLockedForAllStates(): Boolean
+
+ /** Returns a list of device states and their respective auto rotate setting availability. */
+ fun getSettableDeviceStates(): List<SettableDeviceState>
+}
+
+/** Represents a device state and whether it has an auto-rotation setting. */
+data class SettableDeviceState(
+ /** Returns the device state associated with this object. */
+ val deviceState: Int,
+ /** Returns whether there is an auto-rotation setting for this device state. */
+ val isSettable: Boolean
+)
+
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
new file mode 100644
index 0000000..0b6c6e2
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.Log
+import android.util.SparseIntArray
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Implementation of [DeviceStateAutoRotateSettingManager]. This implementation is a part of
+ * refactoring, it should be used when [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]
+ * is enabled.
+ */
+class DeviceStateAutoRotateSettingManagerImpl(
+ context: Context,
+ backgroundExecutor: Executor,
+ private val secureSettings: SecureSettings,
+ private val mainHandler: Handler,
+ private val posturesHelper: PosturesHelper,
+) : DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.
+
+ private val settingListeners: MutableList<DeviceStateAutoRotateSettingListener> =
+ mutableListOf()
+ private val fallbackPostureMap = SparseIntArray()
+ private val settableDeviceState: MutableList<SettableDeviceState> = mutableListOf()
+
+ private val autoRotateSettingValue: String
+ get() = secureSettings.getStringForUser(DEVICE_STATE_ROTATION_LOCK, UserHandle.USER_CURRENT)
+
+ init {
+ loadAutoRotateDeviceStates(context)
+ val contentObserver =
+ object : ContentObserver(mainHandler) {
+ override fun onChange(selfChange: Boolean) = notifyListeners()
+ }
+ backgroundExecutor.execute {
+ secureSettings.registerContentObserver(
+ DEVICE_STATE_ROTATION_LOCK, false, contentObserver, UserHandle.USER_CURRENT
+ )
+ }
+ }
+
+ override fun registerListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ settingListeners.add(settingListener)
+ }
+
+ override fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ if (!settingListeners.remove(settingListener)) {
+ Log.w(TAG, "Attempting to unregister a listener hadn't been registered")
+ }
+ }
+
+ override fun getRotationLockSetting(deviceState: Int): Int {
+ val devicePosture = posturesHelper.deviceStateToPosture(deviceState)
+ val serializedSetting = autoRotateSettingValue
+ val autoRotateSetting = extractSettingForDevicePosture(devicePosture, serializedSetting)
+
+ // If the setting is ignored for this posture, check the fallback posture.
+ if (autoRotateSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ val fallbackPosture =
+ fallbackPostureMap.get(devicePosture, DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ return extractSettingForDevicePosture(fallbackPosture, serializedSetting)
+ }
+
+ return autoRotateSetting
+ }
+
+ override fun isRotationLocked(deviceState: Int) =
+ getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED
+
+ override fun isRotationLockedForAllStates(): Boolean =
+ convertSerializedSettingToMap(autoRotateSettingValue).all { (_, value) ->
+ value == DEVICE_STATE_ROTATION_LOCK_LOCKED
+ }
+
+ override fun getSettableDeviceStates(): List<SettableDeviceState> = settableDeviceState
+
+ override fun updateSetting(deviceState: Int, autoRotate: Boolean) {
+ // TODO: b/350946537 - Create IPC to update the setting, and call it here.
+ throw UnsupportedOperationException("API updateSetting is not implemented yet")
+ }
+
+ private fun notifyListeners() =
+ settingListeners.forEach { listener -> listener.onSettingsChanged() }
+
+ private fun loadAutoRotateDeviceStates(context: Context) {
+ val perDeviceStateAutoRotateDefaults =
+ context.resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ for (entry in perDeviceStateAutoRotateDefaults) {
+ entry.parsePostureEntry()?.let { (posture, autoRotate, fallbackPosture) ->
+ if (autoRotate == DEVICE_STATE_ROTATION_LOCK_IGNORED && fallbackPosture != null) {
+ fallbackPostureMap.put(posture, fallbackPosture)
+ }
+ settableDeviceState.add(
+ SettableDeviceState(posture, autoRotate != DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ )
+ }
+ }
+ }
+
+ private fun convertSerializedSettingToMap(serializedSetting: String): Map<Int, Int> {
+ if (serializedSetting.isEmpty()) return emptyMap()
+ return try {
+ serializedSetting
+ .split(SEPARATOR_REGEX)
+ .hasEvenSize()
+ .chunked(2)
+ .mapNotNull(::parsePostureSettingPair)
+ .toMap()
+ } catch (e: Exception) {
+ Log.w(
+ TAG,
+ "Invalid format in serializedSetting=$serializedSetting: ${e.message}"
+ )
+ return emptyMap()
+ }
+ }
+
+ private fun List<String>.hasEvenSize(): List<String> {
+ if (this.size % 2 != 0) {
+ throw IllegalStateException("Odd number of elements in the list")
+ }
+ return this
+ }
+
+ private fun parsePostureSettingPair(settingPair: List<String>): Pair<Int, Int>? {
+ return settingPair.let { (keyStr, valueStr) ->
+ val key = keyStr.toIntOrNull()
+ val value = valueStr.toIntOrNull()
+ if (key != null && value != null && value in 0..2) {
+ key to value
+ } else {
+ Log.w(TAG, "Invalid key or value in pair: $keyStr, $valueStr")
+ null // Invalid pair, skip it
+ }
+ }
+ }
+
+ private fun extractSettingForDevicePosture(
+ devicePosture: Int,
+ serializedSetting: String
+ ): Int =
+ convertSerializedSettingToMap(serializedSetting)[devicePosture]
+ ?: DEVICE_STATE_ROTATION_LOCK_IGNORED
+
+ private fun String.parsePostureEntry(): Triple<Int, Int, Int?>? {
+ val values = split(SEPARATOR_REGEX)
+ if (values.size !in 2..3) { // It should contain 2 or 3 values.
+ Log.w(TAG, "Invalid number of values in entry: '$this'")
+ return null
+ }
+ return try {
+ val posture = values[0].toInt()
+ val rotationLockSetting = values[1].toInt()
+ val fallbackPosture = if (values.size == 3) values[2].toIntOrNull() else null
+ Triple(posture, rotationLockSetting, fallbackPosture)
+ } catch (e: NumberFormatException) {
+ Log.w(TAG, "Invalid number format in '$this': ${e.message}")
+ null
+ }
+ }
+
+ companion object {
+ private const val TAG = "DeviceStateAutoRotate"
+ private const val SEPARATOR_REGEX = ":"
+ }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
new file mode 100644
index 0000000..4d1d292
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DeviceStateAutoRotateSettingUtils")
+
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import com.android.internal.R
+
+/** Returns true if device-state based rotation lock settings are enabled. */
+object DeviceStateAutoRotateSettingUtils {
+ @JvmStatic
+ fun isDeviceStateRotationLockEnabled(context: Context) =
+ context.resources
+ .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ .isNotEmpty()
+}
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 635f690..deeba57 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,6 +20,8 @@
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
+
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -43,7 +45,6 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
/**
@@ -58,7 +59,7 @@
private static DeviceStateRotationLockSettingsManager sSingleton;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+ private final Set<DeviceStateAutoRotateSettingListener> mListeners = new HashSet<>();
private final SecureSettings mSecureSettings;
private final PosturesHelper mPosturesHelper;
private String[] mPostureRotationLockDefaults;
@@ -127,20 +128,20 @@
}
/**
- * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+ * Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
* change. Can be called multiple times with different listeners.
*/
- public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+ public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
mListeners.add(runnable);
}
/**
- * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+ * Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
* was never registered.
*/
public void unregisterListener(
- DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
- if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+ DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+ if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
}
}
@@ -379,56 +380,8 @@
}
private void notifyListeners() {
- for (DeviceStateRotationLockSettingsListener r : mListeners) {
+ for (DeviceStateAutoRotateSettingListener r : mListeners) {
r.onSettingsChanged();
}
}
-
- /** Listener for changes in device-state based rotation lock settings */
- public interface DeviceStateRotationLockSettingsListener {
- /** Called whenever the settings have changed. */
- void onSettingsChanged();
- }
-
- /** Represents a device state and whether it has an auto-rotation setting. */
- public static class SettableDeviceState {
- private final int mDeviceState;
- private final boolean mIsSettable;
-
- SettableDeviceState(int deviceState, boolean isSettable) {
- mDeviceState = deviceState;
- mIsSettable = isSettable;
- }
-
- /** Returns the device state associated with this object. */
- public int getDeviceState() {
- return mDeviceState;
- }
-
- /** Returns whether there is an auto-rotation setting for this device state. */
- public boolean isSettable() {
- return mIsSettable;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof SettableDeviceState)) return false;
- SettableDeviceState that = (SettableDeviceState) o;
- return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mDeviceState, mIsSettable);
- }
-
- @Override
- public String toString() {
- return "SettableDeviceState{"
- + "mDeviceState=" + mDeviceState
- + ", mIsSettable=" + mIsSettable
- + '}';
- }
- }
}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
index 1052873..ea40e14 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
@@ -19,7 +19,7 @@
import android.database.ContentObserver;
/** Minimal wrapper interface around {@link android.provider.Settings.Secure} for easier testing. */
-interface SecureSettings {
+public interface SecureSettings {
void putStringForUser(String name, String value, int userHandle);
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 7bd4b3f..9d4c5c2 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -113,8 +113,6 @@
mSwitch.setOnCheckedChangeListener(this);
}
- setChecked(mSwitch.isChecked());
-
if (attrs != null) {
final TypedArray a = context.obtainStyledAttributes(attrs,
androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
@@ -130,8 +128,6 @@
}
a.recycle();
}
-
- setBackground(mSwitch.isChecked());
}
@Override
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
index 686c148..112a69b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
@@ -262,4 +262,30 @@
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
+
+ <style name="SettingsLibEntityHeaderContent">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_centerHorizontal">true</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:gravity">center_horizontal</item>
+ </style>
+
+ <style name="SettingsLibEntityHeaderIcon">
+ <item name="android:layout_width">@dimen/settingslib_expressive_space_large3</item>
+ <item name="android:layout_height">@dimen/settingslib_expressive_space_large3</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:antialias">true</item>
+ </style>
+
+ <style name="SettingsLibEntityHeaderTitle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginTop">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:singleLine">false</item>
+ <item name="android:gravity">center</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:textDirection">locale</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleLarge.Emphasized</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index fe8e8b6..6d02c5d 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -16,6 +16,8 @@
package com.android.settingslib.widget;
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -46,6 +48,9 @@
*/
public class SliderPreference extends Preference {
private static final String TAG = "SliderPreference";
+ public static final int HAPTIC_FEEDBACK_MODE_NONE = 0;
+ public static final int HAPTIC_FEEDBACK_MODE_ON_TICKS = 1;
+ public static final int HAPTIC_FEEDBACK_MODE_ON_ENDS = 2;
private final int mTextStartId;
private final int mTextEndId;
@@ -71,6 +76,8 @@
private int mMin;
private int mMax;
private int mSliderIncrement;
+ private int mHapticFeedbackMode = HAPTIC_FEEDBACK_MODE_NONE;
+ private boolean mTickVisible = false;
private boolean mAdjustable;
private boolean mTrackingTouch;
private CharSequence mSliderContentDescription;
@@ -265,6 +272,7 @@
}
if (mSliderIncrement != 0) {
mSlider.setStepSize(mSliderIncrement);
+ mSlider.setTickVisible(mTickVisible);
} else {
mSliderIncrement = (int) (mSlider.getStepSize());
}
@@ -442,6 +450,29 @@
}
/**
+ * Sets the haptic feedback mode. HAPTIC_FEEDBACK_MODE_ON_TICKS means to perform haptic feedback
+ * as the {@link Slider} value is updated; HAPTIC_FEEDBACK_MODE_ON_ENDS means to perform haptic
+ * feedback as the {@link Slider} value is equal to the min/max value.
+ *
+ * @param hapticFeedbackMode The haptic feedback mode.
+ */
+ public void setHapticFeedbackMode(int hapticFeedbackMode) {
+ mHapticFeedbackMode = hapticFeedbackMode;
+ }
+
+ /**
+ * Sets whether the tick marks are visible. Only used when the slider is in discrete mode.
+ *
+ * @param tickVisible The visibility of tick marks.
+ */
+ public void setTickVisible(boolean tickVisible) {
+ if (tickVisible != mTickVisible) {
+ mTickVisible = tickVisible;
+ notifyChanged();
+ }
+ }
+
+ /**
* Gets whether the current {@link Slider} value is displayed to the user.
*
* @return Whether the current {@link Slider} value is displayed to the user
@@ -519,7 +550,16 @@
if (sliderValue != mSliderValue) {
if (callChangeListener(sliderValue)) {
setValueInternal(sliderValue, false);
- // TODO: mHapticFeedbackMode
+ switch (mHapticFeedbackMode) {
+ case HAPTIC_FEEDBACK_MODE_ON_TICKS:
+ slider.performHapticFeedback(CLOCK_TICK);
+ break;
+ case HAPTIC_FEEDBACK_MODE_ON_ENDS:
+ if (mSliderValue == mMax || mSliderValue == mMin) {
+ slider.performHapticFeedback(CLOCK_TICK);
+ }
+ break;
+ }
} else {
slider.setValue(mSliderValue);
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 03cb1ff..1297aa3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1510,6 +1510,9 @@
<!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
<string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string>
+ <!-- Warning message when the bluetooth key is missing. [CHAR_LIMIT=NONE] -->
+ <string name="bluetooth_key_missing_subtext">Can’t connect</string>
+
<!-- Name of the 3.5mm audio device. [CHAR LIMIT=40] -->
<string name="media_transfer_wired_device_name">Wired audio device</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 9d97901..bf6006b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -151,6 +151,18 @@
UserHandle.myUserId());
}
+ /**
+ * Configures the user restriction that this preference will track. This is equivalent to
+ * specifying {@link R.styleable#RestrictedPreference_userRestriction} in XML and allows
+ * configuring user restriction at runtime.
+ */
+ public void setUserRestriction(@Nullable String userRestriction) {
+ mAttrUserRestriction = userRestriction == null ||
+ RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, userRestriction,
+ UserHandle.myUserId()) ? null : userRestriction;
+ setDisabledByAdmin(checkRestrictionEnforced());
+ }
+
public void useAdminDisabledSummary(boolean useSummary) {
mDisabledSummary = useSummary;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 2fdf2ac..ae9ad95 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -703,18 +703,18 @@
// However, app layer need to gate the feature based on whether the device has audio
// sharing capability regardless of the BT state.
// So here we check the BluetoothProperties when BT off.
- String mode = BluetoothProperties.le_audio_dynamic_switcher_mode().orElse("none");
- Set<String> disabledModes = ImmutableSet.of("disabled", "unicast");
+ //
+ // TODO: Also check SystemProperties "persist.bluetooth.leaudio_dynamic_switcher.mode"
+ // and return true if it is in broadcast mode.
+ // Now SystemUI don't have access to read the value.
int sourceSupportedCode = adapter.isLeAudioBroadcastSourceSupported();
int assistantSupportedCode = adapter.isLeAudioBroadcastAssistantSupported();
return (sourceSupportedCode == BluetoothStatusCodes.FEATURE_SUPPORTED
|| (sourceSupportedCode == BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED
- && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)
- && !disabledModes.contains(mode)))
+ && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false)))
&& (assistantSupportedCode == BluetoothStatusCodes.FEATURE_SUPPORTED
|| (assistantSupportedCode == BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED
- && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false)
- && !disabledModes.contains(mode)));
+ && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false)));
} catch (IllegalStateException e) {
Log.d(TAG, "Fail to check isAudioSharingSupported, e = ", e);
return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 5f88bcd..011b2fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1495,6 +1495,11 @@
int leftBattery = -1;
int rightBattery = -1;
+ Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(mDevice);
+ if (keyMissingCount != null && keyMissingCount > 0) {
+ return mContext.getString(R.string.bluetooth_key_missing_subtext);
+ }
+
if (isProfileConnectedFail() && isConnected()) {
return mContext.getString(R.string.profile_connect_timeout_subtext);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index f22bdaf..01d86942 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -724,7 +724,10 @@
Log.d(TAG, "The BluetoothLeBroadcast is null");
return null;
}
- if (mBluetoothLeBroadcastMetadata == null) {
+ if (mBluetoothLeBroadcastMetadata == null
+ // mBroadcastId is updated when onBroadcastStarted, which is always before
+ // onBroadcastMetadataChanged, so mBroadcastId is always the latest broadcast info
+ || mBluetoothLeBroadcastMetadata.getBroadcastId() != mBroadcastId) {
final List<BluetoothLeBroadcastMetadata> metadataList =
mServiceBroadcast.getAllBroadcastMetadata();
mBluetoothLeBroadcastMetadata =
@@ -732,6 +735,7 @@
.filter(i -> i.getBroadcastId() == mBroadcastId)
.findFirst()
.orElse(null);
+ Log.d(TAG, "getLatestBluetoothLeBroadcastMetadata for broadcast id " + mBroadcastId);
}
return mBluetoothLeBroadcastMetadata;
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
new file mode 100644
index 0000000..78dba57
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerImplTest {
+ @get:Rule
+ val rule = MockitoJUnit.rule()
+
+ private val fakeSecureSettings = FakeSecureSettings()
+ private val executor: Executor = Executor { it.run() }
+ private val configPerDeviceStateRotationLockDefaults = arrayOf(
+ "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ )
+
+ @Mock
+ private lateinit var mockContext: Context
+
+ @Mock
+ private lateinit var mockContentResolver: ContentResolver
+
+ @Mock
+ private lateinit var mockPosturesHelper: PosturesHelper
+
+ @Mock
+ private lateinit var mockHandler: Handler
+
+ @Mock
+ private lateinit var mockDeviceStateManager: DeviceStateManager
+
+ @Mock
+ private lateinit var mockResources: Resources
+ private lateinit var settingManager: DeviceStateAutoRotateSettingManagerImpl
+
+ @Before
+ fun setUp() {
+ whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+ whenever(mockContext.resources).thenReturn(mockResources)
+ whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(configPerDeviceStateRotationLockDefaults)
+ whenever(mockHandler.post(any(Runnable::class.java))).thenAnswer { invocation ->
+ val runnable = invocation.arguments[0] as Runnable
+ runnable.run()
+ null
+ }
+ whenever(mockContext.getSystemService(DeviceStateManager::class.java))
+ .thenReturn(mockDeviceStateManager)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_UNFOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_INVALID))
+ .thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mockPosturesHelper,
+ )
+ }
+
+ @Test
+ fun registerListener_onSettingsChanged_listenerNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener).onSettingsChanged()
+ }
+
+ @Test
+ fun registerMultipleListeners_onSettingsChanged_allListenersNotified() {
+ val listener1 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ val listener2 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener1)
+ settingManager.registerListener(listener2)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener1).onSettingsChanged()
+ verify(listener2).onSettingsChanged()
+ }
+
+ @Test
+ fun unregisterListener_onSettingsChanged_listenerNotNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+ settingManager.unregisterListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener, never()).onSettingsChanged()
+ }
+
+ @Test
+ fun getAutoRotateSetting_offForUnfolded_returnsOff() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_onForFolded_returnsOn() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPostureWithNoFallback_returnsIgnored() {
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_INVALID)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidFormat_returnsIgnored() {
+ persistSettings("invalid_format")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidNumberFormat_returnsIgnored() {
+ persistSettings("$DEVICE_STATE_ROTATION_KEY_FOLDED:4")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_multipleSettings_returnsCorrectSetting() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val foldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+ val unfoldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(foldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ assertThat(unfoldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun isAutoRotateOff_offForUnfolded_returnsTrue() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val isAutoRotateOff = settingManager.isRotationLocked(DEVICE_STATE_UNFOLDED)
+
+ assertThat(isAutoRotateOff).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_allStatesLocked_returnsTrue() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_someStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_noStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+ val settableDeviceStates = settingManager.getSettableDeviceStates()
+
+ assertThat(settableDeviceStates)
+ .containsExactly(
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
+ )
+ }
+
+ private fun persistSettings(devicePosture: Int, autoRotateSetting: Int) {
+ persistSettings("$devicePosture:$autoRotateSetting")
+ }
+
+ private fun persistSettings(value: String) {
+ fakeSecureSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK, value, UserHandle.USER_CURRENT
+ )
+ }
+
+ private companion object {
+ const val DEVICE_STATE_FOLDED = 0
+ const val DEVICE_STATE_HALF_FOLDED = 1
+ const val DEVICE_STATE_UNFOLDED = 2
+ const val DEVICE_STATE_REAR_DISPLAY = 3
+ const val DEVICE_STATE_INVALID = 4
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 9f9aaf5..baebaf7 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -40,7 +40,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
import com.google.common.truth.Expect;
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index fc61b1e..d3291b4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -118,6 +118,8 @@
Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
Settings.Global.Wearable.AUTO_BEDTIME_MODE,
+ Settings.Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE,
+ Settings.Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE,
Settings.Global.FORCE_ENABLE_PSS_PROFILING,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 0121d31..829d4cb 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -300,5 +300,9 @@
Settings.Secure.DUAL_SHADE,
Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED,
Settings.Secure.SEARCH_CONTENT_FILTERS_ENABLED,
+ Settings.Secure.SPELL_CHECKER_ENABLED,
+ Settings.Secure.SELECTED_SPELL_CHECKER,
+ // SELECTED_SPELL_CHECKER_SUBTYPE needs to be restored after SELECTED_SPELL_CHECKER
+ Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index cf0447f..98f5fac 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -124,7 +124,8 @@
Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
Settings.System.NOTIFICATION_COOLDOWN_ALL,
Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
- Settings.System.PREFERRED_REGION
+ Settings.System.PREFERRED_REGION,
+ Settings.System.CV_ENABLED
));
if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
settings.add(Settings.System.PEAK_REFRESH_RATE);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 4c6a1ba..cd6521f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -474,5 +474,7 @@
String.valueOf(
Global.Wearable.STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN)
}));
+ VALIDATORS.put(Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 5eb6af6..d0f8462 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -472,5 +472,8 @@
VALIDATORS.put(Secure.DUAL_SHADE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.BROWSER_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SEARCH_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SPELL_CHECKER_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER, NULLABLE_COMPONENT_NAME_VALIDATOR);
+ VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER_SUBTYPE, ANY_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4f649ed..3a58440 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -271,5 +271,7 @@
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.PREFERRED_REGION, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(System.CV_ENABLED,
+ new InclusiveIntegerRangeValidator(0, 1));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
index b0086c1..78c87b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -1,2 +1,2 @@
-per-file WritableNamespacePrefixes.java = mpgroover@google.com,tedbauer@google.com
-per-file WritableNamespaces.java = mpgroover@google.com,tedbauer@google.com
+per-file WritableNamespacePrefixes.java = mpgroover@google.com
+per-file WritableNamespaces.java = mpgroover@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index fc402d4..41ec621 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -37,10 +37,12 @@
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.database.Cursor;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
@@ -941,6 +943,7 @@
Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
int restoredSettingsCount = 0;
+ boolean selectedSpellCheckerRestored = false;
for (String key : allowlist.mSettingsAllowlist) {
boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) {
@@ -1068,6 +1071,25 @@
}
continue;
}
+ } else if (Settings.Secure.SELECTED_SPELL_CHECKER.equals(key)) {
+ ServiceInfo si = getServiceInfoOrNull(value);
+ if (si == null || si.applicationInfo == null) {
+ Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+ + "as it is not installed");
+ continue;
+ } else if (!si.applicationInfo.isSystemApp()
+ && !si.applicationInfo.isUpdatedSystemApp()) {
+ Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+ + "as it is not a system app");
+ continue;
+ }
+ selectedSpellCheckerRestored = true;
+ } else if (Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE.equals(key)) {
+ if (!selectedSpellCheckerRestored) {
+ Log.i(TAG, "Skipping restore for setting selected_spell_checker_subtype "
+ + "as selected_spell_checker was not restored");
+ continue;
+ }
}
if (Settings.System.FONT_SCALE.equals(key)) {
@@ -1868,6 +1890,18 @@
return result;
}
+ @Nullable
+ private ServiceInfo getServiceInfoOrNull(@Nullable String flattenedServiceName) {
+ if (flattenedServiceName == null) return null;
+ ComponentName componentName = ComponentName.unflattenFromString(flattenedServiceName);
+ if (componentName == null) return null;
+ try {
+ return getBaseContext().getPackageManager().getServiceInfo(componentName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
/**
* Store the allowlist of settings to be backed up and validators for them.
*/
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e7527dc..584b21a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -3157,6 +3157,12 @@
SystemSettingsProto.Volume.MASTER_BALANCE);
p.end(volumeToken);
+ final long systemDisplayToken = p.start(SystemSettingsProto.DISPLAY);
+ dumpSetting(s, p,
+ Settings.System.CV_ENABLED,
+ SystemSettingsProto.Display.CV_ENABLED);
+ p.end(systemDisplayToken);
+
dumpSetting(s, p,
Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 70c042c..3148f22 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -749,15 +749,12 @@
Settings.Secure.SECURE_FRP_MODE,
Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
- Settings.Secure.SELECTED_SPELL_CHECKER, // Intentionally removed in Q
- Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, // Intentionally removed in Q
Settings.Secure.SETTINGS_CLASSNAME,
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
Settings.Secure.SLEEP_TIMEOUT,
Settings.Secure.SMS_DEFAULT_APPLICATION,
- Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q
Settings.Secure.TRUST_AGENTS_INITIALIZED,
Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED,
Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index fb0678f..5bba99f 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -255,11 +255,11 @@
/** Always keep remote bugreport files created in the last day. */
private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
- /** Minimum delay for sending last update notification */
- private static final int DELAY_NOTIFICATION_MS = 250;
-
private final Object mLock = new Object();
+/** Minimum delay between percentage points before sending an update notification */
+ private static final int MIN_NOTIFICATION_GAP = 10;
+
/** Managed bugreport info (keyed by id) */
@GuardedBy("mLock")
private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
@@ -1460,17 +1460,6 @@
* Sends a notification indicating the bugreport has finished so use can share it.
*/
private void sendBugreportNotification(BugreportInfo info, boolean takingScreenshot) {
-
- final long lastUpdate = System.currentTimeMillis() - info.lastUpdate.longValue();
- if (lastUpdate < DELAY_NOTIFICATION_MS) {
- Log.d(TAG, "Delaying final notification for "
- + (DELAY_NOTIFICATION_MS - lastUpdate) + " ms ");
- mMainThreadHandler.postDelayed(() -> {
- sendBugreportNotification(info, takingScreenshot);
- }, DELAY_NOTIFICATION_MS - lastUpdate);
- return;
- }
-
// Since adding the details can take a while, do it before notifying user.
addDetailsToZipFile(info);
@@ -1523,7 +1512,7 @@
builder.setSubText(info.getName());
}
- Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+ Log.d(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
NotificationManager.from(mContext).notify(info.id, builder.build());
}
@@ -2753,6 +2742,11 @@
if (progress > CAPPED_PROGRESS) {
progress = CAPPED_PROGRESS;
}
+
+ if ((progress - info.lastProgress.intValue()) < MIN_NOTIFICATION_GAP) {
+ return;
+ }
+
if (DEBUG) {
if (progress != info.progress.intValue()) {
Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5b48566..129949f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -534,6 +534,7 @@
"androidx.compose.animation_animation-graphics",
"androidx.lifecycle_lifecycle-viewmodel-compose",
"kairos",
+ "displaylib",
"aconfig_settings_flags_lib",
],
libs: [
@@ -728,6 +729,7 @@
"Traceur-res",
"aconfig_settings_flags_lib",
"kairos",
+ "displaylib",
],
}
@@ -770,6 +772,7 @@
"androidx.compose.runtime_runtime",
"kairos",
"kosmos",
+ "displaylib",
"testables",
"androidx.test.rules",
"platform-compat-test-rules",
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 7172619..1543dbe 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
@@ -46,7 +46,7 @@
import android.media.AudioManager;
import android.os.PowerManager;
import android.os.UserManager;
-import android.platform.uiautomator_helpers.WaitUtils;
+import android.platform.uiautomatorhelpers.WaitUtils;
import android.provider.Settings;
import android.util.Log;
import android.view.Display;
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 3cb3025..c6bc1c7 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -155,3 +155,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "hearing_devices_input_routing_ui_improvement"
+ namespace: "accessibility"
+ description: "UI improvement for hearing device input routing feature"
+ bug: "397314200"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index ab18612..4693377 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -213,18 +213,6 @@
}
flag {
- name: "notification_undo_guts_on_config_changed"
- namespace: "systemui"
- description: "Fixes a bug where a theme or font change while notification guts were open"
- " (e.g. the snooze options or notification info) would show an empty notification by"
- " closing the guts and undoing changes."
- bug: "379267630"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index 694fc8e..ca94482 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -261,7 +262,8 @@
SurfaceControl.Transaction startTransaction
) {
checkArgument(isOpeningMode(launcherChange.getMode()));
- if (!isClosingType(info.getType())) {
+ if (!isClosingType(info.getType())
+ && !ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX.isTrue()) {
return;
}
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index a352b1e..82e5f5b 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -21,7 +21,6 @@
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -62,6 +61,7 @@
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.graphics.rememberGraphicsLayer
@@ -82,6 +82,7 @@
import androidx.lifecycle.setViewTreeViewModelStoreOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.compose.modifiers.animatedBackground
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.FullScreenComposeViewInOverlay
import com.android.systemui.animation.ComposableControllerFactory
@@ -291,7 +292,7 @@
.updateExpandableSize()
.then(minInteractiveSizeModifier)
.then(clickModifier(controller, onClick, interactionSource))
- .background(color, shape)
+ .animatedBackground(color, shape = shape)
.border(controller)
.onGloballyPositioned { controller.boundsInComposeViewRoot = it.boundsInRoot() }
) {
@@ -307,19 +308,27 @@
contentColor: Color,
content: @Composable (Expandable) -> Unit,
) {
- CompositionLocalProvider(LocalContentColor provides contentColor) {
- // We make sure that the content itself (wrapped by the background) is at least 40.dp, which
- // is the same as the M3 buttons. This applies even if onClick is null, to make it easier to
- // write expandables that are sometimes clickable and sometimes not. There shouldn't be any
- // Expandable smaller than 40dp because if the expandable is not clickable directly, then
- // something in its content should be (and with a size >= 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
- ) {
- content(expandable)
+ val minSizeContent =
+ @Composable {
+ // We make sure that the content itself (wrapped by the background) is at least 40.dp,
+ // which is the same as the M3 buttons. This applies even if onClick is null, to make it
+ // easier to write expandables that are sometimes clickable and sometimes not. There
+ // shouldn't be any Expandable smaller than 40dp because if the expandable is not
+ // clickable directly, then something in its content should be (and with a size >=
+ // 40dp).
+ val minSize = 40.dp
+ Box(
+ Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
}
+
+ if (contentColor.isSpecified) {
+ CompositionLocalProvider(LocalContentColor provides contentColor, content = minSizeContent)
+ } else {
+ minSizeContent()
}
}
@@ -345,7 +354,7 @@
.thenIf(drawContent) {
Modifier.border(controller)
.then(clickModifier(controller, onClick, interactionSource))
- .background(controller.color, controller.shape)
+ .animatedBackground(controller.color, shape = controller.shape)
}
.onPlaced { controller.boundsInComposeViewRoot = it.boundsInRoot() }
.drawWithContent {
@@ -422,7 +431,7 @@
// Background.
this@draw.drawBackground(
state,
- controller.color,
+ controller.color(),
controller.borderStroke,
size = Size(state.width.toFloat(), state.height.toFloat()),
)
@@ -469,7 +478,7 @@
/** Draw [content] in [overlay] while respecting its screen position given by [animatorState]. */
@Composable
private fun AnimatedContentInOverlay(
- color: Color,
+ color: () -> Color,
sizeInOriginalLayout: Size,
overlay: ViewGroupOverlay,
controller: ExpandableControllerImpl,
@@ -523,7 +532,7 @@
return@drawWithContent
}
- drawBackground(animatorState, color, controller.borderStroke)
+ drawBackground(animatorState, color(), controller.borderStroke)
drawContent()
},
// We center the content in the expanding container.
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 72da175e..d7d5a48 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -26,7 +26,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -80,6 +79,24 @@
borderStroke: BorderStroke? = null,
transitionControllerFactory: ComposableControllerFactory? = null,
): ExpandableController {
+ return rememberExpandableController(
+ color = { color },
+ shape = shape,
+ contentColor = contentColor,
+ borderStroke = borderStroke,
+ transitionControllerFactory = transitionControllerFactory,
+ )
+}
+
+/** Create an [ExpandableController] to control an [Expandable]. */
+@Composable
+fun rememberExpandableController(
+ color: () -> Color,
+ shape: Shape,
+ contentColor: Color = Color.Unspecified,
+ borderStroke: BorderStroke? = null,
+ transitionControllerFactory: ComposableControllerFactory? = null,
+): ExpandableController {
val composeViewRoot = LocalView.current
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
@@ -125,7 +142,7 @@
}
internal class ExpandableControllerImpl(
- internal val color: Color,
+ internal val color: () -> Color,
internal val contentColor: Color,
internal val shape: Shape,
internal val borderStroke: BorderStroke?,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 959f28f..2ea9c48 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -95,13 +95,31 @@
* nested scrollable.
*
* This is called whenever a nested scrollable does not consume some scroll amount. If this
- * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
+ * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
* consume all future events during preScroll until the nested scroll is finished.
*/
- fun shouldConsumeNestedScroll(sign: Float): Boolean
+ fun shouldConsumeNestedPostScroll(sign: Float): Boolean = true
+
+ /**
+ * Whether this draggable should consume any scroll amount with the given [sign] *before* it can
+ * be consumed by a nested scrollable.
+ *
+ * This is called before a nested scrollable is able to consume that scroll amount. If this
+ * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
+ * consume all future scroll events during preScroll until the nested scroll is finished.
+ */
+ fun shouldConsumeNestedPreScroll(sign: Float): Boolean = false
interface Controller {
/**
+ * Whether drags that were started from nested scrolls should be automatically
+ * [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to
+ * [onDrag].
+ */
+ val autoStopNestedDrags: Boolean
+ get() = false
+
+ /**
* Drag by [delta] pixels.
*
* @return the consumed [delta]. Any non-consumed delta will be dispatched to the next
@@ -540,6 +558,14 @@
}
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val sign = available.toFloat().sign
+ maybeCreateNewController(
+ sign = sign,
+ condition = {
+ source == NestedScrollSource.UserInput &&
+ draggable.shouldConsumeNestedPreScroll(sign)
+ },
+ )
val controller = nestedScrollController ?: return Offset.Zero
return scrollWithOverscroll(controller, available)
}
@@ -560,33 +586,46 @@
}
val sign = offset.sign
- if (
- nestedDragsEnabled &&
- nestedScrollController == null &&
- // TODO(b/388231324): Remove this.
- !lastEventWasScrollWheel &&
- draggable.shouldConsumeNestedScroll(sign) &&
- lastFirstDown != null
- ) {
- val startedPosition = checkNotNull(lastFirstDown)
-
- // TODO(b/382665591): Ensure that there is at least one pointer down.
- val pointersDownCount = pointersDown.size.coerceAtLeast(1)
- val pointerType = pointersDown.entries.firstOrNull()?.value
- nestedScrollController =
- NestedScrollController(
- overscrollEffect,
- draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
- )
- }
-
+ maybeCreateNewController(
+ sign,
+ condition = { draggable.shouldConsumeNestedPostScroll(sign) },
+ )
val controller = nestedScrollController ?: return Offset.Zero
return scrollWithOverscroll(controller, available)
}
+ private fun maybeCreateNewController(sign: Float, condition: () -> Boolean) {
+ if (
+ !nestedDragsEnabled ||
+ nestedScrollController != null ||
+ lastEventWasScrollWheel ||
+ lastFirstDown == null ||
+ !condition()
+ ) {
+ return
+ }
+
+ // TODO(b/382665591): Ensure that there is at least one pointer down.
+ val pointersDownCount = pointersDown.size.coerceAtLeast(1)
+ val pointerType = pointersDown.entries.firstOrNull()?.value
+ val startedPosition = checkNotNull(lastFirstDown)
+ nestedScrollController =
+ NestedScrollController(
+ overscrollEffect,
+ draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
+ )
+ }
+
private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
- return scrollWithOverscroll(offset) {
- controller.controller.onDrag(it.toFloat()).toOffset()
+ return scrollWithOverscroll(offset) { delta ->
+ val available = delta.toFloat()
+ val consumed = controller.controller.onDrag(available)
+ if (controller.controller.autoStopNestedDrags && consumed != available) {
+ controller.ensureOnDragStoppedIsCalled()
+ this.nestedScrollController = null
+ }
+
+ consumed.toOffset()
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
new file mode 100644
index 0000000..5b1f0a7
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawOutline
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverModifierNode
+import androidx.compose.ui.node.invalidateDraw
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.LayoutDirection
+
+/**
+ * Draws a background in a given [shape] and with a [color] or [alpha] that can be animated.
+ *
+ * @param color color to paint background with
+ * @param alpha alpha of the background
+ * @param shape desired shape of the background
+ */
+fun Modifier.animatedBackground(
+ color: () -> Color,
+ alpha: () -> Float = DefaultAlpha,
+ shape: Shape = RectangleShape,
+) =
+ this.then(
+ BackgroundElement(
+ color = color,
+ alpha = alpha,
+ shape = shape,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "background"
+ value = color
+ properties["color"] = color
+ properties["alpha"] = alpha
+ properties["shape"] = shape
+ },
+ )
+ )
+
+private val DefaultAlpha = { 1f }
+
+private class BackgroundElement(
+ private val color: () -> Color,
+ private val alpha: () -> Float,
+ private val shape: Shape,
+ private val inspectorInfo: InspectorInfo.() -> Unit,
+) : ModifierNodeElement<BackgroundNode>() {
+ override fun create(): BackgroundNode {
+ return BackgroundNode(color, alpha, shape)
+ }
+
+ override fun update(node: BackgroundNode) {
+ node.color = color
+ node.alpha = alpha
+ node.shape = shape
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ inspectorInfo()
+ }
+
+ override fun hashCode(): Int {
+ var result = color.hashCode()
+ result = 31 * result + alpha.hashCode()
+ result = 31 * result + shape.hashCode()
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ val otherModifier = other as? BackgroundElement ?: return false
+ return color == otherModifier.color &&
+ alpha == otherModifier.alpha &&
+ shape == otherModifier.shape
+ }
+}
+
+private class BackgroundNode(var color: () -> Color, var alpha: () -> Float, var shape: Shape) :
+ DrawModifierNode, Modifier.Node(), ObserverModifierNode {
+
+ // Naively cache outline calculation if input parameters are the same, we manually observe
+ // reads inside shape#createOutline separately
+ private var lastSize: Size = Size.Unspecified
+ private var lastLayoutDirection: LayoutDirection? = null
+ private var lastOutline: Outline? = null
+ private var lastShape: Shape? = null
+ private var tmpOutline: Outline? = null
+
+ override fun ContentDrawScope.draw() {
+ if (shape === RectangleShape) {
+ // shortcut to avoid Outline calculation and allocation
+ drawRect()
+ } else {
+ drawOutline()
+ }
+ drawContent()
+ }
+
+ override fun onObservedReadsChanged() {
+ // Reset cached properties
+ lastSize = Size.Unspecified
+ lastLayoutDirection = null
+ lastOutline = null
+ lastShape = null
+ // Invalidate draw so we build the cache again - this is needed because observeReads within
+ // the draw scope obscures the state reads from the draw scope's observer
+ invalidateDraw()
+ }
+
+ private fun ContentDrawScope.drawRect() {
+ drawRect(color = color(), alpha = alpha())
+ }
+
+ private fun ContentDrawScope.drawOutline() {
+ val outline = getOutline()
+ drawOutline(outline, color = color(), alpha = alpha())
+ }
+
+ private fun ContentDrawScope.getOutline(): Outline {
+ val outline: Outline?
+ if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) {
+ outline = lastOutline!!
+ } else {
+ // Manually observe reads so we can directly invalidate the outline when it changes
+ // Use tmpOutline to avoid creating an object reference to local var outline
+ observeReads { tmpOutline = shape.createOutline(size, layoutDirection, this) }
+ outline = tmpOutline
+ tmpOutline = null
+ }
+ lastOutline = outline
+ lastSize = size
+ lastLayoutDirection = layoutDirection
+ lastShape = shape
+ return outline!!
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
deleted file mode 100644
index 794b7a4..0000000
--- a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compose.modifiers
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.DrawModifier
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawOutline
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.LayoutDirection
-
-/**
- * Draws a fading [shape] with a solid [color] and [alpha] behind the content.
- *
- * @param color color to paint background with
- * @param alpha alpha of the background
- * @param shape desired shape of the background
- */
-fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) =
- this.then(
- FadingBackground(
- brush = SolidColor(color),
- alpha = alpha,
- shape = shape,
- inspectorInfo =
- debugInspectorInfo {
- name = "background"
- value = color
- properties["color"] = color
- properties["alpha"] = alpha
- properties["shape"] = shape
- },
- )
- )
-
-private class FadingBackground
-constructor(
- private val brush: Brush,
- private val shape: Shape,
- private val alpha: () -> Float,
- inspectorInfo: InspectorInfo.() -> Unit,
-) : DrawModifier, InspectorValueInfo(inspectorInfo) {
- // naive cache outline calculation if size is the same
- private var lastSize: Size? = null
- private var lastLayoutDirection: LayoutDirection? = null
- private var lastOutline: Outline? = null
-
- override fun ContentDrawScope.draw() {
- if (shape === RectangleShape) {
- // shortcut to avoid Outline calculation and allocation
- drawRect()
- } else {
- drawOutline()
- }
- drawContent()
- }
-
- private fun ContentDrawScope.drawRect() {
- drawRect(brush, alpha = alpha())
- }
-
- private fun ContentDrawScope.drawOutline() {
- val outline =
- if (size == lastSize && layoutDirection == lastLayoutDirection) {
- lastOutline!!
- } else {
- shape.createOutline(size, layoutDirection, this)
- }
- drawOutline(outline, brush = brush, alpha = alpha())
- lastOutline = outline
- lastSize = size
- lastLayoutDirection = layoutDirection
- }
-
- override fun hashCode(): Int {
- var result = brush.hashCode()
- result = 31 * result + alpha.hashCode()
- result = 31 * result + shape.hashCode()
- return result
- }
-
- override fun equals(other: Any?): Boolean {
- val otherModifier = other as? FadingBackground ?: return false
- return brush == otherModifier.brush &&
- alpha == otherModifier.alpha &&
- shape == otherModifier.shape
- }
-
- override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)"
-}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index d8e46ad..b247993 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -971,6 +971,68 @@
assertThat(availableToEffectPostFling).isWithin(1f).of(100f)
}
+ @Test
+ fun consumeNestedPreScroll() {
+ var consumeNestedPreScroll by mutableStateOf(false)
+ val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })
+
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ // Always consume everything so that the only way to start the drag is to
+ // intercept preScroll events.
+ .scrollable(rememberScrollableState { it }, orientation)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+
+ consumeNestedPreScroll = true
+ rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ }
+
+ @Test
+ fun autoStopNestedDrags() {
+ var consumeScrolls by mutableStateOf(true)
+ val draggable =
+ TestDraggable(autoStopNestedDrags = true, onDrag = { if (consumeScrolls) it else 0f })
+
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation)
+ .scrollable(rememberScrollableState { 0f }, orientation)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 1f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ rule.onRoot().performTouchInput { moveBy(50f.toOffset()) }
+
+ assertThat(draggable.onDragStoppedCalled).isFalse()
+
+ consumeScrolls = false
+ rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+ assertThat(draggable.onDragStoppedCalled).isTrue()
+ }
+
private fun ComposeContentTestRule.setContentWithTouchSlop(
content: @Composable () -> Unit
): Float {
@@ -996,7 +1058,9 @@
{ velocity, _ ->
velocity
},
- private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
+ private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
+ private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
+ private val autoStopNestedDrags: Boolean = false,
) : NestedDraggable {
var shouldStartDrag = true
var onDragStartedCalled = false
@@ -1026,6 +1090,8 @@
onDragStarted.invoke(position, sign)
return object : NestedDraggable.Controller {
+ override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags
+
override fun onDrag(delta: Float): Float {
onDragCalled = true
onDragDelta += delta
@@ -1042,8 +1108,12 @@
}
}
- override fun shouldConsumeNestedScroll(sign: Float): Boolean {
- return shouldConsumeNestedScroll.invoke(sign)
+ override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
+ return shouldConsumeNestedPostScroll.invoke(sign)
+ }
+
+ override fun shouldConsumeNestedPreScroll(sign: Float): Boolean {
+ return shouldConsumeNestedPreScroll.invoke(sign)
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 216f0a7..7782705 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -73,7 +73,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.Expandable
import com.android.compose.animation.scene.ContentScope
-import com.android.compose.modifiers.fadingBackground
+import com.android.compose.modifiers.animatedBackground
import com.android.compose.theme.colorAttr
import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.animation.Expandable
@@ -172,8 +172,8 @@
val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
val backgroundModifier =
remember(backgroundColor, backgroundAlphaValue, backgroundTopRadius) {
- Modifier.fadingBackground(
- backgroundColor,
+ Modifier.animatedBackground(
+ { backgroundColor },
backgroundAlphaValue,
RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius),
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 7015f79..0daef46 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -101,7 +101,7 @@
rememberActivated(traceName = "sceneJankMonitor") { sceneJankMonitorFactory.create() }
val hapticFeedback = LocalHapticFeedback.current
- val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion()
+ val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion(isFullWidthShade())
val sceneTransitions =
remember(hapticFeedback, shadeExpansionMotion) {
transitionsBuilder.build(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 9b45ef6..2f5a030 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -6,7 +6,7 @@
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.transitions
import com.android.internal.jank.Cuj
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
@@ -50,7 +50,7 @@
*/
class SceneContainerTransitions : SceneContainerTransitionsBuilder {
override fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions {
return transitions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
index eb5548d..4c9c23a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
@@ -19,7 +19,7 @@
import com.android.compose.animation.scene.SceneTransitions
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
/**
* Builder of the comprehensive definition of all transitions between scenes and overlays in the
@@ -29,7 +29,7 @@
/** Build the [SceneContainer] transitions spec. */
fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions
}
@@ -42,7 +42,7 @@
private val transitions: SceneTransitions = transitions { /* No transitions */ }
) : SceneContainerTransitionsBuilder {
override fun build(
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
): SceneTransitions = transitions
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 9b4b91e..85aad9b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -20,7 +20,7 @@
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
import com.android.systemui.notifications.ui.composable.NotificationsShade
import com.android.systemui.scene.shared.model.Overlays
@@ -29,7 +29,7 @@
fun TransitionBuilder.toNotificationsShadeTransition(
durationScale: Double = 1.0,
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index 47dd85f..8f0447d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -20,14 +20,14 @@
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.systemui.qs.ui.composable.QuickSettingsShade
import com.android.systemui.shade.ui.composable.OverlayShade
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toQuickSettingsShadeTransition(
durationScale: Double = 1.0,
- shadeExpansionMotion: EdgeContainerExpansionSpec,
+ shadeExpansionMotion: VerticalExpandContainerSpec,
revealHaptics: ContainerRevealHaptics,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 3446081..068218a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -53,8 +53,8 @@
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
import com.android.systemui.res.R
import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion
@@ -114,9 +114,9 @@
modifier =
modifier
.disableSwipesWhenScrolling()
- .edgeContainerExpansionBackground(
- OverlayShade.Colors.PanelBackground,
- rememberShadeExpansionMotion(),
+ .verticalExpandContainerBackground(
+ backgroundColor = OverlayShade.Colors.PanelBackground,
+ spec = rememberShadeExpansionMotion(isFullWidthShade()),
)
) {
Column {
@@ -202,8 +202,10 @@
}
@Composable
- fun rememberShadeExpansionMotion(): EdgeContainerExpansionSpec {
+ fun rememberShadeExpansionMotion(isFullWidth: Boolean): VerticalExpandContainerSpec {
val radius = Dimensions.PanelCornerRadius
- return remember(radius) { EdgeContainerExpansionSpec(radius = radius) }
+ return remember(radius, isFullWidth) {
+ VerticalExpandContainerSpec(isFloating = !isFullWidth, radius = radius)
+ }
}
}
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 1360611..024ca22 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
@@ -68,7 +68,7 @@
return layoutImpl.swipeDetector.detectSwipe(change)
}
- override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+ override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
return this.enabled()
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
index 72f9bd5..734de34 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/reveal/ContainerReveal.kt
@@ -29,7 +29,7 @@
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.PropertyTransformationScope
import com.android.mechanics.MotionValue
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
import kotlinx.coroutines.CoroutineScope
interface ContainerRevealHaptics {
@@ -53,7 +53,7 @@
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
fun TransitionBuilder.verticalContainerReveal(
container: ElementKey,
- motionSpec: EdgeContainerExpansionSpec,
+ motionSpec: VerticalExpandContainerSpec,
haptics: ContainerRevealHaptics,
) {
// Make the swipe distance be exactly the target height of the container.
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 2ab27af..d63450b 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -42,6 +42,7 @@
"PlatformMotionTestingCompose",
"androidx.test.runner",
"androidx.test.ext.junit",
+ "platform-parametric-runner-lib",
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui-test-junit4",
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 0000000..5dbb013
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,624 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.6
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 8.4,
+ "y": 5.2
+ },
+ {
+ "x": 11.2,
+ "y": 5.2
+ },
+ {
+ "x": 13.6,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 16.8,
+ "y": 5.2
+ },
+ {
+ "x": 17.6,
+ "y": 5.2
+ },
+ {
+ "x": 18.4,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 286
+ },
+ {
+ "width": 150,
+ "height": 279.6
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 266.8
+ },
+ {
+ "width": 150,
+ "height": 260.4
+ },
+ {
+ "width": 150,
+ "height": 254
+ },
+ {
+ "width": 150,
+ "height": 247.6
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 228
+ },
+ {
+ "width": 150,
+ "height": 221.6
+ },
+ {
+ "width": 150,
+ "height": 215.2
+ },
+ {
+ "width": 150,
+ "height": 208.8
+ },
+ {
+ "width": 150,
+ "height": 202
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 170
+ },
+ {
+ "width": 150,
+ "height": 163.6
+ },
+ {
+ "width": 150,
+ "height": 157.2
+ },
+ {
+ "width": 150,
+ "height": 150.8
+ },
+ {
+ "width": 150,
+ "height": 144.4
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 131.2
+ },
+ {
+ "width": 150,
+ "height": 124.8
+ },
+ {
+ "width": 150,
+ "height": 118.4
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 99.2
+ },
+ {
+ "width": 150,
+ "height": 81.2
+ },
+ {
+ "width": 144,
+ "height": 62.8
+ },
+ {
+ "width": 138,
+ "height": 46.4
+ },
+ {
+ "width": 133.2,
+ "height": 32
+ },
+ {
+ "width": 129.6,
+ "height": 20.4
+ },
+ {
+ "width": 127.2,
+ "height": 12
+ },
+ {
+ "width": 125.2,
+ "height": 6.4
+ },
+ {
+ "width": 124,
+ "height": 2.8
+ },
+ {
+ "width": 123.2,
+ "height": 0.4
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99781144,
+ 0.87040234,
+ 0.6695792,
+ 0.48078007,
+ 0.33033127,
+ 0.22004372,
+ 0.1432175,
+ 0.09153092,
+ 0.057634592,
+ 0.035840213,
+ 0.022048414,
+ 0.013435662,
+ 0.008117795,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 0000000..1543d18
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,644 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960,
+ 976
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.8
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 6.8,
+ "y": 5.2
+ },
+ {
+ "x": 10.4,
+ "y": 5.2
+ },
+ {
+ "x": 13.2,
+ "y": 5.2
+ },
+ {
+ "x": 15.2,
+ "y": 5.2
+ },
+ {
+ "x": 16.8,
+ "y": 5.2
+ },
+ {
+ "x": 17.6,
+ "y": 5.2
+ },
+ {
+ "x": 18.4,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 294
+ },
+ {
+ "width": 150,
+ "height": 287.6
+ },
+ {
+ "width": 150,
+ "height": 282.8
+ },
+ {
+ "width": 150,
+ "height": 278
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 268.4
+ },
+ {
+ "width": 150,
+ "height": 263.6
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 253.6
+ },
+ {
+ "width": 150,
+ "height": 248.8
+ },
+ {
+ "width": 150,
+ "height": 244
+ },
+ {
+ "width": 150,
+ "height": 239.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 229.6
+ },
+ {
+ "width": 150,
+ "height": 224.8
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 214.8
+ },
+ {
+ "width": 150,
+ "height": 210
+ },
+ {
+ "width": 150,
+ "height": 205.2
+ },
+ {
+ "width": 150,
+ "height": 200.4
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 190.8
+ },
+ {
+ "width": 150,
+ "height": 186
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 171.6
+ },
+ {
+ "width": 150,
+ "height": 166.8
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 147.2
+ },
+ {
+ "width": 150,
+ "height": 122
+ },
+ {
+ "width": 150,
+ "height": 95.2
+ },
+ {
+ "width": 146.8,
+ "height": 70.8
+ },
+ {
+ "width": 139.6,
+ "height": 50.8
+ },
+ {
+ "width": 134,
+ "height": 34
+ },
+ {
+ "width": 130,
+ "height": 19.2
+ },
+ {
+ "width": 127.2,
+ "height": 9.2
+ },
+ {
+ "width": 125.2,
+ "height": 2.8
+ },
+ {
+ "width": 123.6,
+ "height": 0
+ },
+ {
+ "width": 122.8,
+ "height": 0
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99979615,
+ 0.8860379,
+ 0.6869267,
+ 0.4955439,
+ 0.34154767,
+ 0.22803628,
+ 0.14868057,
+ 0.09515619,
+ 0.059987247,
+ 0.037340224,
+ 0.02299112,
+ 0.01402092,
+ 0.008477271,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 0000000..13f75d2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 18,
+ "y": 5.2
+ },
+ {
+ "x": 18,
+ "y": 5.2
+ },
+ {
+ "x": 16.8,
+ "y": 5.2
+ },
+ {
+ "x": 16,
+ "y": 5.2
+ },
+ {
+ "x": 14.8,
+ "y": 5.2
+ },
+ {
+ "x": 13.6,
+ "y": 5.2
+ },
+ {
+ "x": 12.4,
+ "y": 5.2
+ },
+ {
+ "x": 11.2,
+ "y": 5.2
+ },
+ {
+ "x": 10.4,
+ "y": 5.2
+ },
+ {
+ "x": 9.2,
+ "y": 5.2
+ },
+ {
+ "x": 9.2,
+ "y": 5.2
+ },
+ {
+ "x": 8,
+ "y": 5.2
+ },
+ {
+ "x": 6.8,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 124.4,
+ "height": 1.6
+ },
+ {
+ "width": 124.4,
+ "height": 1.6
+ },
+ {
+ "width": 126.8,
+ "height": 3.2
+ },
+ {
+ "width": 128.8,
+ "height": 4.8
+ },
+ {
+ "width": 131.2,
+ "height": 6.4
+ },
+ {
+ "width": 133.6,
+ "height": 8
+ },
+ {
+ "width": 135.6,
+ "height": 9.6
+ },
+ {
+ "width": 138,
+ "height": 11.2
+ },
+ {
+ "width": 140,
+ "height": 12.8
+ },
+ {
+ "width": 142.4,
+ "height": 14.4
+ },
+ {
+ "width": 142.4,
+ "height": 14.4
+ },
+ {
+ "width": 144.8,
+ "height": 16.4
+ },
+ {
+ "width": 147.2,
+ "height": 18
+ },
+ {
+ "width": 149.2,
+ "height": 19.6
+ },
+ {
+ "width": 150,
+ "height": 25.6
+ },
+ {
+ "width": 150,
+ "height": 36.4
+ },
+ {
+ "width": 150,
+ "height": 45.6
+ },
+ {
+ "width": 150,
+ "height": 59.2
+ },
+ {
+ "width": 150,
+ "height": 72.8
+ },
+ {
+ "width": 150,
+ "height": 79.6
+ },
+ {
+ "width": 150,
+ "height": 92.8
+ },
+ {
+ "width": 150,
+ "height": 104.4
+ },
+ {
+ "width": 150,
+ "height": 115.2
+ },
+ {
+ "width": 150,
+ "height": 125.2
+ },
+ {
+ "width": 150,
+ "height": 134.8
+ },
+ {
+ "width": 150,
+ "height": 143.2
+ },
+ {
+ "width": 150,
+ "height": 151.2
+ },
+ {
+ "width": 150,
+ "height": 158.8
+ },
+ {
+ "width": 150,
+ "height": 160
+ },
+ {
+ "width": 150,
+ "height": 167.2
+ },
+ {
+ "width": 150,
+ "height": 174.4
+ },
+ {
+ "width": 150,
+ "height": 180.8
+ },
+ {
+ "width": 150,
+ "height": 187.6
+ },
+ {
+ "width": 150,
+ "height": 188
+ },
+ {
+ "width": 150,
+ "height": 200.4
+ },
+ {
+ "width": 150,
+ "height": 218.4
+ },
+ {
+ "width": 150,
+ "height": 236.8
+ },
+ {
+ "width": 150,
+ "height": 253.2
+ },
+ {
+ "width": 150,
+ "height": 266.8
+ },
+ {
+ "width": 150,
+ "height": 277.2
+ },
+ {
+ "width": 150,
+ "height": 284.8
+ },
+ {
+ "width": 150,
+ "height": 290
+ },
+ {
+ "width": 150,
+ "height": 294
+ },
+ {
+ "width": 150,
+ "height": 296.4
+ },
+ {
+ "width": 150,
+ "height": 298
+ },
+ {
+ "width": 150,
+ "height": 298.8
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 300
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.0067873597,
+ 0.06125766,
+ 0.19080031,
+ 0.39327443,
+ 0.5711931,
+ 0.7085583,
+ 0.8074065,
+ 0.8754226,
+ 0.9207788,
+ 0.95032376,
+ 0.9692185,
+ 0.98112255,
+ 0.9885286,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
new file mode 100644
index 0000000..115483cf
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,434 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.4
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 6.4,
+ "y": 5.2
+ },
+ {
+ "x": 10.4,
+ "y": 5.2
+ },
+ {
+ "x": 13.6,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 17.2,
+ "y": 5.2
+ },
+ {
+ "x": 18,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 290
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 266
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 223.6
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 141.2
+ },
+ {
+ "width": 150,
+ "height": 104
+ },
+ {
+ "width": 147.6,
+ "height": 74
+ },
+ {
+ "width": 139.6,
+ "height": 50.8
+ },
+ {
+ "width": 133.6,
+ "height": 32
+ },
+ {
+ "width": 129.2,
+ "height": 15.6
+ },
+ {
+ "width": 126.4,
+ "height": 5.2
+ },
+ {
+ "width": 124.4,
+ "height": 0
+ },
+ {
+ "width": 123.2,
+ "height": 0
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99479187,
+ 0.8575029,
+ 0.65572864,
+ 0.4691311,
+ 0.3215357,
+ 0.21380007,
+ 0.13896108,
+ 0.0887118,
+ 0.05580789,
+ 0.03467691,
+ 0.021318138,
+ 0.0129826665,
+ 0.007839739,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
similarity index 63%
rename from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
rename to packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
index 6dc5a0e..f202fcd 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
@@ -64,104 +64,104 @@
"type": "not_found"
},
{
- "x": 62.4,
- "y": 50
+ "x": 17.6,
+ "y": 5.2
},
{
- "x": 61.2,
- "y": 50
+ "x": 16.4,
+ "y": 5.2
},
{
- "x": 59.2,
- "y": 50
+ "x": 14.4,
+ "y": 5.2
},
{
- "x": 57.2,
- "y": 50
+ "x": 12.4,
+ "y": 5.2
},
{
- "x": 54.8,
- "y": 50
+ "x": 10,
+ "y": 5.2
},
{
- "x": 52.4,
- "y": 50
+ "x": 7.6,
+ "y": 5.2
},
{
- "x": 52.4,
- "y": 50
+ "x": 7.6,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
},
{
- "x": 50,
- "y": 50
+ "x": 5.2,
+ "y": 5.2
}
]
},
@@ -194,104 +194,104 @@
"type": "not_found"
},
{
- "width": 163.2,
+ "width": 125.2,
"height": 2
},
{
- "width": 166,
+ "width": 128,
"height": 4.4
},
{
- "width": 170,
+ "width": 132,
"height": 6.8
},
{
- "width": 174,
+ "width": 136,
"height": 10
},
{
- "width": 178.4,
+ "width": 140.4,
"height": 13.2
},
{
- "width": 183.6,
+ "width": 145.6,
"height": 16.8
},
{
- "width": 183.6,
+ "width": 145.6,
"height": 16.8
},
{
- "width": 188,
- "height": 44.4
+ "width": 150,
+ "height": 36.8
},
{
- "width": 188,
- "height": 103.6
+ "width": 150,
+ "height": 81.2
},
{
- "width": 188,
- "height": 166
+ "width": 150,
+ "height": 126.8
},
{
- "width": 188,
- "height": 222.4
+ "width": 150,
+ "height": 168
},
{
- "width": 188,
- "height": 270
+ "width": 150,
+ "height": 202.8
},
{
- "width": 188,
- "height": 307.2
+ "width": 150,
+ "height": 230
},
{
- "width": 188,
- "height": 335.6
+ "width": 150,
+ "height": 250.8
},
{
- "width": 188,
- "height": 356.4
+ "width": 150,
+ "height": 266.4
},
{
- "width": 188,
- "height": 371.2
+ "width": 150,
+ "height": 277.6
},
{
- "width": 188,
- "height": 381.6
+ "width": 150,
+ "height": 285.2
},
{
- "width": 188,
- "height": 388.8
+ "width": 150,
+ "height": 290.4
},
{
- "width": 188,
- "height": 393.2
+ "width": 150,
+ "height": 294
},
{
- "width": 188,
- "height": 396
+ "width": 150,
+ "height": 296.4
},
{
- "width": 188,
- "height": 398
+ "width": 150,
+ "height": 298
},
{
- "width": 188,
- "height": 398.8
+ "width": 150,
+ "height": 298.8
},
{
- "width": 188,
- "height": 399.2
+ "width": 150,
+ "height": 299.2
},
{
- "width": 188,
- "height": 399.6
+ "width": 150,
+ "height": 299.6
},
{
- "width": 188,
- "height": 399.6
+ "width": 150,
+ "height": 299.6
}
]
},
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 0000000..4c57bda
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,724 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960,
+ 976,
+ 992,
+ 1008,
+ 1024,
+ 1040,
+ 1056,
+ 1072,
+ 1088,
+ 1104
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 18,
+ "y": 5.2
+ },
+ {
+ "x": 17.2,
+ "y": 5.2
+ },
+ {
+ "x": 16.4,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 14.8,
+ "y": 5.2
+ },
+ {
+ "x": 14,
+ "y": 5.2
+ },
+ {
+ "x": 13.2,
+ "y": 5.2
+ },
+ {
+ "x": 12.4,
+ "y": 5.2
+ },
+ {
+ "x": 11.6,
+ "y": 5.2
+ },
+ {
+ "x": 10.8,
+ "y": 5.2
+ },
+ {
+ "x": 10.4,
+ "y": 5.2
+ },
+ {
+ "x": 9.6,
+ "y": 5.2
+ },
+ {
+ "x": 8.8,
+ "y": 5.2
+ },
+ {
+ "x": 8.4,
+ "y": 5.2
+ },
+ {
+ "x": 8,
+ "y": 5.2
+ },
+ {
+ "x": 7.2,
+ "y": 5.2
+ },
+ {
+ "x": 6.8,
+ "y": 5.2
+ },
+ {
+ "x": 6.4,
+ "y": 5.2
+ },
+ {
+ "x": 6,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 5.2
+ },
+ {
+ "x": 6,
+ "y": 5.2
+ },
+ {
+ "x": 6.4,
+ "y": 5.2
+ },
+ {
+ "x": 6.8,
+ "y": 5.2
+ },
+ {
+ "x": 7.2,
+ "y": 5.2
+ },
+ {
+ "x": 8,
+ "y": 5.2
+ },
+ {
+ "x": 8.4,
+ "y": 5.2
+ },
+ {
+ "x": 8.8,
+ "y": 5.2
+ },
+ {
+ "x": 9.6,
+ "y": 5.2
+ },
+ {
+ "x": 10.4,
+ "y": 5.2
+ },
+ {
+ "x": 10.8,
+ "y": 5.2
+ },
+ {
+ "x": 11.6,
+ "y": 5.2
+ },
+ {
+ "x": 12.4,
+ "y": 5.2
+ },
+ {
+ "x": 13.2,
+ "y": 5.2
+ },
+ {
+ "x": 14,
+ "y": 5.2
+ },
+ {
+ "x": 14.8,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 16.4,
+ "y": 5.2
+ },
+ {
+ "x": 17.2,
+ "y": 5.2
+ },
+ {
+ "x": 18,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 124.4,
+ "height": 1.6
+ },
+ {
+ "width": 126,
+ "height": 2.8
+ },
+ {
+ "width": 128,
+ "height": 4
+ },
+ {
+ "width": 129.6,
+ "height": 5.2
+ },
+ {
+ "width": 131.2,
+ "height": 6.4
+ },
+ {
+ "width": 132.8,
+ "height": 7.6
+ },
+ {
+ "width": 134.4,
+ "height": 8.8
+ },
+ {
+ "width": 136,
+ "height": 10
+ },
+ {
+ "width": 137.2,
+ "height": 10.8
+ },
+ {
+ "width": 138.8,
+ "height": 12
+ },
+ {
+ "width": 140,
+ "height": 12.8
+ },
+ {
+ "width": 141.2,
+ "height": 13.6
+ },
+ {
+ "width": 142.8,
+ "height": 14.8
+ },
+ {
+ "width": 144,
+ "height": 15.6
+ },
+ {
+ "width": 144.8,
+ "height": 16.4
+ },
+ {
+ "width": 146,
+ "height": 17.2
+ },
+ {
+ "width": 146.8,
+ "height": 17.6
+ },
+ {
+ "width": 148,
+ "height": 18.4
+ },
+ {
+ "width": 148.8,
+ "height": 19.2
+ },
+ {
+ "width": 149.6,
+ "height": 19.6
+ },
+ {
+ "width": 150,
+ "height": 21.2
+ },
+ {
+ "width": 150,
+ "height": 24.8
+ },
+ {
+ "width": 150,
+ "height": 30
+ },
+ {
+ "width": 150,
+ "height": 38
+ },
+ {
+ "width": 150,
+ "height": 46
+ },
+ {
+ "width": 150,
+ "height": 54
+ },
+ {
+ "width": 150,
+ "height": 61.2
+ },
+ {
+ "width": 150,
+ "height": 66.8
+ },
+ {
+ "width": 150,
+ "height": 71.6
+ },
+ {
+ "width": 150,
+ "height": 75.6
+ },
+ {
+ "width": 150,
+ "height": 78
+ },
+ {
+ "width": 150,
+ "height": 79.6
+ },
+ {
+ "width": 150,
+ "height": 80.8
+ },
+ {
+ "width": 150,
+ "height": 80.8
+ },
+ {
+ "width": 150,
+ "height": 80.4
+ },
+ {
+ "width": 150,
+ "height": 79.6
+ },
+ {
+ "width": 149.6,
+ "height": 78
+ },
+ {
+ "width": 148.8,
+ "height": 76.4
+ },
+ {
+ "width": 148,
+ "height": 74
+ },
+ {
+ "width": 146.8,
+ "height": 71.6
+ },
+ {
+ "width": 146,
+ "height": 69.2
+ },
+ {
+ "width": 144.8,
+ "height": 66
+ },
+ {
+ "width": 144,
+ "height": 62.8
+ },
+ {
+ "width": 142.8,
+ "height": 59.2
+ },
+ {
+ "width": 141.2,
+ "height": 55.6
+ },
+ {
+ "width": 140,
+ "height": 52
+ },
+ {
+ "width": 138.8,
+ "height": 48
+ },
+ {
+ "width": 137.2,
+ "height": 44
+ },
+ {
+ "width": 136,
+ "height": 40
+ },
+ {
+ "width": 134.4,
+ "height": 37.6
+ },
+ {
+ "width": 132.8,
+ "height": 38
+ },
+ {
+ "width": 131.2,
+ "height": 30.4
+ },
+ {
+ "width": 129.6,
+ "height": 25.2
+ },
+ {
+ "width": 128,
+ "height": 20.4
+ },
+ {
+ "width": 126,
+ "height": 16
+ },
+ {
+ "width": 124.4,
+ "height": 12.4
+ },
+ {
+ "width": 122.8,
+ "height": 9.2
+ },
+ {
+ "width": 122,
+ "height": 6.8
+ },
+ {
+ "width": 122,
+ "height": 5.2
+ },
+ {
+ "width": 122,
+ "height": 3.6
+ },
+ {
+ "width": 122,
+ "height": 2.4
+ },
+ {
+ "width": 122,
+ "height": 1.6
+ },
+ {
+ "width": 122,
+ "height": 0.8
+ },
+ {
+ "width": 122,
+ "height": 0.4
+ },
+ {
+ "width": 122,
+ "height": 0.4
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.012518823,
+ 0.0741024,
+ 0.22542936,
+ 0.42628878,
+ 0.5976642,
+ 0.7280313,
+ 0.82100236,
+ 0.8845844,
+ 0.9267946,
+ 0.95419544,
+ 0.9716705,
+ 0.98265487,
+ 0.98947525,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9944124,
+ 0.9417388,
+ 0.81841844,
+ 0.61578125,
+ 0.43616113,
+ 0.29689062,
+ 0.19641556,
+ 0.12716138,
+ 0.080922,
+ 0.050773032,
+ 0.031477194,
+ 0.019312754,
+ 0.011740657,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 0000000..26c80e3
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,314 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 7.2,
+ "y": 5.2
+ },
+ {
+ "x": 11.2,
+ "y": 5.2
+ },
+ {
+ "x": 14,
+ "y": 5.2
+ },
+ {
+ "x": 16,
+ "y": 5.2
+ },
+ {
+ "x": 17.6,
+ "y": 5.2
+ },
+ {
+ "x": 18.4,
+ "y": 5.2
+ },
+ {
+ "x": 18.8,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 234.8
+ },
+ {
+ "width": 150,
+ "height": 185.2
+ },
+ {
+ "width": 150,
+ "height": 138.8
+ },
+ {
+ "width": 150,
+ "height": 100.4
+ },
+ {
+ "width": 146.4,
+ "height": 69.6
+ },
+ {
+ "width": 138.4,
+ "height": 46.8
+ },
+ {
+ "width": 132.4,
+ "height": 28
+ },
+ {
+ "width": 128.4,
+ "height": 13.2
+ },
+ {
+ "width": 125.6,
+ "height": 4
+ },
+ {
+ "width": 124,
+ "height": 0
+ },
+ {
+ "width": 122.8,
+ "height": 0
+ },
+ {
+ "width": 122.4,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 122,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9762947,
+ 0.8118515,
+ 0.60931784,
+ 0.43090785,
+ 0.29299664,
+ 0.19368339,
+ 0.12531388,
+ 0.079705715,
+ 0.049988627,
+ 0.030979574,
+ 0.019001365,
+ 0.011548042,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 0000000..7a02d36
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,224 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 19.2,
+ "y": 5.2
+ },
+ {
+ "x": 15.6,
+ "y": 5.2
+ },
+ {
+ "x": 8,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 122,
+ "height": 0
+ },
+ {
+ "width": 129.2,
+ "height": 5.2
+ },
+ {
+ "width": 144.4,
+ "height": 16
+ },
+ {
+ "width": 150,
+ "height": 62
+ },
+ {
+ "width": 150,
+ "height": 118.4
+ },
+ {
+ "width": 150,
+ "height": 166
+ },
+ {
+ "width": 150,
+ "height": 204
+ },
+ {
+ "width": 150,
+ "height": 233.2
+ },
+ {
+ "width": 150,
+ "height": 254.4
+ },
+ {
+ "width": 150,
+ "height": 270
+ },
+ {
+ "width": 150,
+ "height": 280.8
+ },
+ {
+ "width": 150,
+ "height": 288
+ },
+ {
+ "width": 150,
+ "height": 292.8
+ },
+ {
+ "width": 150,
+ "height": 296
+ },
+ {
+ "width": 150,
+ "height": 298
+ },
+ {
+ "width": 150,
+ "height": 298.8
+ },
+ {
+ "width": 150,
+ "height": 299.2
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.0951103,
+ 0.2911651,
+ 0.48551244,
+ 0.6439433,
+ 0.76157355,
+ 0.8441935,
+ 0.9001033,
+ 0.9369305,
+ 0.96069145,
+ 0.97577035,
+ 0.98520935,
+ 0.9910494,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 0000000..f44d4cd
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,584 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.6
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 293.2
+ },
+ {
+ "width": 150,
+ "height": 286
+ },
+ {
+ "width": 150,
+ "height": 279.6
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 266.8
+ },
+ {
+ "width": 150,
+ "height": 260.4
+ },
+ {
+ "width": 150,
+ "height": 254
+ },
+ {
+ "width": 150,
+ "height": 247.6
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 241.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 228
+ },
+ {
+ "width": 150,
+ "height": 221.6
+ },
+ {
+ "width": 150,
+ "height": 215.2
+ },
+ {
+ "width": 150,
+ "height": 208.8
+ },
+ {
+ "width": 150,
+ "height": 202
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 189.2
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 170
+ },
+ {
+ "width": 150,
+ "height": 163.6
+ },
+ {
+ "width": 150,
+ "height": 157.2
+ },
+ {
+ "width": 150,
+ "height": 150.8
+ },
+ {
+ "width": 150,
+ "height": 144.4
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 137.6
+ },
+ {
+ "width": 150,
+ "height": 131.2
+ },
+ {
+ "width": 150,
+ "height": 124.8
+ },
+ {
+ "width": 150,
+ "height": 118.4
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 112
+ },
+ {
+ "width": 150,
+ "height": 99.2
+ },
+ {
+ "width": 150,
+ "height": 84.4
+ },
+ {
+ "width": 150,
+ "height": 70.8
+ },
+ {
+ "width": 150,
+ "height": 58
+ },
+ {
+ "width": 150,
+ "height": 46.4
+ },
+ {
+ "width": 150,
+ "height": 36.4
+ },
+ {
+ "width": 150,
+ "height": 28
+ },
+ {
+ "width": 150,
+ "height": 20.8
+ },
+ {
+ "width": 150,
+ "height": 15.6
+ },
+ {
+ "width": 150,
+ "height": 11.2
+ },
+ {
+ "width": 150,
+ "height": 8
+ },
+ {
+ "width": 150,
+ "height": 5.6
+ },
+ {
+ "width": 150,
+ "height": 3.6
+ },
+ {
+ "width": 150,
+ "height": 2.4
+ },
+ {
+ "width": 150,
+ "height": 1.2
+ },
+ {
+ "width": 150,
+ "height": 0.8
+ },
+ {
+ "width": 150,
+ "height": 0.4
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99781144,
+ 0.87040234,
+ 0.6695792,
+ 0.48078007,
+ 0.33033127,
+ 0.22004372,
+ 0.1432175,
+ 0.09153092,
+ 0.057634592,
+ 0.035840213,
+ 0.022048414,
+ 0.013435662,
+ 0.008117795,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 0000000..9b68c71
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,624 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.8
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 294
+ },
+ {
+ "width": 150,
+ "height": 287.6
+ },
+ {
+ "width": 150,
+ "height": 282.8
+ },
+ {
+ "width": 150,
+ "height": 278
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 268.4
+ },
+ {
+ "width": 150,
+ "height": 263.6
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 258.8
+ },
+ {
+ "width": 150,
+ "height": 253.6
+ },
+ {
+ "width": 150,
+ "height": 248.8
+ },
+ {
+ "width": 150,
+ "height": 244
+ },
+ {
+ "width": 150,
+ "height": 239.2
+ },
+ {
+ "width": 150,
+ "height": 234.4
+ },
+ {
+ "width": 150,
+ "height": 229.6
+ },
+ {
+ "width": 150,
+ "height": 224.8
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 220
+ },
+ {
+ "width": 150,
+ "height": 214.8
+ },
+ {
+ "width": 150,
+ "height": 210
+ },
+ {
+ "width": 150,
+ "height": 205.2
+ },
+ {
+ "width": 150,
+ "height": 200.4
+ },
+ {
+ "width": 150,
+ "height": 195.6
+ },
+ {
+ "width": 150,
+ "height": 190.8
+ },
+ {
+ "width": 150,
+ "height": 186
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 181.2
+ },
+ {
+ "width": 150,
+ "height": 176.4
+ },
+ {
+ "width": 150,
+ "height": 171.6
+ },
+ {
+ "width": 150,
+ "height": 166.8
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 161.6
+ },
+ {
+ "width": 150,
+ "height": 147.2
+ },
+ {
+ "width": 150,
+ "height": 122
+ },
+ {
+ "width": 150,
+ "height": 95.2
+ },
+ {
+ "width": 150,
+ "height": 70.8
+ },
+ {
+ "width": 150,
+ "height": 51.6
+ },
+ {
+ "width": 150,
+ "height": 36.8
+ },
+ {
+ "width": 150,
+ "height": 25.6
+ },
+ {
+ "width": 150,
+ "height": 17.2
+ },
+ {
+ "width": 150,
+ "height": 11.2
+ },
+ {
+ "width": 150,
+ "height": 6.8
+ },
+ {
+ "width": 150,
+ "height": 4
+ },
+ {
+ "width": 150,
+ "height": 2
+ },
+ {
+ "width": 150,
+ "height": 0.8
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99979615,
+ 0.8860379,
+ 0.6869267,
+ 0.4955439,
+ 0.34154767,
+ 0.22803628,
+ 0.14868057,
+ 0.09515619,
+ 0.059987247,
+ 0.037340224,
+ 0.02299112,
+ 0.01402092,
+ 0.008477271,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 0000000..1bf6a62
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 1.6
+ },
+ {
+ "width": 150,
+ "height": 1.6
+ },
+ {
+ "width": 150,
+ "height": 3.2
+ },
+ {
+ "width": 150,
+ "height": 4.8
+ },
+ {
+ "width": 150,
+ "height": 6.4
+ },
+ {
+ "width": 150,
+ "height": 8
+ },
+ {
+ "width": 150,
+ "height": 9.6
+ },
+ {
+ "width": 150,
+ "height": 11.2
+ },
+ {
+ "width": 150,
+ "height": 12.8
+ },
+ {
+ "width": 150,
+ "height": 14.4
+ },
+ {
+ "width": 150,
+ "height": 14.4
+ },
+ {
+ "width": 150,
+ "height": 16.4
+ },
+ {
+ "width": 150,
+ "height": 18
+ },
+ {
+ "width": 150,
+ "height": 19.6
+ },
+ {
+ "width": 150,
+ "height": 20.8
+ },
+ {
+ "width": 150,
+ "height": 22.8
+ },
+ {
+ "width": 150,
+ "height": 24.4
+ },
+ {
+ "width": 150,
+ "height": 26
+ },
+ {
+ "width": 150,
+ "height": 27.6
+ },
+ {
+ "width": 150,
+ "height": 27.6
+ },
+ {
+ "width": 150,
+ "height": 29.2
+ },
+ {
+ "width": 150,
+ "height": 30.8
+ },
+ {
+ "width": 150,
+ "height": 32.4
+ },
+ {
+ "width": 150,
+ "height": 34
+ },
+ {
+ "width": 150,
+ "height": 40.4
+ },
+ {
+ "width": 150,
+ "height": 52.4
+ },
+ {
+ "width": 150,
+ "height": 64.8
+ },
+ {
+ "width": 150,
+ "height": 83.2
+ },
+ {
+ "width": 150,
+ "height": 96
+ },
+ {
+ "width": 150,
+ "height": 114.8
+ },
+ {
+ "width": 150,
+ "height": 132
+ },
+ {
+ "width": 150,
+ "height": 148
+ },
+ {
+ "width": 150,
+ "height": 162
+ },
+ {
+ "width": 150,
+ "height": 168.4
+ },
+ {
+ "width": 150,
+ "height": 186
+ },
+ {
+ "width": 150,
+ "height": 208
+ },
+ {
+ "width": 150,
+ "height": 229.6
+ },
+ {
+ "width": 150,
+ "height": 248.4
+ },
+ {
+ "width": 150,
+ "height": 263.2
+ },
+ {
+ "width": 150,
+ "height": 274.8
+ },
+ {
+ "width": 150,
+ "height": 283.2
+ },
+ {
+ "width": 150,
+ "height": 289.2
+ },
+ {
+ "width": 150,
+ "height": 293.6
+ },
+ {
+ "width": 150,
+ "height": 296
+ },
+ {
+ "width": 150,
+ "height": 298
+ },
+ {
+ "width": 150,
+ "height": 298.8
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 300
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.0067873597,
+ 0.06125766,
+ 0.19080031,
+ 0.39327443,
+ 0.5711931,
+ 0.7085583,
+ 0.8074065,
+ 0.8754226,
+ 0.9207788,
+ 0.95032376,
+ 0.9692185,
+ 0.98112255,
+ 0.9885286,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
new file mode 100644
index 0000000..86805bd
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,424 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.6,
+ "y": 6.4
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 290
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 266
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 252
+ },
+ {
+ "width": 150,
+ "height": 223.6
+ },
+ {
+ "width": 150,
+ "height": 182.8
+ },
+ {
+ "width": 150,
+ "height": 141.2
+ },
+ {
+ "width": 150,
+ "height": 104
+ },
+ {
+ "width": 150,
+ "height": 72
+ },
+ {
+ "width": 150,
+ "height": 46
+ },
+ {
+ "width": 150,
+ "height": 28
+ },
+ {
+ "width": 150,
+ "height": 15.6
+ },
+ {
+ "width": 150,
+ "height": 7.2
+ },
+ {
+ "width": 150,
+ "height": 2
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.99479187,
+ 0.8575029,
+ 0.65572864,
+ 0.4691311,
+ 0.3215357,
+ 0.21380007,
+ 0.13896108,
+ 0.0887118,
+ 0.05580789,
+ 0.03467691,
+ 0.021318138,
+ 0.0129826665,
+ 0.007839739,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
new file mode 100644
index 0000000..98519db
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
@@ -0,0 +1,374 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 2
+ },
+ {
+ "width": 150,
+ "height": 4.4
+ },
+ {
+ "width": 150,
+ "height": 6.8
+ },
+ {
+ "width": 150,
+ "height": 10
+ },
+ {
+ "width": 150,
+ "height": 13.2
+ },
+ {
+ "width": 150,
+ "height": 16.8
+ },
+ {
+ "width": 150,
+ "height": 16.8
+ },
+ {
+ "width": 150,
+ "height": 23.6
+ },
+ {
+ "width": 150,
+ "height": 32.8
+ },
+ {
+ "width": 150,
+ "height": 76.8
+ },
+ {
+ "width": 150,
+ "height": 123.6
+ },
+ {
+ "width": 150,
+ "height": 164.8
+ },
+ {
+ "width": 150,
+ "height": 198.4
+ },
+ {
+ "width": 150,
+ "height": 225.6
+ },
+ {
+ "width": 150,
+ "height": 246.4
+ },
+ {
+ "width": 150,
+ "height": 262
+ },
+ {
+ "width": 150,
+ "height": 273.2
+ },
+ {
+ "width": 150,
+ "height": 281.6
+ },
+ {
+ "width": 150,
+ "height": 287.6
+ },
+ {
+ "width": 150,
+ "height": 292
+ },
+ {
+ "width": 150,
+ "height": 294.8
+ },
+ {
+ "width": 150,
+ "height": 296.4
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 298.4
+ },
+ {
+ "width": 150,
+ "height": 299.2
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.008216977,
+ 0.06259775,
+ 0.19032806,
+ 0.39281356,
+ 0.57081985,
+ 0.7082821,
+ 0.80721295,
+ 0.8752918,
+ 0.9206928,
+ 0.95026827,
+ 0.9691833,
+ 0.98110056,
+ 0.988515,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 0000000..850cee9
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,744 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432,
+ 448,
+ 464,
+ 480,
+ 496,
+ 512,
+ 528,
+ 544,
+ 560,
+ 576,
+ 592,
+ 608,
+ 624,
+ 640,
+ 656,
+ 672,
+ 688,
+ 704,
+ 720,
+ 736,
+ 752,
+ 768,
+ 784,
+ 800,
+ 816,
+ 832,
+ 848,
+ 864,
+ 880,
+ 896,
+ 912,
+ 928,
+ 944,
+ 960,
+ 976,
+ 992,
+ 1008,
+ 1024,
+ 1040,
+ 1056,
+ 1072,
+ 1088,
+ 1104,
+ 1120,
+ 1136
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 2.8
+ },
+ {
+ "width": 150,
+ "height": 4.8
+ },
+ {
+ "width": 150,
+ "height": 6.8
+ },
+ {
+ "width": 150,
+ "height": 8.4
+ },
+ {
+ "width": 150,
+ "height": 10.4
+ },
+ {
+ "width": 150,
+ "height": 12.4
+ },
+ {
+ "width": 150,
+ "height": 14
+ },
+ {
+ "width": 150,
+ "height": 16
+ },
+ {
+ "width": 150,
+ "height": 17.6
+ },
+ {
+ "width": 150,
+ "height": 19.2
+ },
+ {
+ "width": 150,
+ "height": 20.8
+ },
+ {
+ "width": 150,
+ "height": 22.4
+ },
+ {
+ "width": 150,
+ "height": 24
+ },
+ {
+ "width": 150,
+ "height": 25.6
+ },
+ {
+ "width": 150,
+ "height": 26.8
+ },
+ {
+ "width": 150,
+ "height": 28
+ },
+ {
+ "width": 150,
+ "height": 29.2
+ },
+ {
+ "width": 150,
+ "height": 30.4
+ },
+ {
+ "width": 150,
+ "height": 31.6
+ },
+ {
+ "width": 150,
+ "height": 32.4
+ },
+ {
+ "width": 150,
+ "height": 33.2
+ },
+ {
+ "width": 150,
+ "height": 34
+ },
+ {
+ "width": 150,
+ "height": 36.8
+ },
+ {
+ "width": 150,
+ "height": 42.4
+ },
+ {
+ "width": 150,
+ "height": 50.8
+ },
+ {
+ "width": 150,
+ "height": 64
+ },
+ {
+ "width": 150,
+ "height": 78
+ },
+ {
+ "width": 150,
+ "height": 91.2
+ },
+ {
+ "width": 150,
+ "height": 102.4
+ },
+ {
+ "width": 150,
+ "height": 112.4
+ },
+ {
+ "width": 150,
+ "height": 120
+ },
+ {
+ "width": 150,
+ "height": 126
+ },
+ {
+ "width": 150,
+ "height": 130
+ },
+ {
+ "width": 150,
+ "height": 132.8
+ },
+ {
+ "width": 150,
+ "height": 134
+ },
+ {
+ "width": 150,
+ "height": 134
+ },
+ {
+ "width": 150,
+ "height": 133.2
+ },
+ {
+ "width": 150,
+ "height": 131.2
+ },
+ {
+ "width": 150,
+ "height": 128.8
+ },
+ {
+ "width": 150,
+ "height": 125.2
+ },
+ {
+ "width": 150,
+ "height": 121.6
+ },
+ {
+ "width": 150,
+ "height": 117.6
+ },
+ {
+ "width": 150,
+ "height": 112.8
+ },
+ {
+ "width": 150,
+ "height": 108
+ },
+ {
+ "width": 150,
+ "height": 102.4
+ },
+ {
+ "width": 150,
+ "height": 96.4
+ },
+ {
+ "width": 150,
+ "height": 91.2
+ },
+ {
+ "width": 150,
+ "height": 88
+ },
+ {
+ "width": 150,
+ "height": 81.6
+ },
+ {
+ "width": 150,
+ "height": 70.8
+ },
+ {
+ "width": 150,
+ "height": 59.2
+ },
+ {
+ "width": 150,
+ "height": 48
+ },
+ {
+ "width": 150,
+ "height": 38.4
+ },
+ {
+ "width": 150,
+ "height": 30
+ },
+ {
+ "width": 150,
+ "height": 22.8
+ },
+ {
+ "width": 150,
+ "height": 17.2
+ },
+ {
+ "width": 150,
+ "height": 12.4
+ },
+ {
+ "width": 150,
+ "height": 9.2
+ },
+ {
+ "width": 150,
+ "height": 6.4
+ },
+ {
+ "width": 150,
+ "height": 4.4
+ },
+ {
+ "width": 150,
+ "height": 2.8
+ },
+ {
+ "width": 150,
+ "height": 1.6
+ },
+ {
+ "width": 150,
+ "height": 1.2
+ },
+ {
+ "width": 150,
+ "height": 0.4
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.0066464543,
+ 0.059778452,
+ 0.1875459,
+ 0.39009166,
+ 0.5686131,
+ 0.70664865,
+ 0.8060679,
+ 0.87451804,
+ 0.92018366,
+ 0.94994,
+ 0.9689752,
+ 0.9809703,
+ 0.98843443,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.98828065,
+ 0.9288363,
+ 0.7806658,
+ 0.57941735,
+ 0.40687433,
+ 0.27529213,
+ 0.18131107,
+ 0.11697123,
+ 0.074225225,
+ 0.046460062,
+ 0.028744182,
+ 0.017604083,
+ 0.010684598
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 0000000..afa005a
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,304 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352,
+ 368,
+ 384,
+ 400,
+ 416,
+ 432
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 300
+ },
+ {
+ "width": 150,
+ "height": 278.8
+ },
+ {
+ "width": 150,
+ "height": 234.8
+ },
+ {
+ "width": 150,
+ "height": 185.2
+ },
+ {
+ "width": 150,
+ "height": 138.8
+ },
+ {
+ "width": 150,
+ "height": 100.4
+ },
+ {
+ "width": 150,
+ "height": 66.8
+ },
+ {
+ "width": 150,
+ "height": 41.6
+ },
+ {
+ "width": 150,
+ "height": 23.6
+ },
+ {
+ "width": 150,
+ "height": 12
+ },
+ {
+ "width": 150,
+ "height": 4.4
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 0
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.9762947,
+ 0.8118515,
+ 0.60931784,
+ 0.43090785,
+ 0.29299664,
+ 0.19368339,
+ 0.12531388,
+ 0.079705715,
+ 0.049988627,
+ 0.030979574,
+ 0.019001365,
+ 0.011548042,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 0000000..317d480
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,254 @@
+{
+ "frame_ids": [
+ 0,
+ 16,
+ 32,
+ 48,
+ 64,
+ 80,
+ 96,
+ 112,
+ 128,
+ 144,
+ 160,
+ 176,
+ 192,
+ 208,
+ 224,
+ 240,
+ 256,
+ 272,
+ 288,
+ 304,
+ 320,
+ 336,
+ 352
+ ],
+ "features": [
+ {
+ "name": "RevealElement_position",
+ "type": "dpOffset",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ },
+ {
+ "x": 5.2,
+ "y": 5.2
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_size",
+ "type": "dpSize",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ {
+ "width": 150,
+ "height": 0
+ },
+ {
+ "width": 150,
+ "height": 5.2
+ },
+ {
+ "width": 150,
+ "height": 16
+ },
+ {
+ "width": 150,
+ "height": 28.4
+ },
+ {
+ "width": 150,
+ "height": 63.6
+ },
+ {
+ "width": 150,
+ "height": 116.4
+ },
+ {
+ "width": 150,
+ "height": 161.2
+ },
+ {
+ "width": 150,
+ "height": 197.2
+ },
+ {
+ "width": 150,
+ "height": 225.2
+ },
+ {
+ "width": 150,
+ "height": 246.8
+ },
+ {
+ "width": 150,
+ "height": 262.4
+ },
+ {
+ "width": 150,
+ "height": 274
+ },
+ {
+ "width": 150,
+ "height": 282.4
+ },
+ {
+ "width": 150,
+ "height": 288.4
+ },
+ {
+ "width": 150,
+ "height": 292.4
+ },
+ {
+ "width": 150,
+ "height": 294.8
+ },
+ {
+ "width": 150,
+ "height": 296.4
+ },
+ {
+ "width": 150,
+ "height": 297.6
+ },
+ {
+ "width": 150,
+ "height": 298.4
+ },
+ {
+ "width": 150,
+ "height": 299.2
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ },
+ {
+ "width": 150,
+ "height": 299.6
+ }
+ ]
+ },
+ {
+ "name": "RevealElement_alpha",
+ "type": "float",
+ "data_points": [
+ {
+ "type": "not_found"
+ },
+ 0,
+ 0,
+ 0.0951103,
+ 0.2911651,
+ 0.48551244,
+ 0.6439433,
+ 0.76157355,
+ 0.8441935,
+ 0.9001033,
+ 0.9369305,
+ 0.96069145,
+ 0.97577035,
+ 0.98520935,
+ 0.9910494,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
deleted file mode 100644
index 57f6766..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
+++ /dev/null
@@ -1,654 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656,
- 672,
- 688,
- 704,
- 720,
- 736,
- 752,
- 768,
- 784,
- 800,
- 816,
- 832,
- 848,
- 864,
- 880,
- 896,
- 912,
- 928,
- 944,
- 960,
- 976,
- 992
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50.4
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 52.4,
- "y": 50
- },
- {
- "x": 56,
- "y": 50
- },
- {
- "x": 58.8,
- "y": 50
- },
- {
- "x": 60.8,
- "y": 50
- },
- {
- "x": 62,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 393.2
- },
- {
- "width": 188,
- "height": 393.2
- },
- {
- "width": 188,
- "height": 386
- },
- {
- "width": 188,
- "height": 379.6
- },
- {
- "width": 188,
- "height": 372.8
- },
- {
- "width": 188,
- "height": 366.8
- },
- {
- "width": 188,
- "height": 360.4
- },
- {
- "width": 188,
- "height": 354
- },
- {
- "width": 188,
- "height": 347.6
- },
- {
- "width": 188,
- "height": 341.2
- },
- {
- "width": 188,
- "height": 341.2
- },
- {
- "width": 188,
- "height": 334
- },
- {
- "width": 188,
- "height": 328
- },
- {
- "width": 188,
- "height": 321.6
- },
- {
- "width": 188,
- "height": 315.2
- },
- {
- "width": 188,
- "height": 308.8
- },
- {
- "width": 188,
- "height": 302.4
- },
- {
- "width": 188,
- "height": 296
- },
- {
- "width": 188,
- "height": 289.6
- },
- {
- "width": 188,
- "height": 289.6
- },
- {
- "width": 188,
- "height": 282.8
- },
- {
- "width": 188,
- "height": 276.4
- },
- {
- "width": 188,
- "height": 270
- },
- {
- "width": 188,
- "height": 263.6
- },
- {
- "width": 188,
- "height": 257.2
- },
- {
- "width": 188,
- "height": 250.8
- },
- {
- "width": 188,
- "height": 244.4
- },
- {
- "width": 188,
- "height": 238
- },
- {
- "width": 188,
- "height": 238
- },
- {
- "width": 188,
- "height": 231.2
- },
- {
- "width": 188,
- "height": 224.8
- },
- {
- "width": 188,
- "height": 218.4
- },
- {
- "width": 188,
- "height": 212
- },
- {
- "width": 188,
- "height": 212
- },
- {
- "width": 188,
- "height": 192.4
- },
- {
- "width": 188,
- "height": 159.6
- },
- {
- "width": 188,
- "height": 124.4
- },
- {
- "width": 188,
- "height": 92.8
- },
- {
- "width": 183.2,
- "height": 66.4
- },
- {
- "width": 176,
- "height": 46
- },
- {
- "width": 170.4,
- "height": 28.8
- },
- {
- "width": 166.8,
- "height": 15.2
- },
- {
- "width": 164,
- "height": 6.4
- },
- {
- "width": 162.4,
- "height": 0.8
- },
- {
- "width": 161.2,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.9808927,
- 0.8211168,
- 0.61845565,
- 0.43834114,
- 0.29850912,
- 0.19755232,
- 0.12793064,
- 0.08142871,
- 0.051099956,
- 0.031684637,
- 0.019442618,
- 0.011821032,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
deleted file mode 100644
index 01bc852..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
+++ /dev/null
@@ -1,644 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656,
- 672,
- 688,
- 704,
- 720,
- 736,
- 752,
- 768,
- 784,
- 800,
- 816,
- 832,
- 848,
- 864,
- 880,
- 896,
- 912,
- 928,
- 944,
- 960,
- 976
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50.8,
- "y": 52
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 52.4,
- "y": 50
- },
- {
- "x": 55.6,
- "y": 50
- },
- {
- "x": 58.4,
- "y": 50
- },
- {
- "x": 60.4,
- "y": 50
- },
- {
- "x": 61.6,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 63.2,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 390
- },
- {
- "width": 188,
- "height": 390
- },
- {
- "width": 188,
- "height": 379.2
- },
- {
- "width": 188,
- "height": 371.2
- },
- {
- "width": 188,
- "height": 363.2
- },
- {
- "width": 188,
- "height": 355.2
- },
- {
- "width": 188,
- "height": 347.2
- },
- {
- "width": 188,
- "height": 339.2
- },
- {
- "width": 188,
- "height": 331.2
- },
- {
- "width": 188,
- "height": 323.2
- },
- {
- "width": 188,
- "height": 323.2
- },
- {
- "width": 188,
- "height": 314.8
- },
- {
- "width": 188,
- "height": 306.8
- },
- {
- "width": 188,
- "height": 298.8
- },
- {
- "width": 188,
- "height": 290.8
- },
- {
- "width": 188,
- "height": 282.8
- },
- {
- "width": 188,
- "height": 274.8
- },
- {
- "width": 188,
- "height": 266.8
- },
- {
- "width": 188,
- "height": 258.8
- },
- {
- "width": 188,
- "height": 258.8
- },
- {
- "width": 188,
- "height": 250.4
- },
- {
- "width": 188,
- "height": 242.4
- },
- {
- "width": 188,
- "height": 234.4
- },
- {
- "width": 188,
- "height": 226.4
- },
- {
- "width": 188,
- "height": 218.4
- },
- {
- "width": 188,
- "height": 210.4
- },
- {
- "width": 188,
- "height": 202.4
- },
- {
- "width": 188,
- "height": 194.4
- },
- {
- "width": 188,
- "height": 194.4
- },
- {
- "width": 188,
- "height": 185.6
- },
- {
- "width": 188,
- "height": 178
- },
- {
- "width": 188,
- "height": 170
- },
- {
- "width": 188,
- "height": 161.6
- },
- {
- "width": 188,
- "height": 161.6
- },
- {
- "width": 188,
- "height": 144.8
- },
- {
- "width": 188,
- "height": 118.8
- },
- {
- "width": 188,
- "height": 92
- },
- {
- "width": 183.6,
- "height": 68
- },
- {
- "width": 176.8,
- "height": 48.4
- },
- {
- "width": 171.6,
- "height": 32
- },
- {
- "width": 167.6,
- "height": 18
- },
- {
- "width": 164.8,
- "height": 8.8
- },
- {
- "width": 162.8,
- "height": 2.8
- },
- {
- "width": 161.6,
- "height": 0
- },
- {
- "width": 160.8,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.9967737,
- 0.86538374,
- 0.66414475,
- 0.47619528,
- 0.32686388,
- 0.21757984,
- 0.14153665,
- 0.09041709,
- 0.05691254,
- 0.035380244,
- 0.02175957,
- 0.01325649,
- 0.008007765,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
deleted file mode 100644
index b6e423a..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
+++ /dev/null
@@ -1,544 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656,
- 672,
- 688,
- 704,
- 720,
- 736,
- 752,
- 768,
- 784,
- 800,
- 816
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 61.6,
- "y": 50
- },
- {
- "x": 60.8,
- "y": 50
- },
- {
- "x": 59.6,
- "y": 50
- },
- {
- "x": 58.4,
- "y": 50
- },
- {
- "x": 57.2,
- "y": 50
- },
- {
- "x": 56,
- "y": 50
- },
- {
- "x": 55.2,
- "y": 50
- },
- {
- "x": 54,
- "y": 50
- },
- {
- "x": 54,
- "y": 50
- },
- {
- "x": 52.8,
- "y": 50
- },
- {
- "x": 51.6,
- "y": 50
- },
- {
- "x": 50.4,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "width": 162.4,
- "height": 1.6
- },
- {
- "width": 162.4,
- "height": 1.6
- },
- {
- "width": 164.8,
- "height": 3.2
- },
- {
- "width": 166.8,
- "height": 4.8
- },
- {
- "width": 169.2,
- "height": 6.4
- },
- {
- "width": 171.6,
- "height": 8
- },
- {
- "width": 173.6,
- "height": 9.6
- },
- {
- "width": 176,
- "height": 11.2
- },
- {
- "width": 178,
- "height": 12.8
- },
- {
- "width": 180.4,
- "height": 14.4
- },
- {
- "width": 180.4,
- "height": 14.4
- },
- {
- "width": 182.8,
- "height": 16.4
- },
- {
- "width": 185.2,
- "height": 18
- },
- {
- "width": 187.2,
- "height": 19.6
- },
- {
- "width": 188,
- "height": 25.6
- },
- {
- "width": 188,
- "height": 36.4
- },
- {
- "width": 188,
- "height": 45.6
- },
- {
- "width": 188,
- "height": 59.2
- },
- {
- "width": 188,
- "height": 72.8
- },
- {
- "width": 188,
- "height": 79.6
- },
- {
- "width": 188,
- "height": 92.8
- },
- {
- "width": 188,
- "height": 104.4
- },
- {
- "width": 188,
- "height": 115.2
- },
- {
- "width": 188,
- "height": 125.2
- },
- {
- "width": 188,
- "height": 134.8
- },
- {
- "width": 188,
- "height": 143.2
- },
- {
- "width": 188,
- "height": 151.2
- },
- {
- "width": 188,
- "height": 158.8
- },
- {
- "width": 188,
- "height": 160
- },
- {
- "width": 188,
- "height": 167.2
- },
- {
- "width": 188,
- "height": 174.4
- },
- {
- "width": 188,
- "height": 180.8
- },
- {
- "width": 188,
- "height": 187.6
- },
- {
- "width": 188,
- "height": 188
- },
- {
- "width": 188,
- "height": 207.2
- },
- {
- "width": 188,
- "height": 240
- },
- {
- "width": 188,
- "height": 275.2
- },
- {
- "width": 188,
- "height": 306.8
- },
- {
- "width": 188,
- "height": 333.2
- },
- {
- "width": 188,
- "height": 353.6
- },
- {
- "width": 188,
- "height": 368.8
- },
- {
- "width": 188,
- "height": 380
- },
- {
- "width": 188,
- "height": 387.6
- },
- {
- "width": 188,
- "height": 392.4
- },
- {
- "width": 188,
- "height": 395.6
- },
- {
- "width": 188,
- "height": 398
- },
- {
- "width": 188,
- "height": 398.8
- },
- {
- "width": 188,
- "height": 399.6
- },
- {
- "width": 188,
- "height": 400
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- 0,
- 0,
- 0,
- 0,
- 0.0067873597,
- 0.0612576,
- 0.19080025,
- 0.39327443,
- 0.5711931,
- 0.70855826,
- 0.8074064,
- 0.8754226,
- 0.9207788,
- 0.95032376,
- 0.9692185,
- 0.98112255,
- 0.9885286,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
deleted file mode 100644
index a82db34..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
+++ /dev/null
@@ -1,444 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50.4,
- "y": 50.8
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 51.2,
- "y": 50
- },
- {
- "x": 55.6,
- "y": 50
- },
- {
- "x": 58.8,
- "y": 50
- },
- {
- "x": 60.8,
- "y": 50
- },
- {
- "x": 62,
- "y": 50
- },
- {
- "x": 63.2,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 389.6
- },
- {
- "width": 188,
- "height": 378.8
- },
- {
- "width": 188,
- "height": 366
- },
- {
- "width": 188,
- "height": 352
- },
- {
- "width": 188,
- "height": 352
- },
- {
- "width": 188,
- "height": 316.8
- },
- {
- "width": 188,
- "height": 261.2
- },
- {
- "width": 188,
- "height": 202.8
- },
- {
- "width": 188,
- "height": 150.8
- },
- {
- "width": 188,
- "height": 107.6
- },
- {
- "width": 186,
- "height": 74.4
- },
- {
- "width": 177.2,
- "height": 49.6
- },
- {
- "width": 170.8,
- "height": 29.6
- },
- {
- "width": 166.8,
- "height": 12.8
- },
- {
- "width": 164,
- "height": 2.4
- },
- {
- "width": 162,
- "height": 0
- },
- {
- "width": 160.8,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.9833227,
- 0.8263634,
- 0.623688,
- 0.44261706,
- 0.3016883,
- 0.1997872,
- 0.12944388,
- 0.08242595,
- 0.051743627,
- 0.032093227,
- 0.019698441,
- 0.0119793415,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
deleted file mode 100644
index 1cd971a..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
+++ /dev/null
@@ -1,724 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464,
- 480,
- 496,
- 512,
- 528,
- 544,
- 560,
- 576,
- 592,
- 608,
- 624,
- 640,
- 656,
- 672,
- 688,
- 704,
- 720,
- 736,
- 752,
- 768,
- 784,
- 800,
- 816,
- 832,
- 848,
- 864,
- 880,
- 896,
- 912,
- 928,
- 944,
- 960,
- 976,
- 992,
- 1008,
- 1024,
- 1040,
- 1056,
- 1072,
- 1088,
- 1104
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 62,
- "y": 50
- },
- {
- "x": 61.2,
- "y": 50
- },
- {
- "x": 60.4,
- "y": 50
- },
- {
- "x": 59.6,
- "y": 50
- },
- {
- "x": 58.8,
- "y": 50
- },
- {
- "x": 58,
- "y": 50
- },
- {
- "x": 57.2,
- "y": 50
- },
- {
- "x": 56.4,
- "y": 50
- },
- {
- "x": 55.6,
- "y": 50
- },
- {
- "x": 55.2,
- "y": 50
- },
- {
- "x": 54.4,
- "y": 50
- },
- {
- "x": 53.6,
- "y": 50
- },
- {
- "x": 53.2,
- "y": 50
- },
- {
- "x": 52.8,
- "y": 50
- },
- {
- "x": 52,
- "y": 50
- },
- {
- "x": 51.6,
- "y": 50
- },
- {
- "x": 51.2,
- "y": 50
- },
- {
- "x": 50.8,
- "y": 50
- },
- {
- "x": 50.4,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50.4,
- "y": 50
- },
- {
- "x": 50.8,
- "y": 50
- },
- {
- "x": 51.2,
- "y": 50
- },
- {
- "x": 51.6,
- "y": 50
- },
- {
- "x": 52,
- "y": 50
- },
- {
- "x": 52.8,
- "y": 50
- },
- {
- "x": 53.2,
- "y": 50
- },
- {
- "x": 53.6,
- "y": 50
- },
- {
- "x": 54.4,
- "y": 50
- },
- {
- "x": 55.2,
- "y": 50
- },
- {
- "x": 55.6,
- "y": 50
- },
- {
- "x": 56.4,
- "y": 50
- },
- {
- "x": 57.2,
- "y": 50
- },
- {
- "x": 58,
- "y": 50
- },
- {
- "x": 58.8,
- "y": 50
- },
- {
- "x": 59.6,
- "y": 50
- },
- {
- "x": 60.4,
- "y": 50
- },
- {
- "x": 61.2,
- "y": 50
- },
- {
- "x": 62,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "width": 162.4,
- "height": 1.6
- },
- {
- "width": 164,
- "height": 2.8
- },
- {
- "width": 166,
- "height": 4
- },
- {
- "width": 167.6,
- "height": 5.2
- },
- {
- "width": 169.2,
- "height": 6.4
- },
- {
- "width": 170.8,
- "height": 7.6
- },
- {
- "width": 172.4,
- "height": 8.8
- },
- {
- "width": 174,
- "height": 10
- },
- {
- "width": 175.2,
- "height": 10.8
- },
- {
- "width": 176.8,
- "height": 12
- },
- {
- "width": 178,
- "height": 12.8
- },
- {
- "width": 179.2,
- "height": 13.6
- },
- {
- "width": 180.8,
- "height": 14.8
- },
- {
- "width": 182,
- "height": 15.6
- },
- {
- "width": 182.8,
- "height": 16.4
- },
- {
- "width": 184,
- "height": 17.2
- },
- {
- "width": 184.8,
- "height": 17.6
- },
- {
- "width": 186,
- "height": 18.4
- },
- {
- "width": 186.8,
- "height": 19.2
- },
- {
- "width": 187.6,
- "height": 19.6
- },
- {
- "width": 188,
- "height": 21.2
- },
- {
- "width": 188,
- "height": 24.8
- },
- {
- "width": 188,
- "height": 30
- },
- {
- "width": 188,
- "height": 38
- },
- {
- "width": 188,
- "height": 46
- },
- {
- "width": 188,
- "height": 54
- },
- {
- "width": 188,
- "height": 61.2
- },
- {
- "width": 188,
- "height": 66.8
- },
- {
- "width": 188,
- "height": 71.6
- },
- {
- "width": 188,
- "height": 75.6
- },
- {
- "width": 188,
- "height": 78
- },
- {
- "width": 188,
- "height": 79.6
- },
- {
- "width": 188,
- "height": 80.8
- },
- {
- "width": 188,
- "height": 80.8
- },
- {
- "width": 188,
- "height": 80.4
- },
- {
- "width": 188,
- "height": 79.6
- },
- {
- "width": 187.6,
- "height": 78
- },
- {
- "width": 186.8,
- "height": 76.4
- },
- {
- "width": 186,
- "height": 74
- },
- {
- "width": 184.8,
- "height": 71.6
- },
- {
- "width": 184,
- "height": 69.2
- },
- {
- "width": 182.8,
- "height": 66
- },
- {
- "width": 182,
- "height": 62.8
- },
- {
- "width": 180.8,
- "height": 59.2
- },
- {
- "width": 179.2,
- "height": 55.6
- },
- {
- "width": 178,
- "height": 52
- },
- {
- "width": 176.8,
- "height": 48
- },
- {
- "width": 175.2,
- "height": 44
- },
- {
- "width": 174,
- "height": 40
- },
- {
- "width": 172.4,
- "height": 37.6
- },
- {
- "width": 170.8,
- "height": 38
- },
- {
- "width": 169.2,
- "height": 30.4
- },
- {
- "width": 167.6,
- "height": 25.2
- },
- {
- "width": 166,
- "height": 20.4
- },
- {
- "width": 164,
- "height": 16
- },
- {
- "width": 162.4,
- "height": 12.4
- },
- {
- "width": 160.8,
- "height": 9.2
- },
- {
- "width": 160,
- "height": 6.8
- },
- {
- "width": 160,
- "height": 5.2
- },
- {
- "width": 160,
- "height": 3.6
- },
- {
- "width": 160,
- "height": 2.4
- },
- {
- "width": 160,
- "height": 1.6
- },
- {
- "width": 160,
- "height": 0.8
- },
- {
- "width": 160,
- "height": 0.4
- },
- {
- "width": 160,
- "height": 0.4
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- {
- "type": "not_found"
- },
- 0,
- 0,
- 0,
- 0,
- 0.012518823,
- 0.0741024,
- 0.2254293,
- 0.42628878,
- 0.5976641,
- 0.7280312,
- 0.82100236,
- 0.8845844,
- 0.9267946,
- 0.95419544,
- 0.9716705,
- 0.98265487,
- 0.98947525,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.9944124,
- 0.9417388,
- 0.8184184,
- 0.6157812,
- 0.4361611,
- 0.2968906,
- 0.19641554,
- 0.12716137,
- 0.080921985,
- 0.050773025,
- 0.03147719,
- 0.019312752,
- 0.011740655,
- 0
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
deleted file mode 100644
index 1030455..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
+++ /dev/null
@@ -1,324 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336,
- 352,
- 368,
- 384,
- 400,
- 416,
- 432,
- 448,
- 464
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 53.2,
- "y": 50
- },
- {
- "x": 57.2,
- "y": 50
- },
- {
- "x": 59.6,
- "y": 50
- },
- {
- "x": 61.6,
- "y": 50
- },
- {
- "x": 62.8,
- "y": 50
- },
- {
- "x": 63.6,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 64,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 400
- },
- {
- "width": 188,
- "height": 372
- },
- {
- "width": 188,
- "height": 312.8
- },
- {
- "width": 188,
- "height": 246.8
- },
- {
- "width": 188,
- "height": 185.2
- },
- {
- "width": 188,
- "height": 133.6
- },
- {
- "width": 188,
- "height": 93.2
- },
- {
- "width": 181.6,
- "height": 62.8
- },
- {
- "width": 174,
- "height": 40.8
- },
- {
- "width": 168.8,
- "height": 22.4
- },
- {
- "width": 165.2,
- "height": 10
- },
- {
- "width": 162.8,
- "height": 2.4
- },
- {
- "width": 161.2,
- "height": 0
- },
- {
- "width": 160.4,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 160,
- "height": 0
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 0.91758585,
- 0.72435355,
- 0.52812576,
- 0.3665868,
- 0.24600428,
- 0.16102076,
- 0.103373945,
- 0.06533456,
- 0.04075712,
- 0.025142312,
- 0.015358448,
- 0.0092999935,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
deleted file mode 100644
index 622c29e..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
+++ /dev/null
@@ -1,244 +0,0 @@
-{
- "frame_ids": [
- 0,
- 16,
- 32,
- 48,
- 64,
- 80,
- 96,
- 112,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 272,
- 288,
- 304,
- 320,
- 336
- ],
- "features": [
- {
- "name": "RevealElement_position",
- "type": "dpOffset",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "x": 64,
- "y": 50
- },
- {
- "x": 59.2,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- },
- {
- "x": 50,
- "y": 50
- }
- ]
- },
- {
- "name": "RevealElement_size",
- "type": "dpSize",
- "data_points": [
- {
- "type": "not_found"
- },
- {
- "width": 160,
- "height": 0
- },
- {
- "width": 169.6,
- "height": 6.8
- },
- {
- "width": 188,
- "height": 26.8
- },
- {
- "width": 188,
- "height": 95.6
- },
- {
- "width": 188,
- "height": 163.2
- },
- {
- "width": 188,
- "height": 222
- },
- {
- "width": 188,
- "height": 269.6
- },
- {
- "width": 188,
- "height": 307.2
- },
- {
- "width": 188,
- "height": 335.2
- },
- {
- "width": 188,
- "height": 356
- },
- {
- "width": 188,
- "height": 370.4
- },
- {
- "width": 188,
- "height": 380.8
- },
- {
- "width": 188,
- "height": 387.6
- },
- {
- "width": 188,
- "height": 392.4
- },
- {
- "width": 188,
- "height": 395.2
- },
- {
- "width": 188,
- "height": 397.2
- },
- {
- "width": 188,
- "height": 398
- },
- {
- "width": 188,
- "height": 398.8
- },
- {
- "width": 188,
- "height": 399.2
- },
- {
- "width": 188,
- "height": 399.2
- },
- {
- "width": 188,
- "height": 399.6
- }
- ]
- },
- {
- "name": "RevealElement_alpha",
- "type": "float",
- "data_points": [
- {
- "type": "not_found"
- },
- 0,
- 0.05698657,
- 0.24197984,
- 0.44158113,
- 0.6097554,
- 0.73685503,
- 0.8271309,
- 0.8886989,
- 0.9294886,
- 0.9559254,
- 0.97276413,
- 0.98333716,
- 0.98989624,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1,
- 1
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
index f4e2328..1bc83e0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
@@ -37,7 +37,6 @@
import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.FeatureCaptures.elementAlpha
@@ -47,8 +46,8 @@
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.featureOfElement
import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
import kotlin.math.sin
import kotlinx.coroutines.CoroutineScope
import org.junit.Rule
@@ -63,26 +62,48 @@
import platform.test.motion.compose.recordMotion
import platform.test.motion.compose.runTest
import platform.test.motion.testing.createGoldenPathManager
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.PathConfig
+import platform.test.screenshot.PathElementNoContext
-@RunWith(AndroidJUnit4::class)
@MotionTest
-class ContentRevealTest {
+@RunWith(ParameterizedAndroidJunit4::class)
+class ContentRevealTest(private val isFloating: Boolean) {
+
+ private val pathConfig =
+ PathConfig(
+ PathElementNoContext("floating", isDir = false) {
+ if (isFloating) "floating" else "edge"
+ }
+ )
private val goldenPaths =
- createGoldenPathManager("frameworks/base/packages/SystemUI/compose/scene/tests/goldens")
+ createGoldenPathManager(
+ "frameworks/base/packages/SystemUI/compose/scene/tests/goldens",
+ pathConfig,
+ )
@get:Rule val motionRule = createFixedConfigurationComposeMotionTestRule(goldenPaths)
private val fakeHaptics = FakeHaptics()
+ private val motionSpec = VerticalExpandContainerSpec(isFloating)
+
@Test
fun verticalReveal_triggeredRevealOpenTransition() {
- assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneClosed, SceneOpen))
+ assertVerticalContainerRevealMotion(
+ TriggeredRevealMotion(SceneClosed, SceneOpen),
+ "verticalReveal_triggeredRevealOpenTransition",
+ )
}
@Test
fun verticalReveal_triggeredRevealCloseTransition() {
- assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneOpen, SceneClosed))
+ assertVerticalContainerRevealMotion(
+ TriggeredRevealMotion(SceneOpen, SceneClosed),
+ "verticalReveal_triggeredRevealCloseTransition",
+ )
}
@Test
@@ -90,15 +111,18 @@
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneClosed) {
val gestureDurationMillis = 1000L
+ // detach position for the floating container is larger
+ val gestureHeight = if (isFloating) 160.dp.toPx() else 100.dp.toPx()
swipe(
curve = {
val progress = it / gestureDurationMillis.toFloat()
- val y = sin(progress * Math.PI).toFloat() * 100.dp.toPx()
+ val y = sin(progress * Math.PI).toFloat() * gestureHeight
Offset(centerX, y)
},
gestureDurationMillis,
)
- }
+ },
+ "verticalReveal_gesture_magneticDetachAndReattach",
)
}
@@ -107,7 +131,8 @@
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneClosed) {
swipeDown(endY = 200.dp.toPx(), durationMillis = 500)
- }
+ },
+ "verticalReveal_gesture_dragOpen",
)
}
@@ -117,7 +142,8 @@
GestureRevealMotion(SceneClosed) {
val end = Offset(centerX, 80.dp.toPx())
swipeWithVelocity(start = topCenter, end = end, endVelocity = FlingVelocity.toPx())
- }
+ },
+ "verticalReveal_gesture_flingOpen",
)
}
@@ -126,7 +152,8 @@
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneOpen) {
swipeUp(200.dp.toPx(), 0.dp.toPx(), durationMillis = 500)
- }
+ },
+ "verticalReveal_gesture_dragFullyClose",
)
}
@@ -134,8 +161,9 @@
fun verticalReveal_gesture_dragHalfClose() {
assertVerticalContainerRevealMotion(
GestureRevealMotion(SceneOpen) {
- swipeUp(350.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
- }
+ swipeUp(250.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
+ },
+ "verticalReveal_gesture_dragHalfClose",
)
}
@@ -146,7 +174,8 @@
val start = Offset(centerX, 260.dp.toPx())
val end = Offset(centerX, 200.dp.toPx())
swipeWithVelocity(start, end, FlingVelocity.toPx())
- }
+ },
+ "verticalReveal_gesture_flingClose",
)
}
@@ -164,11 +193,14 @@
val gestureControl: TouchInjectionScope.() -> Unit,
) : RevealMotion
- private fun assertVerticalContainerRevealMotion(testInstructions: RevealMotion) =
+ private fun assertVerticalContainerRevealMotion(
+ testInstructions: RevealMotion,
+ goldenName: String,
+ ) =
motionRule.runTest {
val transitions = transitions {
from(SceneClosed, to = SceneOpen) {
- verticalContainerReveal(RevealElement, MotionSpec, fakeHaptics)
+ verticalContainerReveal(RevealElement, motionSpec, fakeHaptics)
}
}
@@ -221,9 +253,9 @@
SceneTransitionLayoutForTesting(
state,
modifier =
- Modifier.padding(50.dp)
+ Modifier.padding(5.dp)
.background(Color.Yellow)
- .size(ContainerSize.width, ContainerSize.height + 200.dp)
+ .size(ContainerSize.width, ContainerSize.height + 100.dp)
.testTag("stl"),
) {
scene(
@@ -241,7 +273,7 @@
recordingSpec,
)
- assertThat(motion).timeSeriesMatchesGolden()
+ assertThat(motion).timeSeriesMatchesGolden(goldenName)
}
@Composable
@@ -256,7 +288,7 @@
modifier =
Modifier.element(RevealElement)
.size(ContainerSize)
- .edgeContainerExpansionBackground(Color.DarkGray, MotionSpec)
+ .verticalExpandContainerBackground(Color.DarkGray, motionSpec)
)
}
}
@@ -266,7 +298,9 @@
}
companion object {
- val ContainerSize = DpSize(200.dp, 400.dp)
+ @get:Parameters @JvmStatic val parameterValues = listOf(true, false)
+
+ val ContainerSize = DpSize(150.dp, 300.dp)
val FlingVelocity = 1000.dp // dp/sec
@@ -274,6 +308,5 @@
val SceneOpen = SceneKey("SceneB")
val RevealElement = ElementKey("RevealElement")
- val MotionSpec = EdgeContainerExpansionSpec()
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index 37acbe2..5f71b19 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -22,10 +22,10 @@
import com.android.systemui.log.core.Logger
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
@@ -111,7 +111,7 @@
override fun onZenDataChanged(data: ZenData) {}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {
view.updateAxes(axes)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index bc4bdf4..3cfa78d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -26,6 +26,7 @@
import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockEventListener
@@ -33,7 +34,6 @@
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
@@ -232,7 +232,7 @@
override fun onZenDataChanged(data: ZenData) {}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {}
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {}
}
open inner class DefaultClockAnimations(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index c3935e6..d778bc0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -20,6 +20,8 @@
import android.view.LayoutInflater
import com.android.systemui.customization.R
import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AxisPresetConfig
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
import com.android.systemui.plugins.clocks.ClockLogger
@@ -28,7 +30,7 @@
import com.android.systemui.plugins.clocks.ClockPickerConfig
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockSettings
-import com.android.systemui.shared.clocks.FlexClockController.Companion.AXIS_PRESETS
+import com.android.systemui.shared.clocks.FlexClockController.Companion.buildPresetGroup
import com.android.systemui.shared.clocks.FlexClockController.Companion.getDefaultAxes
private val TAG = DefaultClockProvider::class.simpleName
@@ -80,7 +82,7 @@
return if (isClockReactiveVariantsEnabled) {
val buffers = messageBuffers ?: ClockMessageBuffers(ClockLogger.DEFAULT_MESSAGE_BUFFER)
val fontAxes = getDefaultAxes(settings).merge(settings.axes)
- val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
+ val clockSettings = settings.copy(axes = ClockAxisStyle(fontAxes))
val typefaceCache =
TypefaceCache(buffers.infraMessageBuffer, NUM_CLOCK_FONT_ANIMATION_STEPS) {
FLEX_TYPEFACE
@@ -106,17 +108,35 @@
throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
}
- return ClockPickerConfig(
- settings.clockId ?: DEFAULT_CLOCK_ID,
- resources.getString(R.string.clock_default_name),
- resources.getString(R.string.clock_default_description),
- resources.getDrawable(R.drawable.clock_default_thumbnail, null),
- isReactiveToTone = true,
- axes =
- if (!isClockReactiveVariantsEnabled) emptyList()
- else getDefaultAxes(settings).merge(settings.axes),
- axisPresets = if (!isClockReactiveVariantsEnabled) emptyList() else AXIS_PRESETS,
- )
+ if (!isClockReactiveVariantsEnabled) {
+ return ClockPickerConfig(
+ settings.clockId ?: DEFAULT_CLOCK_ID,
+ resources.getString(R.string.clock_default_name),
+ resources.getString(R.string.clock_default_description),
+ resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+ isReactiveToTone = true,
+ axes = emptyList(),
+ presetConfig = null,
+ )
+ } else {
+ val fontAxes = getDefaultAxes(settings).merge(settings.axes)
+ return ClockPickerConfig(
+ settings.clockId ?: DEFAULT_CLOCK_ID,
+ resources.getString(R.string.clock_default_name),
+ resources.getString(R.string.clock_default_description),
+ resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+ isReactiveToTone = true,
+ axes = fontAxes,
+ presetConfig =
+ AxisPresetConfig(
+ listOf(
+ buildPresetGroup(resources, isRound = true),
+ buildPresetGroup(resources, isRound = false),
+ )
+ )
+ .let { cfg -> cfg.copy(current = cfg.findStyle(ClockAxisStyle(fontAxes))) },
+ )
+ }
}
companion object {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 5acd446..96c3ac7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,20 +16,24 @@
package com.android.systemui.shared.clocks
+import android.content.res.Resources
import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.AxisPresetConfig
import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockEventListener
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFontAxis
import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.put
+import com.android.systemui.shared.clocks.FontUtils.toClockAxis
import com.android.systemui.shared.clocks.view.FlexClockView
import java.io.PrintWriter
import java.util.Locale
@@ -96,8 +100,8 @@
largeClock.events.onZenDataChanged(data)
}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
- val fontAxes = getDefaultAxes(clockCtx.settings).merge(axes).map { it.toSetting() }
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {
+ val fontAxes = ClockAxisStyle(getDefaultAxes(clockCtx.settings).merge(axes))
smallClock.events.onFontAxesChanged(fontAxes)
largeClock.events.onFontAxesChanged(fontAxes)
}
@@ -162,66 +166,46 @@
),
)
- private val LEGACY_FLEX_SETTINGS =
- listOf(
- GSFAxes.WEIGHT.toClockAxisSetting(600f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- )
+ private val LEGACY_FLEX_SETTINGS = ClockAxisStyle {
+ put(GSFAxes.WEIGHT, 600f)
+ put(GSFAxes.WIDTH, 100f)
+ put(GSFAxes.ROUND, 100f)
+ put(GSFAxes.SLANT, 0f)
+ }
- val AXIS_PRESETS =
- listOf(
- FONT_AXES.map { it.toSetting() },
- LEGACY_FLEX_SETTINGS,
- listOf( // Porcelain
- GSFAxes.WEIGHT.toClockAxisSetting(500f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Midnight
- GSFAxes.WEIGHT.toClockAxisSetting(300f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(-10f),
- ),
- listOf( // Sterling
- GSFAxes.WEIGHT.toClockAxisSetting(1000f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Smoky Green
- GSFAxes.WEIGHT.toClockAxisSetting(150f),
- GSFAxes.WIDTH.toClockAxisSetting(50f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Iris
- GSFAxes.WEIGHT.toClockAxisSetting(500f),
- GSFAxes.WIDTH.toClockAxisSetting(100f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
- listOf( // Margarita
- GSFAxes.WEIGHT.toClockAxisSetting(300f),
- GSFAxes.WIDTH.toClockAxisSetting(30f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(-10f),
- ),
- listOf( // Raspberry
- GSFAxes.WEIGHT.toClockAxisSetting(700f),
- GSFAxes.WIDTH.toClockAxisSetting(140f),
- GSFAxes.ROUND.toClockAxisSetting(100f),
- GSFAxes.SLANT.toClockAxisSetting(-7f),
- ),
- listOf( // Ultra Blue
- GSFAxes.WEIGHT.toClockAxisSetting(850f),
- GSFAxes.WIDTH.toClockAxisSetting(130f),
- GSFAxes.ROUND.toClockAxisSetting(0f),
- GSFAxes.SLANT.toClockAxisSetting(0f),
- ),
+ private val PRESET_COUNT = 8
+ private val PRESET_WIDTH_INIT = 30f
+ private val PRESET_WIDTH_STEP = 12.5f
+ private val PRESET_WEIGHT_INIT = 800f
+ private val PRESET_WEIGHT_STEP = -100f
+ private val BASE_PRESETS: List<ClockAxisStyle> = run {
+ val presets = mutableListOf<ClockAxisStyle>()
+ var weight = PRESET_WEIGHT_INIT
+ var width = PRESET_WIDTH_INIT
+ for (i in 1..PRESET_COUNT) {
+ presets.add(
+ ClockAxisStyle {
+ put(GSFAxes.WEIGHT, weight)
+ put(GSFAxes.WIDTH, width)
+ put(GSFAxes.ROUND, 0f)
+ put(GSFAxes.SLANT, 0f)
+ }
+ )
+
+ weight += PRESET_WEIGHT_STEP
+ width += PRESET_WIDTH_STEP
+ }
+
+ return@run presets
+ }
+
+ fun buildPresetGroup(resources: Resources, isRound: Boolean): AxisPresetConfig.Group {
+ val round = if (isRound) GSFAxes.ROUND.maxValue else GSFAxes.ROUND.minValue
+ return AxisPresetConfig.Group(
+ presets = BASE_PRESETS.map { it.copy { put(GSFAxes.ROUND, round) } },
+ // TODO(b/395647577): Placeholder Icon; Replace or remove
+ icon = resources.getDrawable(R.drawable.clock_default_thumbnail, null),
)
+ }
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 578a489..171a68f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -25,16 +25,18 @@
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
import com.android.systemui.plugins.clocks.ClockFaceLayout
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.get
+import com.android.systemui.shared.clocks.FontUtils.set
import com.android.systemui.shared.clocks.ViewUtils.computeLayoutDiff
import com.android.systemui.shared.clocks.view.FlexClockView
import com.android.systemui.shared.clocks.view.HorizontalAlignment
@@ -129,17 +131,10 @@
layerController.faceEvents.onThemeChanged(theme)
}
- override fun onFontAxesChanged(settings: List<ClockFontAxisSetting>) {
- var axes = settings
- if (!isLargeClock) {
- axes =
- axes.map { axis ->
- if (axis.key == GSFAxes.WIDTH.tag && axis.value > SMALL_CLOCK_MAX_WDTH) {
- axis.copy(value = SMALL_CLOCK_MAX_WDTH)
- } else {
- axis
- }
- }
+ override fun onFontAxesChanged(settings: ClockAxisStyle) {
+ var axes = ClockAxisStyle(settings)
+ if (!isLargeClock && axes[GSFAxes.WIDTH] > SMALL_CLOCK_MAX_WDTH) {
+ axes[GSFAxes.WIDTH] = SMALL_CLOCK_MAX_WDTH
}
layerController.events.onFontAxesChanged(axes)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
index 212b1e2..722d76b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
@@ -18,26 +18,36 @@
import com.android.systemui.animation.AxisDefinition
import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockFontAxis
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-fun AxisDefinition.toClockAxis(
- type: AxisType,
- currentValue: Float? = null,
- name: String,
- description: String,
-): ClockFontAxis {
- return ClockFontAxis(
- key = this.tag,
- type = type,
- maxValue = this.maxValue,
- minValue = this.minValue,
- currentValue = currentValue ?: this.defaultValue,
- name = name,
- description = description,
- )
-}
+object FontUtils {
+ fun AxisDefinition.toClockAxis(
+ type: AxisType,
+ currentValue: Float? = null,
+ name: String,
+ description: String,
+ ): ClockFontAxis {
+ return ClockFontAxis(
+ key = this.tag,
+ type = type,
+ maxValue = this.maxValue,
+ minValue = this.minValue,
+ currentValue = currentValue ?: this.defaultValue,
+ name = name,
+ description = description,
+ )
+ }
-fun AxisDefinition.toClockAxisSetting(value: Float? = null): ClockFontAxisSetting {
- return ClockFontAxisSetting(this.tag, value ?: this.defaultValue)
+ fun ClockAxisStyle.put(def: AxisDefinition, value: Float? = null) {
+ this.put(def.tag, value ?: def.defaultValue)
+ }
+
+ operator fun ClockAxisStyle.set(def: AxisDefinition, value: Float) {
+ this[def.tag] = value
+ }
+
+ operator fun ClockAxisStyle.get(def: AxisDefinition): Float {
+ return this[def.tag] ?: def.defaultValue
+ }
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index 1d963af..7be9a93 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -26,10 +26,10 @@
import com.android.systemui.log.core.Logger
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockEvents
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
@@ -172,7 +172,7 @@
override fun onZenDataChanged(data: ZenData) {}
- override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+ override fun onFontAxesChanged(axes: ClockAxisStyle) {
view.updateAxes(axes)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index ba32ab0..4531aed 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -26,7 +26,7 @@
import androidx.core.view.children
import com.android.app.animation.Interpolators
import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockLogger
import com.android.systemui.plugins.clocks.VPoint
import com.android.systemui.plugins.clocks.VPointF
@@ -272,7 +272,7 @@
invalidate()
}
- fun updateAxes(axes: List<ClockFontAxisSetting>) {
+ fun updateAxes(axes: ClockAxisStyle) {
childViews.forEach { view -> view.updateAxes(axes) }
requestLayout()
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index da9e26a..377a24c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -36,13 +36,12 @@
import android.widget.TextView
import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.AxisDefinition
import com.android.systemui.animation.GSFAxes
import com.android.systemui.animation.TextAnimator
import com.android.systemui.animation.TextAnimatorListener
import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.replace
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.toFVar
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockLogger
import com.android.systemui.plugins.clocks.VPoint
import com.android.systemui.plugins.clocks.VPointF
@@ -56,9 +55,9 @@
import com.android.systemui.shared.clocks.DimensionParser
import com.android.systemui.shared.clocks.FLEX_CLOCK_ID
import com.android.systemui.shared.clocks.FontTextStyle
+import com.android.systemui.shared.clocks.FontUtils.set
import com.android.systemui.shared.clocks.ViewUtils.measuredSize
import com.android.systemui.shared.clocks.ViewUtils.size
-import com.android.systemui.shared.clocks.toClockAxisSetting
import java.lang.Thread
import kotlin.math.max
import kotlin.math.min
@@ -123,9 +122,9 @@
private val isLegacyFlex = clockCtx.settings.clockId == FLEX_CLOCK_ID
private val fixedAodAxes =
when {
- !isLegacyFlex -> listOf(AOD_WEIGHT_AXIS, WIDTH_AXIS)
- isLargeClock -> listOf(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
- else -> listOf(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+ !isLegacyFlex -> fromAxes(AOD_WEIGHT_AXIS, WIDTH_AXIS)
+ isLargeClock -> fromAxes(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+ else -> fromAxes(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
}
private var lsFontVariation: String
@@ -135,11 +134,11 @@
init {
val roundAxis = if (!isLegacyFlex) ROUND_AXIS else FLEX_ROUND_AXIS
val lsFontAxes =
- if (!isLegacyFlex) listOf(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
- else listOf(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
+ if (!isLegacyFlex) fromAxes(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
+ else fromAxes(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
lsFontVariation = lsFontAxes.toFVar()
- aodFontVariation = (fixedAodAxes + listOf(roundAxis, SLANT_AXIS)).toFVar()
+ aodFontVariation = fixedAodAxes.copyWith(fromAxes(roundAxis, SLANT_AXIS)).toFVar()
fidgetFontVariation = buildFidgetVariation(lsFontAxes).toFVar()
}
@@ -201,9 +200,9 @@
invalidate()
}
- fun updateAxes(lsAxes: List<ClockFontAxisSetting>) {
+ fun updateAxes(lsAxes: ClockAxisStyle) {
lsFontVariation = lsAxes.toFVar()
- aodFontVariation = lsAxes.replace(fixedAodAxes).toFVar()
+ aodFontVariation = lsAxes.copyWith(fixedAodAxes).toFVar()
fidgetFontVariation = buildFidgetVariation(lsAxes).toFVar()
logger.updateAxes(lsFontVariation, aodFontVariation)
@@ -220,19 +219,16 @@
invalidate()
}
- fun buildFidgetVariation(axes: List<ClockFontAxisSetting>): List<ClockFontAxisSetting> {
- val result = mutableListOf<ClockFontAxisSetting>()
- for (axis in axes) {
- result.add(
- FIDGET_DISTS.get(axis.key)?.let { (dist, midpoint) ->
- ClockFontAxisSetting(
- axis.key,
- axis.value + dist * if (axis.value > midpoint) -1 else 1,
- )
- } ?: axis
- )
- }
- return result
+ fun buildFidgetVariation(axes: ClockAxisStyle): ClockAxisStyle {
+ return ClockAxisStyle(
+ axes.items
+ .map { (key, value) ->
+ FIDGET_DISTS.get(key)?.let { (dist, midpoint) ->
+ key to value + dist * if (value > midpoint) -1 else 1
+ } ?: (key to value)
+ }
+ .toMap()
+ )
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -666,18 +662,22 @@
)
val AOD_COLOR = Color.WHITE
- val LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(400f)
- val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(200f)
- val WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(85f)
- val ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(0f)
- val SLANT_AXIS = GSFAxes.SLANT.toClockAxisSetting(0f)
+ private val LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 400f
+ private val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT to 200f
+ private val WIDTH_AXIS = GSFAxes.WIDTH to 85f
+ private val ROUND_AXIS = GSFAxes.ROUND to 0f
+ private val SLANT_AXIS = GSFAxes.SLANT to 0f
// Axes for Legacy version of the Flex Clock
- val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(600f)
- val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(74f)
- val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(133f)
- val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(100f)
- val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(43f)
- val FLEX_ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(100f)
+ private val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 600f
+ private val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT to 74f
+ private val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT to 133f
+ private val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH to 100f
+ private val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH to 43f
+ private val FLEX_ROUND_AXIS = GSFAxes.ROUND to 100f
+
+ private fun fromAxes(vararg axes: Pair<AxisDefinition, Float>): ClockAxisStyle {
+ return ClockAxisStyle(axes.map { (def, value) -> def.tag to value }.toMap())
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 14d34d7..162218d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -87,7 +87,7 @@
contentResolver,
selectedUserInteractor,
lazyKeyguardUpdateMonitor,
- dumpManager
+ dumpManager,
)
}
@@ -116,9 +116,9 @@
)
)
assertFalse(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
- )
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
)
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -212,7 +212,7 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"",
- currentUser
+ currentUser,
)
updateSetting(
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -285,7 +285,7 @@
ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
"${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
"|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
@@ -328,7 +328,7 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"${ActiveUnlockConfig.BiometricType.NONE.intValue}",
- currentUser
+ currentUser,
)
updateSetting(
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -358,7 +358,7 @@
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
"|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
- currentUser
+ currentUser,
)
updateSetting(
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -397,10 +397,10 @@
@Test
fun isWakeupConsideredUnlockIntent_singleValue() {
// GIVEN lift is considered an unlock intent
- secureSettings.putIntForUser(
+ secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- PowerManager.WAKE_REASON_LIFT,
- currentUser
+ PowerManager.WAKE_REASON_LIFT.toString(),
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
@@ -422,7 +422,7 @@
PowerManager.WAKE_REASON_LIFT.toString() +
"|" +
PowerManager.WAKE_REASON_TAP.toString(),
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
@@ -452,7 +452,7 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
" ",
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
@@ -479,7 +479,7 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
PowerManager.WAKE_REASON_LIFT.toString(),
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
@@ -501,7 +501,7 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
" ",
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
@@ -521,7 +521,7 @@
PowerManager.WAKE_REASON_LIFT.toString() +
"|" +
PowerManager.WAKE_REASON_TAP.toString(),
- currentUser
+ currentUser,
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
@@ -544,7 +544,7 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"-1",
- currentUser
+ currentUser,
)
// WHEN the setting updates
@@ -581,7 +581,7 @@
eq(uri),
eq(false),
capture(settingsObserverCaptor),
- eq(UserHandle.USER_ALL)
+ eq(UserHandle.USER_ALL),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 245388c..b0db8b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -22,12 +22,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.testKosmos
import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
@@ -47,16 +47,14 @@
@RunWith(AndroidJUnit4::class)
class KeyguardUnfoldTransitionTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val progressProvider: FakeUnfoldTransitionProvider =
kosmos.fakeUnfoldTransitionProgressProvider
- @Mock
- private lateinit var keyguardRootView: KeyguardRootView
+ @Mock private lateinit var keyguardRootView: KeyguardRootView
- @Mock
- private lateinit var notificationShadeWindowView: NotificationShadeWindowView
+ @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
@Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -71,10 +69,14 @@
xTranslationMax =
context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
- underTest = KeyguardUnfoldTransition(
- context, keyguardRootView, notificationShadeWindowView,
- statusBarStateController, progressProvider
- )
+ underTest =
+ KeyguardUnfoldTransition(
+ context,
+ keyguardRootView,
+ notificationShadeWindowView,
+ statusBarStateController,
+ progressProvider,
+ )
underTest.setup()
underTest.statusViewCentered = false
@@ -88,9 +90,8 @@
underTest.statusViewCentered = true
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
- view
- )
+ whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+ .thenReturn(view)
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
@@ -110,9 +111,8 @@
whenever(statusBarStateController.getState()).thenReturn(SHADE)
val view = View(context)
- whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
- view
- )
+ whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+ .thenReturn(view)
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
@@ -133,9 +133,11 @@
val view = View(context)
whenever(
- notificationShadeWindowView
- .findViewById<View>(customR.id.lockscreen_clock_view_large)
- ).thenReturn(view)
+ notificationShadeWindowView.findViewById<View>(
+ customR.id.lockscreen_clock_view_large
+ )
+ )
+ .thenReturn(view)
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
index cde42bd..7660623 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
@@ -23,7 +23,6 @@
import com.android.internal.accessibility.AccessibilityShortcutController
import com.android.internal.accessibility.common.ShortcutConstants
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -31,6 +30,7 @@
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -58,7 +58,7 @@
private lateinit var extraDimDialogDelegate: ExtraDimDialogDelegate
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
@Mock private lateinit var dialog: SystemUIDialog
@@ -79,7 +79,7 @@
kosmos.testDispatcher,
dialogFactory,
accessibilityManager,
- userTracker
+ userTracker,
)
}
@@ -94,7 +94,7 @@
verify(dialog)
.setPositiveButton(
eq(R.string.accessibility_deprecate_extra_dim_dialog_button),
- clickListener.capture()
+ clickListener.capture(),
)
clickListener.firstValue.onClick(dialog, 0)
@@ -110,7 +110,7 @@
.flattenToString()
)
),
- anyInt()
+ anyInt(),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 7e4704a..d118ace 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -141,6 +141,10 @@
@Mock
private QSSettingsPackageRepository mQSSettingsPackageRepository;
@Mock
+ private HearingDevicesInputRoutingController.Factory mInputRoutingFactory;
+ @Mock
+ private HearingDevicesInputRoutingController mInputRoutingController;
+ @Mock
private CachedBluetoothDevice mCachedDevice;
@Mock
private BluetoothDevice mDevice;
@@ -184,6 +188,7 @@
when(mCachedDevice.getBondState()).thenReturn(BOND_BONDED);
when(mCachedDevice.getDeviceSide()).thenReturn(SIDE_LEFT);
when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
+ when(mInputRoutingFactory.create(any())).thenReturn(mInputRoutingController);
mContext.setMockPackageManager(mPackageManager);
}
@@ -349,6 +354,7 @@
setUpDeviceDialogWithoutPairNewDeviceButton();
mDialog.show();
+ mExecutor.runAllReady();
ViewGroup ambientLayout = getAmbientLayout(mDialog);
assertThat(ambientLayout.getVisibility()).isEqualTo(View.VISIBLE);
@@ -401,7 +407,8 @@
mExecutor,
mAudioManager,
mUiEventLogger,
- mQSSettingsPackageRepository
+ mQSSettingsPackageRepository,
+ mInputRoutingFactory
);
mDialog = mDialogDelegate.createDialog();
}
@@ -438,7 +445,6 @@
return dialog.requireViewById(R.id.ambient_layout);
}
-
private int countChildWithoutSpace(ViewGroup viewGroup) {
int spaceCount = 0;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
new file mode 100644
index 0000000..0a41b77
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid
+
+import android.media.AudioDeviceInfo
+import android.media.AudioManager
+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.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesInputRoutingController.InputRoutingControlAvailableCallback
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesInputRoutingControllerTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private var hapClientProfile: HapClientProfile = mock()
+ private var cachedDevice: CachedBluetoothDevice = mock()
+ private var memberCachedDevice: CachedBluetoothDevice = mock()
+ private var btDevice: android.bluetooth.BluetoothDevice = mock()
+ private var audioManager: AudioManager = mock()
+ private lateinit var underTest: HearingDevicesInputRoutingController
+ private val testDispatcher = kosmos.testDispatcher
+
+ @Before
+ fun setUp() {
+ hapClientProfile.stub { on { isProfileReady } doReturn true }
+ cachedDevice.stub {
+ on { device } doReturn btDevice
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+ memberCachedDevice.stub {
+ on { device } doReturn btDevice
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+
+ underTest = HearingDevicesInputRoutingController(mContext, audioManager, testDispatcher)
+ underTest.setDevice(cachedDevice)
+ }
+
+ @Test
+ fun isInputRoutingControlAvailable_validInput_supportHapProfile_returnTrue() {
+ testScope.runTest {
+ val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isTrue()
+ }
+ }
+
+ @Test
+ fun isInputRoutingControlAvailable_notSupportHapProfile_returnFalse() {
+ testScope.runTest {
+ val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn emptyList()
+ }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isFalse()
+ }
+ }
+
+ @Test
+ fun isInputRoutingControlAvailable_validInputMember_supportHapProfile_returnTrue() {
+ testScope.runTest {
+ val mockInfoAddress2 = arrayOf(mockTestAddressInfo(TEST_ADDRESS_2))
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn listOf(hapClientProfile)
+ on { memberDevice } doReturn (setOf(memberCachedDevice))
+ }
+ memberCachedDevice.stub { on { address } doReturn TEST_ADDRESS_2 }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress2
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isTrue()
+ }
+ }
+
+ @Test
+ fun isAvailable_notValidInputDevice_returnFalse() {
+ testScope.runTest {
+ cachedDevice.stub {
+ on { address } doReturn TEST_ADDRESS
+ on { profiles } doReturn listOf(hapClientProfile)
+ }
+ audioManager.stub {
+ on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn emptyArray()
+ }
+
+ var result: Boolean? = null
+ underTest.isInputRoutingControlAvailable(
+ object : InputRoutingControlAvailableCallback {
+ override fun onResult(available: Boolean) {
+ result = available
+ }
+ }
+ )
+
+ runCurrent()
+ assertThat(result).isFalse()
+ }
+ }
+
+ @Test
+ fun selectInputRouting_builtinMic_setMicrophonePreferredForCallsFalse() {
+ underTest.selectInputRouting(
+ HearingDevicesInputRoutingController.InputRoutingValue.BUILTIN_MIC.ordinal
+ )
+
+ verify(btDevice).isMicrophonePreferredForCalls = false
+ }
+
+ private fun mockTestAddressInfo(address: String): AudioDeviceInfo {
+ val info: AudioDeviceInfo = mock()
+ info.stub {
+ on { type } doReturn AudioDeviceInfo.TYPE_BLE_HEADSET
+ on { this.address } doReturn address
+ }
+
+ return info
+ }
+
+ companion object {
+ private const val TEST_ADDRESS = "55:66:77:88:99:AA"
+ private const val TEST_ADDRESS_2 = "55:66:77:88:99:BB"
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8c5fad3..8573312 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -32,7 +32,6 @@
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -50,6 +49,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -74,7 +74,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class BackActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val executor = FakeExecutor(FakeSystemClock())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
index 648d74d..29a0b69 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
@@ -22,8 +22,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class CameraAutoRotateRepositoryImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val settings = kosmos.fakeSettings
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
index b73a212..2e357d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
@@ -22,8 +22,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testUser = UserHandle.of(1)
private val privacyManager = mock<SensorPrivacyManager>()
@@ -46,7 +46,7 @@
CameraSensorPrivacyRepositoryImpl(
testScope.testScheduler,
testScope.backgroundScope,
- privacyManager
+ privacyManager,
)
@Test
@@ -87,7 +87,7 @@
.addSensorPrivacyListener(
ArgumentMatchers.eq(SensorPrivacyManager.Sensors.CAMERA),
ArgumentMatchers.eq(testUser.identifier),
- captor.capture()
+ captor.capture(),
)
val sensorPrivacyCallback = captor.value!!
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
index 6c8097e..b3d8983 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
@@ -21,7 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class FakeCameraAutoRotateRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val underTest = kosmos.fakeCameraAutoRotateRepository
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
index 7161c2c..6b9a7de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
@@ -21,7 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class FakeCameraSensorPrivacyRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val underTest = kosmos.fakeCameraSensorPrivacyRepository
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
index 239e026..652a2ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
@@ -25,6 +25,10 @@
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -33,7 +37,11 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ActionIntentCreatorTest : SysuiTestCase() {
- val creator = ActionIntentCreator()
+ private val scheduler = TestCoroutineScheduler()
+ private val mainDispatcher = UnconfinedTestDispatcher(scheduler)
+ private val testScope = TestScope(mainDispatcher)
+
+ val creator = ActionIntentCreator(testScope.backgroundScope)
@Test
fun test_getTextEditorIntent() {
@@ -65,7 +73,7 @@
}
@Test
- fun test_getImageEditIntent() {
+ fun test_getImageEditIntent() = runTest {
context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
val fakeUri = Uri.parse("content://foo")
var intent = creator.getImageEditIntent(fakeUri, context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
index 126b3fa..10c5c52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
@@ -34,6 +34,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.atomic.AtomicReference;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DefaultIntentCreatorTest extends SysuiTestCase {
@@ -73,12 +75,16 @@
}
@Test
- public void test_getImageEditIntent() {
+ public void test_getImageEditIntentAsync() {
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
"");
Uri fakeUri = Uri.parse("content://foo");
- Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
+ final AtomicReference<Intent> intentHolder = new AtomicReference<>(null);
+ mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+ intentHolder.set(output);
+ });
+ Intent intent = intentHolder.get();
assertEquals(Intent.ACTION_EDIT, intent.getAction());
assertEquals("image/*", intent.getType());
assertEquals(null, intent.getComponent());
@@ -90,8 +96,10 @@
"com.android.remotecopy.RemoteCopyActivity");
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
fakeComponent.flattenToString());
- intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
- assertEquals(fakeComponent, intent.getComponent());
+ mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+ intentHolder.set(output);
+ });
+ assertEquals(fakeComponent, intentHolder.get().getComponent());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index df10d05..b08e676 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,12 +16,14 @@
package com.android.systemui.communal.widgets
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalEnabled
@@ -49,6 +51,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -65,6 +68,7 @@
private lateinit var appWidgetIdToRemove: MutableSharedFlow<Int>
+ private lateinit var communalInteractorSpy: CommunalInteractor
private lateinit var underTest: CommunalAppWidgetHostStartable
@Before
@@ -78,12 +82,13 @@
helper = kosmos.fakeGlanceableHubMultiUserHelper
appWidgetIdToRemove = MutableSharedFlow()
whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
+ communalInteractorSpy = spy(kosmos.communalInteractor)
underTest =
CommunalAppWidgetHostStartable(
{ appWidgetHost },
{ communalWidgetHost },
- { kosmos.communalInteractor },
+ { communalInteractorSpy },
{ kosmos.communalSettingsInteractor },
{ kosmos.keyguardInteractor },
{ kosmos.fakeUserTracker },
@@ -259,6 +264,41 @@
}
@Test
+ fun removeNotLockscreenWidgets_whenCommunalIsAvailable() =
+ with(kosmos) {
+ testScope.runTest {
+ // Communal is available
+ setCommunalAvailable(true)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ fakeCommunalWidgetRepository.addWidget(
+ appWidgetId = 1,
+ userId = MAIN_USER_INFO.id,
+ category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+ )
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(
+ appWidgetId = 3,
+ userId = MAIN_USER_INFO.id,
+ category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+ )
+
+ underTest.start()
+ runCurrent()
+
+ val communalWidgets by
+ collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
+ assertThat(communalWidgets).hasSize(1)
+ assertThat(communalWidgets!![0].appWidgetId).isEqualTo(2)
+
+ verify(communalInteractorSpy).deleteWidget(1)
+ verify(communalInteractorSpy).deleteWidget(3)
+ }
+ }
+
+ @Test
fun onStartHeadlessSystemUser_registerWidgetManager_whenCommunalIsAvailable() =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 197b0ee..8591375 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -26,6 +26,7 @@
import android.view.IWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
@@ -71,13 +72,19 @@
// that the initial state (soon after construction) contains the expected ones set in every
// test.
private val displayRepository: DisplayRepositoryImpl by lazy {
- DisplayRepositoryImpl(
+ // TODO b/401305290 - move this to kosmos
+ val displayRepositoryFromLib =
+ com.android.app.displaylib.DisplayRepositoryImpl(
displayManager,
+ testHandler,
+ testScope.backgroundScope,
+ UnconfinedTestDispatcher(),
+ )
+ DisplayRepositoryImpl(
commandQueue,
windowManager,
- testHandler,
- TestScope(UnconfinedTestDispatcher()),
- UnconfinedTestDispatcher(),
+ testScope.backgroundScope,
+ displayRepositoryFromLib,
)
.also {
verify(displayManager, never()).registerDisplayListener(any(), any())
@@ -403,7 +410,7 @@
val pendingDisplay by lastPendingDisplay()
sendOnDisplayConnected(1, TYPE_EXTERNAL)
- val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ val initialPendingDisplay: PendingDisplay? = pendingDisplay
assertThat(pendingDisplay).isNotNull()
sendOnDisplayChanged(1)
@@ -416,7 +423,7 @@
val pendingDisplay by lastPendingDisplay()
sendOnDisplayConnected(1, TYPE_EXTERNAL)
- val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ val initialPendingDisplay: PendingDisplay? = pendingDisplay
assertThat(pendingDisplay).isNotNull()
sendOnDisplayConnected(2, TYPE_EXTERNAL)
@@ -648,7 +655,7 @@
return flowValue
}
- private fun TestScope.lastPendingDisplay(): FlowValue<DisplayRepository.PendingDisplay?> {
+ private fun TestScope.lastPendingDisplay(): FlowValue<PendingDisplay?> {
val flowValue = collectLastValue(displayRepository.pendingDisplay)
captureAddedRemovedListener()
verify(displayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
index e41d46c..28b9e73 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
@@ -19,6 +19,7 @@
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.testScope
@@ -31,7 +32,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.kotlin.eq
+import org.mockito.kotlin.any
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
@@ -105,7 +106,7 @@
@Test
fun start_registersDumpable() {
- verify(kosmos.dumpManager).registerNormalDumpable(anyString(), eq(underTest))
+ verify(kosmos.dumpManager).registerNormalDumpable(anyString(), any())
}
private fun createDisplay(displayId: Int): Display =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index f2a6c11..7229761 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -26,9 +26,9 @@
import com.android.systemui.education.data.model.EduDeviceConnectionTime
import com.android.systemui.education.data.model.GestureEduModel
import com.android.systemui.education.domain.interactor.mockEduInputManager
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.io.File
import javax.inject.Provider
@@ -48,7 +48,7 @@
class ContextualEducationRepositoryTest : SysuiTestCase() {
private lateinit var underTest: UserContextualEducationRepository
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val dsScopeProvider: Provider<CoroutineScope> = Provider {
TestScope(kosmos.testDispatcher).backgroundScope
@@ -70,7 +70,7 @@
testContext,
dsScopeProvider,
kosmos.mockEduInputManager,
- kosmos.testDispatcher
+ kosmos.testDispatcher,
)
underTest.setUser(testUserId)
}
@@ -109,7 +109,7 @@
lastEducationTime = kosmos.fakeEduClock.instant(),
usageSessionStartTime = kosmos.fakeEduClock.instant(),
userId = testUserId,
- gestureType = BACK
+ gestureType = BACK,
)
underTest.updateGestureEduModel(BACK) { newModel }
val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
index 5030d1e..0f75e57c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
@@ -21,9 +21,9 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.haptics.msdl.msdlPlayer
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -45,7 +45,7 @@
@RunWith(AndroidJUnit4::class)
class HapticSliderPluginTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
@Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var vibratorHelper: VibratorHelper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
index 798c5ab..39b5e81 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
@@ -23,9 +23,9 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.inputdevice.data.model.UserDeviceConnectionStatus
import com.android.systemui.keyboard.data.repository.keyboardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.touchpad.data.repository.touchpadRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.user.data.repository.userRepository
@@ -41,7 +41,7 @@
class UserInputDeviceRepositoryTest : SysuiTestCase() {
private lateinit var underTest: UserInputDeviceRepository
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyboardRepository = kosmos.keyboardRepository
private val touchpadRepository = kosmos.touchpadRepository
@@ -54,7 +54,7 @@
kosmos.testDispatcher,
keyboardRepository,
touchpadRepository,
- kosmos.userRepository
+ kosmos.userRepository,
)
userRepository.setUserInfos(USER_INFOS)
}
@@ -72,7 +72,7 @@
assertThat(isAnyKeyboardConnected)
.containsExactly(
UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
- UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+ UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
)
.inOrder()
}
@@ -90,16 +90,13 @@
assertThat(isAnyTouchpadConnected)
.containsExactly(
UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
- UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+ UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
)
.inOrder()
}
companion object {
private val USER_INFOS =
- listOf(
- UserInfo(100, "First User", 0),
- UserInfo(101, "Second User", 0),
- )
+ listOf(UserInfo(100, "First User", 0), UserInfo(101, "Second User", 0))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
index 274880b..6bd0fb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
@@ -23,9 +23,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@
@Mock private lateinit var inputMethodManager: InputMethodManager
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private lateinit var underTest: InputMethodRepository
@@ -72,7 +72,7 @@
inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
any(),
anyBoolean(),
- eq(USER_HANDLE)
+ eq(USER_HANDLE),
)
)
.thenReturn(listOf())
@@ -97,7 +97,7 @@
inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
eq(selectedImiId),
anyBoolean(),
- eq(USER_HANDLE)
+ eq(USER_HANDLE),
)
)
.thenReturn(
@@ -125,7 +125,7 @@
verify(inputMethodManager)
.showInputMethodPickerFromSystem(
/* showAuxiliarySubtypes = */ eq(true),
- /* displayId = */ eq(displayId)
+ /* displayId = */ eq(displayId),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
index 8e6de2f..9e129a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
@@ -22,8 +22,8 @@
import com.android.systemui.inputmethod.data.model.InputMethodModel
import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository
import com.android.systemui.inputmethod.data.repository.inputMethodRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlinx.coroutines.test.runTest
@@ -34,7 +34,7 @@
@RunWith(AndroidJUnit4::class)
class InputMethodInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val fakeInputMethodRepository = kosmos.fakeInputMethodRepository
@@ -148,7 +148,7 @@
subtypes =
List(auxiliarySubtypes + nonAuxiliarySubtypes) {
InputMethodModel.Subtype(subtypeId = it, isAuxiliary = it < auxiliarySubtypes)
- }
+ },
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index 1bb4805..655c646 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -34,7 +34,6 @@
import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.keyboard.shortcut.shortcutHelperViewModel
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
@@ -43,6 +42,7 @@
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.systemUIDialogFactory
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -60,7 +60,7 @@
private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
private val mockUserContext: Context = mock()
private val kosmos =
- Kosmos().also {
+ testKosmos().also {
it.testCase = this
it.testDispatcher = UnconfinedTestDispatcher()
it.shortcutHelperSystemShortcutsSource = fakeSystemSource
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index be9e93c..ba57ffd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -26,7 +26,7 @@
import com.android.systemui.keyboard.stickykeys.shared.model.Locked
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -52,19 +52,19 @@
fun setup() {
val dialogFactory = mock<StickyKeyDialogFactory>()
whenever(dialogFactory.create(any())).thenReturn(dialog)
- val keyboardRepository = Kosmos().keyboardRepository
+ val keyboardRepository = testKosmos().keyboardRepository
val viewModel =
StickyKeysIndicatorViewModel(
stickyKeysRepository,
keyboardRepository,
- testScope.backgroundScope
+ testScope.backgroundScope,
)
coordinator =
StickyKeysIndicatorCoordinator(
testScope.backgroundScope,
dialogFactory,
viewModel,
- mock<StickyKeysLogger>()
+ mock<StickyKeysLogger>(),
)
coordinator.startListening()
keyboardRepository.setIsAnyKeyboardConnected(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 9daf0ff..1c00419 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -33,7 +33,6 @@
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
@@ -64,7 +63,7 @@
private val inputManager = mock<InputManager>()
private val keyboardRepository = FakeKeyboardRepository()
private val secureSettings = kosmos.fakeSettings
- private val userRepository = Kosmos().fakeUserRepository
+ private val userRepository = testKosmos().fakeUserRepository
private val captor =
ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index baf3b5b..3f1cadc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -23,12 +23,12 @@
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argThat
import java.util.function.Predicate
@@ -68,7 +68,7 @@
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var powerManager: PowerManager
@Mock private lateinit var wallpaperManager: WallpaperManager
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val deviceStateManager = kosmos.deviceStateManager
@Mock
@@ -92,7 +92,7 @@
surfaceControl1,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private var surfaceControl2 = mock(SurfaceControl::class.java)
@@ -113,7 +113,7 @@
surfaceControl2,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
@@ -135,7 +135,7 @@
surfaceControlWp,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
@@ -157,7 +157,7 @@
surfaceControlLockWp,
Rect(),
mock(ActivityManager.RunningTaskInfo::class.java),
- false
+ false,
)
private lateinit var lockWallpaperTargets: Array<RemoteAnimationTarget>
private var shouldPerformSmartspaceTransition = false
@@ -179,14 +179,14 @@
notificationShadeWindowController,
powerManager,
wallpaperManager,
- deviceStateManager
+ deviceStateManager,
) {
override fun shouldPerformSmartspaceTransition(): Boolean =
shouldPerformSmartspaceTransition
}
keyguardUnlockAnimationController.setLauncherUnlockController(
"",
- launcherUnlockAnimationController
+ launcherUnlockAnimationController,
)
whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
@@ -227,7 +227,7 @@
arrayOf(),
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
@@ -259,7 +259,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Since the animation is running, we should not have finished the remote animation.
@@ -282,7 +282,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any())
@@ -303,7 +303,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any())
@@ -327,7 +327,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- true /* requestedShowSurfaceBehindKeyguard */
+ true, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
@@ -351,7 +351,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- true /* requestedShowSurfaceBehindKeyguard */
+ true, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -373,7 +373,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -389,7 +389,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- true /* requestedShowSurfaceBehindKeyguard */
+ true, /* requestedShowSurfaceBehindKeyguard */
)
assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
@@ -406,7 +406,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -427,7 +427,7 @@
wallpaperTargets,
lockWallpaperTargets,
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
for (i in 0..10) {
@@ -471,7 +471,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -492,7 +492,7 @@
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(
surfaceTransactionApplier,
- times(1).description("WallpaperSurface was expected to receive scheduleApply once")
+ times(1).description("WallpaperSurface was expected to receive scheduleApply once"),
)
.scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
@@ -523,7 +523,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -539,7 +539,7 @@
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(
surfaceTransactionApplier,
- atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply")
+ atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply"),
)
.scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
@@ -562,7 +562,7 @@
wallpaperTargets,
arrayOf(),
0 /* startTime */,
- false /* requestedShowSurfaceBehindKeyguard */
+ false, /* requestedShowSurfaceBehindKeyguard */
)
// Stop the animator - we just want to test whether the override is not applied.
@@ -578,7 +578,7 @@
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(
surfaceTransactionApplier,
- atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply")
+ atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply"),
)
.scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
@@ -588,7 +588,7 @@
assertEquals(
"Wallpaper surface was expected to have opacity 1",
1f,
- captorWp.getLastValue().alpha
+ captorWp.getLastValue().alpha,
)
verifyNoMoreInteractions(surfaceTransactionApplier)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 97c746c..d0762a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -25,10 +25,13 @@
import android.view.RemoteAnimationTarget
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.android.window.flags.Flags
@@ -63,6 +66,9 @@
@Mock
private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor
@Mock private lateinit var keyguardTransitions: KeyguardTransitions
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock private lateinit var keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@Before
fun setUp() {
@@ -77,6 +83,9 @@
keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor,
keyguardTransitions = keyguardTransitions,
+ selectedUserInteractor = selectedUserInteractor,
+ lockPatternUtils = lockPatternUtils,
+ keyguardShowWhileAwakeInteractor = keyguardShowWhileAwakeInteractor,
)
}
@@ -236,6 +245,8 @@
.whenever(keyguardDismissTransitionInteractor)
.startDismissKeyguardTransition(any(), any())
+ whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(-1)
+
underTest.onKeyguardGoingAwayRemoteAnimationStart(
transit = 0,
apps = arrayOf(mock<RemoteAnimationTarget>()),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
index 6bc8000..04f7fe1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
@@ -21,10 +21,10 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.statusbar.policy.fakeCastController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class MediaRouterRepositoryTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
val testScope = kosmos.testScope
val castController = kosmos.fakeCastController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 858ed6a..473d7b6d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -22,8 +22,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
@@ -64,7 +64,7 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private lateinit var testableLooper: TestableLooper
@@ -85,7 +85,7 @@
Handler(testableLooper.looper),
TEST_SETTING,
USER,
- DEFAULT_VALUE
+ DEFAULT_VALUE,
) {
override fun handleValueChanged(value: Int, observedChange: Boolean) {
callback(value, observedChange)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index 0049042..a2829b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
@@ -36,6 +35,7 @@
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.WorkModeTile
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -47,7 +47,7 @@
@RunWith(AndroidJUnit4::class)
class WorkTileAutoAddableTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val restoreProcessor: RestoreProcessor
get() = kosmos.workTileRestoreProcessor
@@ -62,7 +62,7 @@
FakeUserTracker(
_userId = USER_INFO_0.id,
_userInfo = USER_INFO_0,
- _userProfiles = listOf(USER_INFO_0)
+ _userProfiles = listOf(USER_INFO_0),
)
underTest = WorkTileAutoAddable(userTracker, kosmos.workTileRestoreProcessor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
index 3a9c3d0..4e0adca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.MediumTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.FakeQSFactory
@@ -39,6 +38,7 @@
import com.android.systemui.qs.qsTileFactory
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -55,22 +55,12 @@
@RunWith(AndroidJUnit4::class)
class NoLowNumberOfTilesTest : SysuiTestCase() {
- private val USER_0_INFO =
- UserInfo(
- 0,
- "zero",
- "",
- UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
- )
+ private val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
- private val defaultTiles =
- listOf(
- TileSpec.create("internet"),
- TileSpec.create("bt"),
- )
+ private val defaultTiles = listOf(TileSpec.create("internet"), TileSpec.create("bt"))
private val kosmos =
- Kosmos().apply {
+ testKosmos().apply {
fakeMinimumTilesRepository = MinimumTilesFixedRepository(minNumberOfTiles = 2)
fakeUserTracker.set(listOf(USER_0_INFO), 0)
qsTileFactory = FakeQSFactory(::tileCreator)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
index 9d9bfda..77030ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.MediumTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.FakeQSFactory
@@ -34,6 +33,7 @@
import com.android.systemui.qs.qsTileFactory
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -52,7 +52,9 @@
@RunWith(AndroidJUnit4::class)
class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() {
- private val kosmos by lazy { Kosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) } }
+ private val kosmos by lazy {
+ testKosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) }
+ }
// Getter here so it can change when there is a managed profile.
private val workTileAvailable: Boolean
get() = hasManagedProfile()
@@ -143,30 +145,15 @@
}
private fun TestScope.createManagedProfileAndAdd() {
- kosmos.fakeUserTracker.set(
- listOf(USER_0_INFO, MANAGED_USER_INFO),
- 0,
- )
+ kosmos.fakeUserTracker.set(listOf(USER_0_INFO, MANAGED_USER_INFO), 0)
runCurrent()
}
private companion object {
val WORK_TILE_SPEC = "work".toTileSpec()
- val USER_0_INFO =
- UserInfo(
- 0,
- "zero",
- "",
- UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
- )
+ val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
val MANAGED_USER_INFO =
- UserInfo(
- 10,
- "ten-managed",
- "",
- 0,
- UserManager.USER_TYPE_PROFILE_MANAGED,
- )
+ UserInfo(10, "ten-managed", "", 0, UserManager.USER_TYPE_PROFILE_MANAGED)
fun String.toTileSpec() = TileSpec.create(this)
}
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
index 557f4ea..311f1f7 100644
--- 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
@@ -23,13 +23,13 @@
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 com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class AirplaneModeMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val airplaneModeConfig = kosmos.qsAirplaneModeTileConfig
private lateinit var mapper: AirplaneModeMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index 24e4668..bb58eca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -23,12 +23,12 @@
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.alarm.domain.model.AlarmTileModel
import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.time.FakeSystemClock
import java.time.Instant
import java.time.LocalDateTime
@@ -41,7 +41,7 @@
@RunWith(AndroidJUnit4::class)
class AlarmTileMapperTest : SysuiTestCase() {
private val oneMinute = 60000L
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val alarmTileConfig = kosmos.qsAlarmTileConfig
private val fakeClock = FakeSystemClock()
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
index 44c175a..1ef1a72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
@@ -23,10 +23,10 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.utils.leaks.FakeBatteryController
import com.google.common.truth.Truth
import kotlinx.coroutines.flow.flowOf
@@ -40,7 +40,7 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class BatterySaverTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val batteryController = FakeBatteryController(LeakCheck())
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
index 2ddaddd..cb50ec9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
@@ -22,12 +22,12 @@
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.battery.domain.model.BatterySaverTileModel
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,7 +35,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class BatterySaverTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val batterySaverTileConfig = kosmos.qsBatterySaverTileConfig
private lateinit var mapper: BatterySaverTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
index a3c159820..bcd443c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -22,19 +22,19 @@
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.colorcorrection.domain.model.ColorCorrectionTileModel
import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ColorCorrectionTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig
private val subtitleArray by lazy {
context.resources.getStringArray(R.array.tile_states_color_correction)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
index 83e61d1..3c0e7d51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -24,7 +24,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -34,6 +33,7 @@
import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runCurrent
@@ -45,7 +45,7 @@
@RunWith(AndroidJUnit4::class)
class CustomTileRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
+ private val kosmos = testKosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
private val underTest: CustomTileRepository =
with(kosmos) {
CustomTileRepositoryImpl(
@@ -213,7 +213,7 @@
underTest.updateWithTile(
TEST_USER_1,
Tile().apply { subtitle = "test_subtitle" },
- true
+ true,
)
runCurrent()
@@ -247,7 +247,7 @@
underTest.updateWithTile(
TEST_USER_1,
Tile().apply { subtitle = "test_subtitle" },
- true
+ true,
)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index a115c12..2da144e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -21,11 +21,11 @@
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.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Test
@@ -34,7 +34,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class FlashlightMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsFlashlightTileConfig
private val mapper by lazy {
FlashlightMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
index 8f8f710..45720b86 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -22,19 +22,19 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import com.android.systemui.qs.tiles.impl.fontscaling.qsFontScalingTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class FontScalingTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val fontScalingTileConfig = kosmos.qsFontScalingTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
index 9bd4895..93f2bc3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -26,7 +26,6 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.LaunchableView
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.shared.QSSettingsPackageRepository
@@ -37,6 +36,7 @@
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -67,7 +67,7 @@
@Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val scope = kosmos.testScope
private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
private val keyguardStateController = FakeKeyguardStateController()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
index 3d3447d..c410342 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -21,19 +21,19 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
private val mapper by lazy {
HearingDevicesTileMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
index 4b9d11d..4d38e75 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -24,11 +24,11 @@
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -46,7 +46,7 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
index 1b497a2..0ba057b1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -22,13 +22,13 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.shared.QSSettingsPackageRepository
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -47,7 +47,7 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val inputHandler = FakeQSTileIntentUserInputHandler()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index 54a653d..e15664e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.Text.Companion.loadText
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
@@ -38,13 +37,14 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class InternetTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val internetTileConfig = kosmos.qsInternetTileConfig
private val handler = kosmos.fakeExecutorHandler
private val mapper by lazy {
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 63607f1..45582f0 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
@@ -29,7 +29,6 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
@@ -54,6 +53,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -67,7 +67,7 @@
@RunWith(AndroidJUnit4::class)
class InternetTileDataInteractorTest : SysuiTestCase() {
private val testUser = UserHandle.of(1)
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private lateinit var underTest: InternetTileDataInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index 261e3de..d019a45 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
@@ -29,6 +28,7 @@
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -44,7 +44,7 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class InternetTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val inputHandler = FakeQSTileIntentUserInputHandler()
private lateinit var underTest: InternetTileUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
index 780d58c..48e1146 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
@@ -22,20 +22,20 @@
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.tileimpl.SubtitleArrayMapping
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
import com.android.systemui.qs.tiles.impl.inversion.qsColorInversionTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class ColorInversionTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig
private val subtitleArrayId =
SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
index 7562ac0..57f85bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
@@ -22,13 +22,13 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.settings.fakeUserFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.flow.flowOf
@@ -42,7 +42,7 @@
@RunWith(AndroidJUnit4::class)
class IssueRecordingDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val userTracker = kosmos.userTracker
private val userFileManager = kosmos.fakeUserFileManager
private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
index fa6d8bf..0201168 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
@@ -20,7 +20,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsEventLogger
@@ -30,6 +29,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.recordissue.RecordIssueModule
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class IssueRecordingMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val uiConfig =
QSTileUIConfig.Resource(R.drawable.qs_record_issue_icon_off, R.string.qs_record_issue_label)
private val config =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 9c2be89..125419b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.dialogTransitionAnimator
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -39,6 +38,7 @@
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.test.runTest
@@ -56,7 +56,7 @@
@Mock private lateinit var recordingController: RecordingController
val user = UserHandle(1)
- val kosmos = Kosmos().also { it.testCase = this }
+ val kosmos = testKosmos().also { it.testCase = this }
private lateinit var userContextProvider: UserContextProvider
private lateinit var underTest: IssueRecordingUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 4ebe23dc..a6ad549 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -21,11 +21,11 @@
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.location.domain.model.LocationTileModel
import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import junit.framework.Assert
import org.junit.Test
@@ -34,7 +34,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class LocationTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsLocationTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
index 8b21cb4..435aea0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
@@ -32,6 +31,7 @@
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.test.runTest
@@ -58,7 +58,7 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
underTest =
LocationTileUserActionInteractor(
EmptyCoroutineContext,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
index a0aa2d4..a5e2922 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
@@ -26,7 +26,7 @@
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testUser = UserHandle.of(1)!!
private val testStartTime = LocalTime.MIDNIGHT
private val testEndTime = LocalTime.NOON
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
index adc8bcb..51975b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
@@ -26,12 +26,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.intentInputs
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
+import com.android.systemui.testKosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -51,7 +51,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
private val testUser = UserHandle.of(1)
private val colorDisplayManager =
@@ -89,7 +89,7 @@
NightDisplayTileUserActionInteractor(
nightDisplayRepository,
qsTileIntentUserActionHandler,
- kosmos.qsTileLogger
+ kosmos.qsTileLogger,
)
@Test
@@ -143,7 +143,7 @@
underTest.handleInput(
QSTileInputTestKtx.longClick(
NightDisplayTileModel.AutoModeOff(enabledState, false),
- testUser
+ testUser,
)
)
@@ -163,7 +163,7 @@
underTest.handleInput(
QSTileInputTestKtx.longClick(
NightDisplayTileModel.AutoModeOff(enabledState, false),
- testUser
+ testUser,
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
index 7c85326..0b0b88e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
@@ -24,13 +24,13 @@
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.base.logging.QSTileLogger
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
import com.android.systemui.qs.tiles.impl.night.qsNightDisplayTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import java.time.LocalTime
import java.time.format.DateTimeFormatter
@@ -41,7 +41,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsNightDisplayTileConfig
private val testStartTime = LocalTime.MIDNIGHT
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
index b6caa22..ecf6f2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
@@ -22,19 +22,19 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
import com.android.systemui.qs.tiles.impl.notes.qsNotesTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import kotlin.test.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotesTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsNotesTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
index 4786fc4..84ab690 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
@@ -24,9 +24,9 @@
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toCollection
@@ -38,12 +38,11 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotesTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testUser = UserHandle.of(1)
private lateinit var underTest: NotesTileDataInteractor
-
@EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
@Test
fun availability_qsFlagEnabled_notesRoleEnabled_returnTrue() =
@@ -92,7 +91,7 @@
fun tileData_notEmpty() = runTest {
underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
val flowValue by
- collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+ collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
index 54911e6..282af91 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
@@ -20,7 +20,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
@@ -29,18 +28,19 @@
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotesTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val panelInteractor = mock<PanelInteractor>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
index 59eb069..16b0caa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
@@ -22,9 +22,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.oneHandedModeRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileDataInteractor
+import com.android.systemui.testKosmos
import com.android.wm.shell.onehanded.OneHanded
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
@@ -37,7 +37,7 @@
@RunWith(AndroidJUnit4::class)
class OneHandedModeTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testUser = UserHandle.of(1)!!
private val oneHandedModeRepository = kosmos.oneHandedModeRepository
private val underTest: OneHandedModeTileDataInteractor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
index 5b39810..3fba857 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
@@ -22,13 +22,13 @@
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.tileimpl.SubtitleArrayMapping
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
import com.android.systemui.qs.tiles.impl.onehanded.qsOneHandedModeTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,7 +36,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class OneHandedModeTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsOneHandedModeTileConfig
private val subtitleArrayId = SubtitleArrayMapping.getSubtitleId(config.tileSpec.spec)
private val subtitleArray by lazy { context.resources.getStringArray(subtitleArrayId) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
index 312f180..ef21df6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
@@ -21,12 +21,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -36,7 +36,7 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
private val inputHandler = kosmos.qsTileIntentUserInputHandler
private val underTest = kosmos.qrCodeScannerTileUserActionInteractor
private val intent = mock<Intent>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
index c572ff6..9e20aae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -23,11 +23,11 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
@@ -36,7 +36,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class QRCodeScannerTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsQRCodeScannerTileConfig
private lateinit var mapper: QRCodeScannerTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
index dc3248d..fecb3de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
@@ -23,10 +23,10 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.reduceBrightColorsController
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -40,14 +40,14 @@
class ReduceBrightColorsTileDataInteractorTest : SysuiTestCase() {
private val isAvailable = true
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val reduceBrightColorsController = kosmos.reduceBrightColorsController
private val underTest: ReduceBrightColorsTileDataInteractor =
ReduceBrightColorsTileDataInteractor(
testScope.testScheduler,
isAvailable,
- reduceBrightColorsController
+ reduceBrightColorsController,
)
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
index 75b090c..7732138 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
@@ -28,11 +28,11 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
import com.android.systemui.accessibility.reduceBrightColorsController
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -48,7 +48,7 @@
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val controller = kosmos.reduceBrightColorsController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
index 00017f9..ebf70da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -23,12 +23,12 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.qs.tiles.impl.reducebrightness.qsReduceBrightColorsTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,7 +36,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsReduceBrightColorsTileConfig
private lateinit var mapper: ReduceBrightColorsTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
index 283fa60..6f05c31 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
@@ -28,9 +28,9 @@
import com.android.systemui.camera.data.repository.fakeCameraAutoRotateRepository
import com.android.systemui.camera.data.repository.fakeCameraSensorPrivacyRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.utils.leaks.FakeBatteryController
@@ -48,7 +48,7 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class RotationLockTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val batteryController = FakeBatteryController(LeakCheck())
private val rotationController = FakeRotationLockController(LeakCheck())
@@ -65,7 +65,7 @@
whenever(
packageManager.checkPermission(
eq(Manifest.permission.CAMERA),
- eq(TEST_PACKAGE_NAME)
+ eq(TEST_PACKAGE_NAME),
)
)
.thenReturn(PackageManager.PERMISSION_GRANTED)
@@ -81,7 +81,7 @@
.apply {
addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
}
- .resources
+ .resources,
)
}
@@ -182,7 +182,7 @@
whenever(
packageManager.checkPermission(
eq(Manifest.permission.CAMERA),
- eq(TEST_PACKAGE_NAME)
+ eq(TEST_PACKAGE_NAME),
)
)
.thenReturn(PackageManager.PERMISSION_DENIED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 7401014..0e6aaa6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -25,7 +25,6 @@
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
import com.android.systemui.foldedDeviceStateList
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig
@@ -33,6 +32,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.devicePostureController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -42,7 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class RotationLockTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig
private val devicePostureController = kosmos.devicePostureController
private val deviceStateManager = kosmos.deviceStateManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index 1fb5048..0799216 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -22,19 +22,19 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.qs.tiles.impl.saver.qsDataSaverTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class DataSaverTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
index 41174e7..127160d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
@@ -22,11 +22,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class ScreenRecordTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val screenRecordRepo = kosmos.screenRecordRepository
private val underTest: ScreenRecordTileDataInteractor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index 778c73f..f47e057 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -26,7 +26,6 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
@@ -36,6 +35,7 @@
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.testKosmos
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,7 +49,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyguardInteractor = kosmos.keyguardInteractor
private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
index 3632556..d118c09 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -23,13 +23,13 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
import com.android.systemui.qs.tiles.impl.screenrecord.qsScreenRecordTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenRecordTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val config = kosmos.qsScreenRecordTileConfig
private lateinit var mapper: ScreenRecordTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
index 6c7bb1b..4a28fc0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
@@ -23,11 +23,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.sensorprivacy.SensorPrivacyToggleTileDataInteractor
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -44,7 +44,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class SensorPrivacyToggleTileDataInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val mockSensorPrivacyController =
mock<IndividualSensorPrivacyController> {
@@ -55,7 +55,7 @@
SensorPrivacyToggleTileDataInteractor(
testScope.testScheduler,
mockSensorPrivacyController,
- CAMERA
+ CAMERA,
)
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
index 562e6eb..7856fca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
@@ -26,7 +26,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.activityStarter
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
@@ -34,6 +33,7 @@
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.SensorPrivacyToggleTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -48,7 +48,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val keyguardInteractor = kosmos.keyguardInteractor
// The keyguard repository below is the same one kosmos used to create the interactor above
@@ -64,7 +64,7 @@
mockActivityStarter,
mockSensorPrivacyController,
fakeSafetyCenterManager,
- CAMERA
+ CAMERA,
)
@Test
@@ -79,7 +79,7 @@
.setSensorBlocked(
eq(SensorPrivacyManager.Sources.QS_TILE),
eq(CAMERA),
- eq(!originalIsBlocked)
+ eq(!originalIsBlocked),
)
}
@@ -95,7 +95,7 @@
.setSensorBlocked(
eq(SensorPrivacyManager.Sources.QS_TILE),
eq(CAMERA),
- eq(!originalIsBlocked)
+ eq(!originalIsBlocked),
)
}
@@ -114,7 +114,7 @@
.setSensorBlocked(
eq(SensorPrivacyManager.Sources.QS_TILE),
eq(CAMERA),
- eq(!originalIsBlocked)
+ eq(!originalIsBlocked),
)
verify(mockActivityStarter).postQSRunnableDismissingKeyguard(any())
}
@@ -150,7 +150,7 @@
mockActivityStarter,
mockSensorPrivacyController,
fakeSafetyCenterManager,
- MICROPHONE
+ MICROPHONE,
)
micUserActionInteractor.handleInput(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
index e4cd0e0..3b810dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
@@ -24,7 +24,6 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
import com.android.systemui.qs.tiles.impl.sensorprivacy.qsCameraSensorPrivacyToggleTileConfig
@@ -33,13 +32,14 @@
import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources.MicrophonePrivacyTileResources
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val cameraConfig = kosmos.qsCameraSensorPrivacyToggleTileConfig
private val micConfig = kosmos.qsMicrophoneSensorPrivacyToggleTileConfig
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index 8f5f2d3..547bbbc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -25,12 +25,12 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.uimodenight.UiModeNightTileModelHelper.createModel
import com.android.systemui.qs.tiles.impl.uimodenight.qsUiModeNightTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import kotlin.reflect.KClass
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,7 +38,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class UiModeNightTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsUiModeNightTileConfig
private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index 2c81f39..ed3fc50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
@@ -26,12 +26,12 @@
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.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
import com.android.systemui.qs.tiles.impl.work.qsWorkModeTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -43,7 +43,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class WorkModeTileMapperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val qsTileConfig = kosmos.qsWorkModeTileConfig
private val devicePolicyManager = kosmos.devicePolicyManager
private val testLabel = context.getString(R.string.quick_settings_work_mode_label)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index a8b005f..12ed3f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -40,6 +39,7 @@
import com.android.systemui.settings.brightness.MirrorController
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeModeInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -65,7 +65,7 @@
@RunWith(AndroidJUnit4::class)
class QSSceneAdapterImplTest : SysuiTestCase() {
- private val kosmos = Kosmos().apply { testCase = this@QSSceneAdapterImplTest }
+ private val kosmos = testKosmos().apply { testCase = this@QSSceneAdapterImplTest }
private val testDispatcher = kosmos.testDispatcher
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index 7bcaeab..390a5d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -28,12 +28,12 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.dialogTransitionAnimator
import com.android.systemui.concurrency.fakeExecutor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.android.traceur.TraceConfig
import com.google.common.truth.Truth
@@ -52,7 +52,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class IssueRecordingServiceSessionTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val bgExecutor = kosmos.fakeExecutor
private val userContextProvider: UserContextProvider = kosmos.userTracker
private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 83bdcd2..0510e6ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -22,9 +22,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import org.junit.Before
@@ -40,7 +40,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class IssueRecordingStateTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private lateinit var underTest: IssueRecordingState
@Mock private lateinit var resolver: ContentResolver
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
index 737b101..6f0dd16 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
@@ -20,10 +20,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.settings.UserTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import org.junit.Before
import org.junit.Test
@@ -34,7 +34,7 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class ScreenRecordingStartTimeStoreTest : SysuiTestCase() {
- private val userTracker: UserTracker = Kosmos().also { it.testCase = this }.userTracker
+ private val userTracker: UserTracker = testKosmos().also { it.testCase = this }.userTracker
private lateinit var underTest: ScreenRecordingStartTimeStore
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index a82a7de..7e9487b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -25,7 +25,6 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -38,6 +37,7 @@
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.init.NotificationsController
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -57,7 +57,7 @@
@RunWith(AndroidJUnit4::class)
class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val testDispatcher = StandardTestDispatcher()
private val iStatusBarService = mock<IStatusBarService>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index 9724974..bd54166 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -21,10 +21,10 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -39,7 +39,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenRecordRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val recordingController = mock<RecordingController>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index c6ce581..0c90d07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -25,7 +25,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.assist.AssistManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -43,6 +42,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -63,7 +63,7 @@
@SmallTest
class ShadeControllerImplTest : SysuiTestCase() {
private val executor = FakeExecutor(FakeSystemClock())
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@Mock private lateinit var commandQueue: CommandQueue
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index 054c1b8..32eec56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -35,6 +34,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
@@ -52,7 +52,7 @@
@RunWith(AndroidJUnit4::class)
@EnableSceneContainer
class ShadeControllerSceneImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ddad230..2f2fafa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -25,8 +25,8 @@
import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.clocks.ClockAxisStyle
import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
@@ -543,7 +543,7 @@
@Test
fun jsonDeserialization_fontAxes() {
- val expected = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+ val expected = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
val json = JSONObject("""{"axes":[{"key":"KEY","value":10}]}""")
val actual = ClockSettings.fromJson(json)
assertEquals(expected, actual)
@@ -576,7 +576,7 @@
@Test
fun jsonSerialization_axisSettings() {
- val settings = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+ val settings = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
val actual = ClockSettings.toJson(settings)
val expected = JSONObject("""{"metadata":{},"axes":[{"key":"KEY","value":10}]}""")
assertEquals(expected.toString(), actual.toString())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 01046cd..3c19179 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -227,7 +227,7 @@
if (NotificationBundleUi.isEnabled()) {
return row.getEntryAdapter().getSbn().getNotification();
} else {
- return row.getEntry().getSbn().getNotification();
+ return row.getEntryLegacy().getSbn().getNotification();
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 326d8ff..3ecf302 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -32,14 +32,12 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
import com.android.systemui.shade.ShadeExpansionChangeEvent
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.WallpaperController
import com.android.systemui.util.mockito.eq
@@ -77,7 +75,6 @@
private val kosmos = testKosmos()
private val applicationScope = kosmos.testScope.backgroundScope
- @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var blurUtils: BlurUtils
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@@ -87,6 +84,8 @@
@Mock private lateinit var wallpaperController: WallpaperController
@Mock private lateinit var wallpaperInteractor: WallpaperInteractor
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+ @Mock private lateinit var shadeModeInteractor: ShadeModeInteractor
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut>
@Mock private lateinit var root: View
@@ -103,7 +102,6 @@
private var statusBarState = StatusBarState.SHADE
private val maxBlur = 150
private lateinit var notificationShadeDepthController: NotificationShadeDepthController
- private val configurationController = FakeConfigurationController()
@Before
fun setup() {
@@ -133,13 +131,11 @@
wallpaperInteractor,
notificationShadeWindowController,
dozeParameters,
- context,
- ResourcesSplitShadeStateController(),
+ shadeModeInteractor,
windowRootViewBlurInteractor,
appZoomOutOptional,
applicationScope,
- dumpManager,
- configurationController,
+ dumpManager
)
notificationShadeDepthController.shadeAnimation = shadeAnimation
notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
@@ -492,15 +488,10 @@
}
private fun enableSplitShade() {
- setSplitShadeEnabled(true)
+ `when` (shadeModeInteractor.isSplitShade).thenReturn(true)
}
private fun disableSplitShade() {
- setSplitShadeEnabled(false)
- }
-
- private fun setSplitShadeEnabled(enabled: Boolean) {
- overrideResource(R.bool.config_use_split_notification_shade, enabled)
- configurationController.notifyConfigurationChanged()
+ `when` (shadeModeInteractor.isSplitShade).thenReturn(false)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index 03dee3a..72d21f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -24,13 +24,13 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.kotlin.JavaAdapter
@@ -54,7 +54,7 @@
private lateinit var underTest: OperatorNameViewController
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = TestScope()
private val view = OperatorNameView(mContext)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 485b9fe..2fa9a02 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -17,12 +17,17 @@
package com.android.systemui.statusbar.chips.call.ui.viewmodel
import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -51,6 +56,7 @@
import kotlin.test.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -481,6 +487,294 @@
verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(pendingIntent, null)
}
+ @Test
+ @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ @EnableChipsModernization
+ fun chipWithReturnAnimation_updatesCorrectly_withStateAndTransitionState() =
+ kosmos.runTest {
+ val pendingIntent = mock<PendingIntent>()
+ val intent = mock<Intent>()
+ whenever(pendingIntent.intent).thenReturn(intent)
+ val component = mock<ComponentName>()
+ whenever(intent.component).thenReturn(component)
+
+ val expandable = mock<Expandable>()
+ val activityController = mock<ActivityTransitionAnimator.Controller>()
+ whenever(
+ expandable.activityTransitionController(
+ anyOrNull(),
+ anyOrNull(),
+ any(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(activityController)
+
+ val latest by collectLastValue(underTest.chip)
+
+ // Start off with no call.
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+ // Call starts [NoCall -> InCall(isAppVisible=true), NoTransition].
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+ val factory = latest!!.transitionManager!!.controllerFactory
+ assertThat(factory!!.component).isEqualTo(component)
+
+ // Request a return transition [InCall(isAppVisible=true), NoTransition ->
+ // ReturnRequested].
+ factory.onCompose(expandable)
+ var controller = factory.createController(forLaunch = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Start the return transition [InCall(isAppVisible=true), ReturnRequested ->
+ // Returning].
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // End the return transition [InCall(isAppVisible=true), Returning -> NoTransition].
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Settle the return transition [InCall(isAppVisible=true) ->
+ // InCall(isAppVisible=false), NoTransition].
+ kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Trigger a launch transition [InCall(isAppVisible=false) -> InCall(isAppVisible=true),
+ // NoTransition].
+ kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, true)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Request the return transition [InCall(isAppVisible=true), NoTransition ->
+ // LaunchRequested].
+ controller = factory.createController(forLaunch = true)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // Start the return transition [InCall(isAppVisible=true), LaunchRequested ->
+ // Launching].
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // End the return transition [InCall(isAppVisible=true), Launching -> NoTransition].
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+
+ // End the call with the app visible [InCall(isAppVisible=true) -> NoCall,
+ // NoTransition].
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+ // End the call with the app hidden [InCall(isAppVisible=false) -> NoCall,
+ // NoTransition].
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ isAppVisible = false,
+ )
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+ }
+
+ @Test
+ @DisableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ fun chipLegacy_hasNoTransitionAnimationInformation() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ // NoCall
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest!!.transitionManager).isNull()
+
+ // InCall with visible app
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest!!.transitionManager).isNull()
+
+ // InCall with hidden app
+ kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+ assertThat(latest!!.transitionManager).isNull()
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ @EnableChipsModernization
+ fun chipWithReturnAnimation_chipDataChangesMidTransition() =
+ kosmos.runTest {
+ val pendingIntent = mock<PendingIntent>()
+ val intent = mock<Intent>()
+ whenever(pendingIntent.intent).thenReturn(intent)
+ val component = mock<ComponentName>()
+ whenever(intent.component).thenReturn(component)
+
+ val expandable = mock<Expandable>()
+ val activityController = mock<ActivityTransitionAnimator.Controller>()
+ whenever(
+ expandable.activityTransitionController(
+ anyOrNull(),
+ anyOrNull(),
+ any(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(activityController)
+
+ val latest by collectLastValue(underTest.chip)
+
+ // Start with the app visible and trigger a return animation.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ var factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ var controller = factory.createController(forLaunch = false)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip changes state.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 0,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // Reset the state and trigger a launch animation.
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ controller = factory.createController(forLaunch = true)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip changes state.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = -2,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ }
+
+ @Test
+ @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+ @EnableChipsModernization
+ fun chipWithReturnAnimation_chipDisappearsMidTransition() =
+ kosmos.runTest {
+ val pendingIntent = mock<PendingIntent>()
+ val intent = mock<Intent>()
+ whenever(pendingIntent.intent).thenReturn(intent)
+ val component = mock<ComponentName>()
+ whenever(intent.component).thenReturn(component)
+
+ val expandable = mock<Expandable>()
+ val activityController = mock<ActivityTransitionAnimator.Controller>()
+ whenever(
+ expandable.activityTransitionController(
+ anyOrNull(),
+ anyOrNull(),
+ any(),
+ anyOrNull(),
+ any(),
+ )
+ )
+ .thenReturn(activityController)
+
+ val latest by collectLastValue(underTest.chip)
+
+ // Start with the app visible and trigger a return animation.
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ var factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ var controller = factory.createController(forLaunch = false)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip disappears.
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+
+ // Reset the state and trigger a launch animation.
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+ addOngoingCallState(
+ key = NOTIFICATION_KEY,
+ startTimeMs = 345,
+ contentIntent = pendingIntent,
+ uid = NOTIFICATION_UID,
+ isAppVisible = true,
+ )
+ factory = latest!!.transitionManager!!.controllerFactory!!
+ factory.onCompose(expandable)
+ controller = factory.createController(forLaunch = true)
+ controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+ // The chip disappears.
+ removeOngoingCallState(key = NOTIFICATION_KEY)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+ }
+
companion object {
fun createStatusBarIconViewOrNull(): StatusBarIconView? =
if (StatusBarConnectedDisplays.isEnabled) {
@@ -500,6 +794,8 @@
}
.build()
+ private const val NOTIFICATION_KEY = "testKey"
+ private const val NOTIFICATION_UID = 12345
private const val PROMOTED_BACKGROUND_COLOR = 65
private const val PROMOTED_PRIMARY_TEXT_COLOR = 98
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
index b2174c1..21a4560 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
@@ -20,12 +20,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
import com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor.mediaRouterChipInteractor
import com.android.systemui.statusbar.chips.casttootherdevice.domain.model.MediaRouterCastModel
import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -35,7 +35,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class MediaRouterChipInteractorTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos()
val testScope = kosmos.testScope
val mediaRouterRepository = kosmos.fakeMediaRouterRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
index 274efbb..5681297 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndCastScreenToOtherDeviceDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
private lateinit var underTest: EndCastScreenToOtherDeviceDialogDelegate
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
index 88207d1..30a415c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
@@ -26,7 +26,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
@@ -35,6 +34,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -51,7 +51,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndGenericCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
private lateinit var underTest: EndGenericCastToOtherDeviceDialogDelegate
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index ccc844a..d921ab3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -52,6 +51,7 @@
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -69,7 +69,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
private val mediaRouterRepo = kosmos.fakeMediaRouterRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
index 795988f..fac50b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
@@ -24,12 +24,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.runner.RunWith
@@ -42,7 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val underTest = kosmos.endMediaProjectionDialogHelper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
index f560ee7..981c952 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -39,6 +38,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -55,7 +55,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndScreenRecordingDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
index 95aa6cd..b2e90ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class EndShareScreenToAppDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
private lateinit var underTest: EndShareScreenToAppDialogDelegate
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index e3a84fd..6d91fb5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -22,12 +22,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,7 +39,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class ChipTransitionHelperTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 2f6bedb..ea61b71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -29,7 +29,6 @@
import com.android.internal.view.AppearanceRegion
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.layout.BoundsPair
@@ -42,6 +41,7 @@
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -59,7 +59,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class StatusBarModeRepositoryImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope = TestScope()
private val commandQueue = mock<CommandQueue>()
private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 790b2c3..bfd700d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.util.time.FakeSystemClock;
@@ -151,7 +152,8 @@
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isBlockable());
}
@@ -251,7 +253,8 @@
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
@@ -365,7 +368,8 @@
.setKey(sbn.getKey())
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isChannelVisibilityPrivate());
}
@@ -378,7 +382,8 @@
.setKey(sbn.getKey())
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
assertFalse(entry.isChannelVisibilityPrivate());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
index ef0a416..d532010 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener
import com.android.systemui.statusbar.notification.collection.notifPipeline
@@ -323,7 +324,10 @@
setPulsing(true)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN group changes aren't allowed
assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
@@ -349,7 +353,10 @@
setPulsing(false)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.uptimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN the notification list is invalidated
verifyStabilityManagerWasInvalidated(times(1))
@@ -365,7 +372,10 @@
setPulsing(false)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN invalidate is not called because this entry was never suppressed from reordering
verifyStabilityManagerWasInvalidated(never())
@@ -382,7 +392,10 @@
assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// THEN invalidate is not called because this entry was never suppressed from
// reordering;
@@ -415,7 +428,10 @@
setPulsing(true)
// WHEN we temporarily allow section changes for this notification entry
- underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ underTest.temporarilyAllowSectionChanges(
+ entry,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+ )
// can now reorder, so invalidates
verifyStabilityManagerWasInvalidated(times(1))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 1b8d64d..387c62d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
@@ -80,6 +81,9 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -107,9 +111,6 @@
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import kotlin.test.assertNotNull
-import kotlin.test.fail
/** Tests for [NotificationGutsManager]. */
@SmallTest
@@ -149,6 +150,7 @@
@Mock private lateinit var launcherApps: LauncherApps
@Mock private lateinit var shortcutManager: ShortcutManager
@Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
@Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock private lateinit var contextTracker: UserContextProvider
@Mock private lateinit var bubblesManager: BubblesManager
@@ -214,6 +216,7 @@
launcherApps,
shortcutManager,
channelEditorDialogController,
+ packageDemotionInteractor,
contextTracker,
assistantFeedbackController,
Optional.of(bubblesManager),
@@ -509,6 +512,7 @@
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -521,6 +525,7 @@
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -530,6 +535,7 @@
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(true),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -545,6 +551,7 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -560,6 +567,7 @@
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -569,6 +577,7 @@
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(true),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -584,6 +593,7 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -597,6 +607,7 @@
any<NotificationIconStyleProvider>(),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -606,6 +617,7 @@
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
+ /* isDismissable = */ eq(true),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
index 96ae070..0ac5fe9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
@@ -49,6 +49,7 @@
import android.view.View.VISIBLE
import android.widget.ImageView
import android.widget.TextView
+import androidx.core.view.isVisible
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
@@ -57,17 +58,18 @@
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Dependency
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+import com.android.systemui.testKosmos
import com.android.telecom.telecomManager
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
@@ -89,7 +91,7 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationInfoTest : SysuiTestCase() {
- private val kosmos = Kosmos().also { it.testCase = this }
+ private val kosmos = testKosmos().also { it.testCase = this }
private lateinit var underTest: NotificationInfo
private lateinit var notificationChannel: NotificationChannel
@@ -105,6 +107,7 @@
private val onUserInteractionCallback = mock<OnUserInteractionCallback>()
private val mockINotificationManager = mock<INotificationManager>()
private val channelEditorDialogController = mock<ChannelEditorDialogController>()
+ private val packageDemotionInteractor = mock<PackageDemotionInteractor>()
private val assistantFeedbackController = mock<AssistantFeedbackController>()
@Before
@@ -863,6 +866,31 @@
assertThat(underTest.findViewById<View>(R.id.feedback).visibility).isEqualTo(GONE)
}
+ @Test
+ @Throws(RemoteException::class)
+ fun testDismissListenerBound() {
+ val latch = CountDownLatch(1)
+ bindNotification(onCloseClick = { _: View? -> latch.countDown() })
+
+ val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+ assertThat(dismissView.isVisible).isTrue()
+ dismissView.performClick()
+
+ // Verify that listener was triggered.
+ assertThat(latch.count).isEqualTo(0)
+ }
+
+ @Test
+ @Throws(RemoteException::class)
+ fun testDismissHiddenWhenUndismissable() {
+
+ entry.sbn.notification.flags =
+ entry.sbn.notification.flags or android.app.Notification.FLAG_NO_DISMISS
+ bindNotification(isDismissable = false)
+ val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+ assertThat(dismissView.isVisible).isFalse()
+ }
+
private fun bindNotification(
pm: PackageManager = this.mockPackageManager,
iNotificationManager: INotificationManager = this.mockINotificationManager,
@@ -871,6 +899,7 @@
onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback,
channelEditorDialogController: ChannelEditorDialogController =
this.channelEditorDialogController,
+ packageDemotionInteractor: PackageDemotionInteractor = this.packageDemotionInteractor,
pkg: String = TEST_PACKAGE_NAME,
notificationChannel: NotificationChannel = this.notificationChannel,
entry: NotificationEntry = this.entry,
@@ -880,6 +909,7 @@
uiEventLogger: UiEventLogger = this.uiEventLogger,
isDeviceProvisioned: Boolean = true,
isNonblockable: Boolean = false,
+ isDismissable: Boolean = true,
wasShownHighPriority: Boolean = true,
assistantFeedbackController: AssistantFeedbackController = this.assistantFeedbackController,
metricsLogger: MetricsLogger = kosmos.metricsLogger,
@@ -892,6 +922,7 @@
iconStyleProvider,
onUserInteractionCallback,
channelEditorDialogController,
+ packageDemotionInteractor,
pkg,
notificationChannel,
entry,
@@ -901,6 +932,7 @@
uiEventLogger,
isDeviceProvisioned,
isNonblockable,
+ isDismissable,
wasShownHighPriority,
assistantFeedbackController,
metricsLogger,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index af67a04..2d4063b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -27,7 +27,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableResources;
import android.view.View;
@@ -39,7 +38,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -93,7 +91,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
public void closeControls_withoutSave_performsUndo() {
ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
mUnderTest.mSelectedOption = options.getFirst();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
index 5638e0b..209dfb2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -92,6 +93,8 @@
@Mock
private ChannelEditorDialogController mChannelEditorDialogController;
@Mock
+ private PackageDemotionInteractor mPackageDemotionInteractor;
+ @Mock
private AssistantFeedbackController mAssistantFeedbackController;
@Mock
private TelecomManager mTelecomManager;
@@ -138,6 +141,7 @@
mMockIconStyleProvider,
mOnUserInteractionCallback,
mChannelEditorDialogController,
+ mPackageDemotionInteractor,
TEST_PACKAGE_NAME,
mNotificationChannel,
mEntry,
@@ -148,6 +152,7 @@
true,
false,
true,
+ true,
mAssistantFeedbackController,
mMetricsLogger,
null);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 048028c..db0c59f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
@@ -23,12 +23,12 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.statusbar.lockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -44,7 +44,7 @@
class NotificationShelfInteractorTest : SysuiTestCase() {
private val kosmos =
- Kosmos().apply {
+ testKosmos().apply {
testCase = this@NotificationShelfInteractorTest
lockscreenShadeTransitionController = mock()
screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 6381b4e..7265262 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -24,7 +24,6 @@
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testCase
@@ -35,6 +34,7 @@
import com.android.systemui.shade.domain.interactor.enableSplitShade
import com.android.systemui.statusbar.lockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -50,7 +50,7 @@
class NotificationShelfViewModelTest : SysuiTestCase() {
private val kosmos =
- Kosmos().apply {
+ testKosmos().apply {
testCase = this@NotificationShelfViewModelTest
lockscreenShadeTransitionController = mock()
screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index c5abd02..19e9838 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -106,6 +106,7 @@
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
return entry;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index eb95ddb..c58b4bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
@@ -48,6 +47,7 @@
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -70,7 +70,7 @@
@TestableLooper.RunWithLooper
@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
class OngoingCallControllerTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val mainExecutor = kosmos.fakeExecutor
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index a446313..7de56dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -19,12 +19,11 @@
import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,7 +32,7 @@
@RunWith(AndroidJUnit4::class)
@DisableFlags(StatusBarChipsModernization.FLAG_NAME)
class OngoingCallRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val underTest = kosmos.ongoingCallRepository
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index f4204af7..84f1d5c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -23,7 +23,6 @@
import com.android.systemui.activity.data.repository.activityManagerRepository
import com.android.systemui.activity.data.repository.fake
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -36,6 +35,7 @@
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -51,7 +51,7 @@
@RunWith(AndroidJUnit4::class)
@EnableChipsModernization
class OngoingCallInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos().useUnconfinedTestDispatcher()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val underTest = kosmos.ongoingCallInteractor
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 91b3896..39cf02d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -54,9 +54,7 @@
MutableStateFlow(OngoingActivityChipModel.Inactive())
override val ongoingActivityChips =
- MutableStateFlow(
- ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
- )
+ ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
override val ongoingActivityChipsLegacy =
MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 2da692b..20cf3ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -49,6 +49,7 @@
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.log.assertLogsWtf
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -107,7 +108,8 @@
@RunWith(AndroidJUnit4::class)
class HomeStatusBarViewModelImplTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private val Kosmos.underTest by Kosmos.Fixture { kosmos.homeStatusBarViewModel }
+ private val Kosmos.underTest by
+ Kosmos.Fixture { kosmos.homeStatusBarViewModel.also { it.activateIn(kosmos.testScope) } }
@Before
fun setUp() {
@@ -891,32 +893,26 @@
@EnableChipsModernization
fun ongoingActivityChips_statusBarHidden_noSecureCamera_noHun_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
// home status bar not allowed
kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_secureCamera_noHun_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.OCCLUDED,
@@ -924,7 +920,7 @@
)
kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@@ -932,8 +928,6 @@
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOff_notAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -943,7 +937,7 @@
)
)
- assertThat(latest!!.areChipsAllowed).isFalse()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
}
@Test
@@ -951,8 +945,6 @@
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOff_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -962,16 +954,14 @@
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
@EnableChipsModernization
- fun ongoingActivityChips_tatusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
+ fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -981,7 +971,7 @@
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@@ -989,8 +979,6 @@
@EnableChipsModernization
fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOn_isAllowed() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
-
transitionKeyguardToGone()
headsUpNotificationRepository.setNotifications(
@@ -1000,7 +988,7 @@
)
)
- assertThat(latest!!.areChipsAllowed).isTrue()
+ assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
}
@Test
@@ -1008,17 +996,16 @@
@EnableChipsModernization
fun ongoingActivityChips_followsChipsViewModel() =
kosmos.runTest {
- val latest by collectLastValue(underTest.ongoingActivityChips)
transitionKeyguardToGone()
screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
- assertIsScreenRecordChip(latest!!.chips.active[0])
+ assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
addOngoingCallState(key = "call")
- assertIsScreenRecordChip(latest!!.chips.active[0])
- assertIsCallChip(latest!!.chips.active[1], "call", context)
+ assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
+ assertIsCallChip(underTest.ongoingActivityChips.chips.active[1], "call", context)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 92bec70..18a124c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -38,7 +38,6 @@
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.foldedDeviceStateList
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
@@ -47,6 +46,7 @@
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.COOL_DOWN_DURATION
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
@@ -89,7 +89,7 @@
private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
@Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val mockContext = mock<Context>()
private val resources = mock<Resources>()
private val foldStateRepository = kosmos.fakeDeviceStateRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
index dfc4d0f..98d99f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -36,6 +35,7 @@
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.testKosmos
import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
@@ -61,7 +61,7 @@
class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule(this)
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testScope: TestScope = kosmos.testScope
private val fakeDeviceStateRepository = kosmos.fakeDeviceStateRepository
private val powerInteractor = kosmos.powerInteractor
@@ -93,7 +93,7 @@
fakeAnimationStatusRepository,
mockControllerFactory,
mockFoldLockSettingAvailabilityProvider,
- mockJankMonitor
+ mockJankMonitor,
)
foldLightRevealAnimation.init()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
index b4ba41a..a180d51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
@@ -27,7 +27,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceStateManager
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
@@ -39,7 +39,7 @@
@RunWith(AndroidJUnit4::class)
class UtilsTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val deviceStateManager = kosmos.deviceStateManager
private lateinit var testableResources: TestableResources
@@ -53,7 +53,7 @@
fun isFoldableReturnsFalse_overlayConfigurationValues() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf() // empty array <=> device is not foldable
+ intArrayOf(), // empty array <=> device is not foldable
)
whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -64,7 +64,7 @@
fun isFoldableReturnsFalse_deviceStateManager() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf() // empty array <=> device is not foldable
+ intArrayOf(), // empty array <=> device is not foldable
)
whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -75,7 +75,7 @@
fun isFoldableReturnsTrue_overlayConfigurationValues() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf(FOLDED_DEVICE_STATE.identifier)
+ intArrayOf(FOLDED_DEVICE_STATE.identifier),
)
whenever(deviceStateManager.supportedDeviceStates)
.thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
@@ -87,7 +87,7 @@
fun isFoldableReturnsTrue_deviceStateManager() {
testableResources.addOverride(
com.android.internal.R.array.config_foldedDeviceStates,
- intArrayOf(FOLDED_DEVICE_STATE.identifier)
+ intArrayOf(FOLDED_DEVICE_STATE.identifier),
)
whenever(deviceStateManager.supportedDeviceStates)
.thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
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 f232d52..93e5721 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
@@ -20,8 +20,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.volume.panel.domain.availableCriteria
import com.android.systemui.volume.panel.domain.defaultCriteria
import com.android.systemui.volume.panel.domain.model.ComponentModel
@@ -39,7 +39,7 @@
@RunWith(AndroidJUnit4::class)
class ComponentsInteractorImplTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private lateinit var underTest: ComponentsInteractor
@@ -60,12 +60,7 @@
fun componentsAvailability_checked() {
with(kosmos) {
testScope.runTest {
- enabledComponents =
- setOf(
- BOTTOM_BAR,
- COMPONENT_1,
- COMPONENT_2,
- )
+ enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
criteriaByKey =
mapOf(
BOTTOM_BAR to Provider { availableCriteria },
@@ -90,12 +85,7 @@
fun noCriteria_fallbackToDefaultCriteria() {
with(kosmos) {
testScope.runTest {
- enabledComponents =
- setOf(
- BOTTOM_BAR,
- COMPONENT_1,
- COMPONENT_2,
- )
+ enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
criteriaByKey =
mapOf(
BOTTOM_BAR to Provider { availableCriteria },
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
index 235475f..0fc3470 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
@@ -44,5 +44,5 @@
fun onZenDataChanged(data: ZenData)
/** Update reactive axes for this clock */
- fun onFontAxesChanged(axes: List<ClockFontAxisSetting>)
+ fun onFontAxesChanged(axes: ClockAxisStyle)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
index 0cbc30d..2147ca1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
@@ -35,10 +35,54 @@
/** Font axes that can be modified on this clock */
val axes: List<ClockFontAxis> = listOf(),
- /** List of font presets for this clock. Can be assigned directly. */
- val axisPresets: List<List<ClockFontAxisSetting>> = listOf(),
+ /** Presets for this clock. Null indicates the preset list should be disabled. */
+ val presetConfig: AxisPresetConfig? = null,
)
+data class AxisPresetConfig(
+ /** Groups of Presets. Each group can be used together in a single control. */
+ val groups: List<Group>,
+
+ /** Preset item currently being used, null when the current style is not a preset */
+ val current: IndexedStyle? = null,
+) {
+ /** The selected clock axis style, and its indices */
+ data class IndexedStyle(
+ /** Index of the group that this clock axis style appears in */
+ val groupIndex: Int,
+
+ /** Index of the preset within the group */
+ val presetIndex: Int,
+
+ /** Reference to the style in question */
+ val style: ClockAxisStyle,
+ )
+
+ /** A group of preset styles */
+ data class Group(
+ /* List of preset styles in this group */
+ val presets: List<ClockAxisStyle>,
+
+ /* Icon to use when this preset-group is active */
+ val icon: Drawable,
+ )
+
+ fun findStyle(style: ClockAxisStyle): IndexedStyle? {
+ groups.forEachIndexed { groupIndex, group ->
+ group.presets.forEachIndexed { presetIndex, preset ->
+ if (preset == style) {
+ return@findStyle IndexedStyle(
+ groupIndex = groupIndex,
+ presetIndex = presetIndex,
+ style = preset,
+ )
+ }
+ }
+ }
+ return null
+ }
+}
+
/** Represents an Axis that can be modified */
data class ClockFontAxis(
/** Axis key, not user renderable */
@@ -62,19 +106,12 @@
/** Description of the axis */
val description: String,
) {
- fun toSetting() = ClockFontAxisSetting(key, currentValue)
-
companion object {
- fun List<ClockFontAxis>.merge(
- axisSettings: List<ClockFontAxisSetting>
- ): List<ClockFontAxis> {
- val result = mutableListOf<ClockFontAxis>()
- for (axis in this) {
- val setting = axisSettings.firstOrNull { axis.key == it.key }
- val output = setting?.let { axis.copy(currentValue = it.value) } ?: axis
- result.add(output)
- }
- return result
+ fun List<ClockFontAxis>.merge(axisStyle: ClockAxisStyle): List<ClockFontAxis> {
+ return this.map { axis ->
+ axisStyle.get(axis.key)?.let { axis.copy(currentValue = it) } ?: axis
+ }
+ .toList()
}
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
index e7b3662..cccc558 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
@@ -22,7 +22,7 @@
data class ClockSettings(
val clockId: ClockId? = null,
val seedColor: Int? = null,
- val axes: List<ClockFontAxisSetting> = listOf(),
+ val axes: ClockAxisStyle = ClockAxisStyle(),
) {
// Exclude metadata from equality checks
var metadata: JSONObject = JSONObject()
@@ -38,15 +38,15 @@
put(KEY_CLOCK_ID, setting.clockId)
put(KEY_SEED_COLOR, setting.seedColor)
put(KEY_METADATA, setting.metadata)
- put(KEY_AXIS_LIST, ClockFontAxisSetting.toJson(setting.axes))
+ put(KEY_AXIS_LIST, ClockAxisStyle.toJson(setting.axes))
}
}
fun fromJson(json: JSONObject): ClockSettings {
val clockId = if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null
val seedColor = if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
- val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockFontAxisSetting::fromJson)
- return ClockSettings(clockId, seedColor, axisList ?: listOf()).apply {
+ val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockAxisStyle::fromJson)
+ return ClockSettings(clockId, seedColor, axisList ?: ClockAxisStyle()).apply {
metadata = json.optJSONObject(KEY_METADATA) ?: JSONObject()
}
}
@@ -54,64 +54,102 @@
}
@Keep
-/** Axis setting value for a clock */
-data class ClockFontAxisSetting(
- /** Axis key; matches ClockFontAxis.key */
- val key: String,
+class ClockAxisStyle {
+ private val settings: MutableMap<String, Float>
- /** Value to set this axis to */
- val value: Float,
-) {
+ // Iterable would be implemented on ClockAxisStyle directly,
+ // but that doesn't appear to work with plugins/dynamic libs.
+ val items: Iterable<Map.Entry<String, Float>>
+ get() = settings.asIterable()
+
+ val isEmpty: Boolean
+ get() = settings.isEmpty()
+
+ constructor(initialize: ClockAxisStyle.() -> Unit = {}) {
+ settings = mutableMapOf()
+ this.initialize()
+ }
+
+ constructor(style: ClockAxisStyle) {
+ settings = style.settings.toMutableMap()
+ }
+
+ constructor(items: Map<String, Float>) {
+ settings = items.toMutableMap()
+ }
+
+ constructor(key: String, value: Float) {
+ settings = mutableMapOf(key to value)
+ }
+
+ constructor(items: List<ClockFontAxis>) {
+ settings = items.associate { it.key to it.currentValue }.toMutableMap()
+ }
+
+ fun copy(initialize: ClockAxisStyle.() -> Unit): ClockAxisStyle {
+ return ClockAxisStyle(this).apply { initialize() }
+ }
+
+ operator fun get(key: String): Float? = settings[key]
+
+ operator fun set(key: String, value: Float) = put(key, value)
+
+ fun put(key: String, value: Float) {
+ settings.put(key, value)
+ }
+
+ fun toFVar(): String {
+ val sb = StringBuilder()
+ for (axis in settings) {
+ if (sb.length > 0) sb.append(", ")
+ sb.append("'${axis.key}' ${axis.value.toInt()}")
+ }
+ return sb.toString()
+ }
+
+ fun copyWith(replacements: ClockAxisStyle): ClockAxisStyle {
+ val result = ClockAxisStyle(this)
+ for ((key, value) in replacements.settings) {
+ result[key] = value
+ }
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is ClockAxisStyle) return false
+ return settings == other.settings
+ }
+
companion object {
private val KEY_AXIS_KEY = "key"
private val KEY_AXIS_VALUE = "value"
- fun toJson(setting: ClockFontAxisSetting): JSONObject {
- return JSONObject().apply {
- put(KEY_AXIS_KEY, setting.key)
- put(KEY_AXIS_VALUE, setting.value)
- }
- }
-
- fun toJson(settings: List<ClockFontAxisSetting>): JSONArray {
- return JSONArray().apply {
- for (axis in settings) {
- put(toJson(axis))
- }
- }
- }
-
- fun fromJson(jsonObj: JSONObject): ClockFontAxisSetting {
- return ClockFontAxisSetting(
- key = jsonObj.getString(KEY_AXIS_KEY),
- value = jsonObj.getDouble(KEY_AXIS_VALUE).toFloat(),
- )
- }
-
- fun fromJson(jsonArray: JSONArray): List<ClockFontAxisSetting> {
- val result = mutableListOf<ClockFontAxisSetting>()
+ fun fromJson(jsonArray: JSONArray): ClockAxisStyle {
+ val result = ClockAxisStyle()
for (i in 0..jsonArray.length() - 1) {
val obj = jsonArray.getJSONObject(i)
if (obj == null) continue
- result.add(fromJson(obj))
+
+ result.put(
+ key = obj.getString(KEY_AXIS_KEY),
+ value = obj.getDouble(KEY_AXIS_VALUE).toFloat(),
+ )
}
return result
}
- fun List<ClockFontAxisSetting>.toFVar(): String {
- val sb = StringBuilder()
- for (axis in this) {
- if (sb.length > 0) sb.append(", ")
- sb.append("'${axis.key}' ${axis.value.toInt()}")
+ fun toJson(style: ClockAxisStyle): JSONArray {
+ return JSONArray().apply {
+ for ((key, value) in style.settings) {
+ put(
+ JSONObject().apply {
+ put(KEY_AXIS_KEY, key)
+ put(KEY_AXIS_VALUE, value)
+ }
+ )
+ }
}
- return sb.toString()
- }
-
- fun List<ClockFontAxisSetting>.replace(
- replacements: List<ClockFontAxisSetting>
- ): List<ClockFontAxisSetting> {
- var remaining = this.filterNot { lhs -> replacements.any { rhs -> lhs.key == rhs.key } }
- return remaining + replacements
}
}
}
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 949a6ab..a1b26fc 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -85,13 +85,49 @@
android:longClickable="false"/>
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/input_routing_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/preset_layout"
+ android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView
+ android:id="@+id/input_routing_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+ android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+ android:paddingStart="@dimen/hearing_devices_small_title_padding_horizontal"
+ android:text="@string/hearing_devices_input_routing_label"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:textSize="14sp"
+ android:gravity="center_vertical"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textDirection="locale"/>
+ <Spinner
+ android:id="@+id/input_routing_spinner"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:layout_marginTop="4dp"
+ android:paddingStart="0dp"
+ android:paddingEnd="0dp"
+ android:background="@drawable/hearing_devices_spinner_background"
+ android:popupBackground="@drawable/hearing_devices_spinner_popup_background"
+ android:dropDownWidth="match_parent"
+ android:longClickable="false"/>
+ </LinearLayout>
+
<com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout
android:id="@+id/ambient_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/preset_layout"
+ app:layout_constraintTop_toBottomOf="@id/input_routing_layout"
android:layout_marginTop="@dimen/hearing_devices_layout_margin" />
<LinearLayout
diff --git a/packages/SystemUI/res/layout/notification_2025_info.xml b/packages/SystemUI/res/layout/notification_2025_info.xml
index 7b69166..fa852a2 100644
--- a/packages/SystemUI/res/layout/notification_2025_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_info.xml
@@ -18,6 +18,7 @@
<!-- extends LinearLayout -->
<com.android.systemui.statusbar.notification.row.NotificationInfo
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/notification_guts"
android:layout_width="match_parent"
@@ -324,18 +325,34 @@
</com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
</LinearLayout>
-
- <LinearLayout
+ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bottom_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@*android:dimen/notification_2025_margin"
android:minHeight="@dimen/notification_2025_guts_button_size"
- android:gravity="center_vertical"
- >
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingBottom="@*android:dimen/notification_2025_margin"
+ app:layout_constraintStart_toStartOf="parent"
+ android:gravity="center"
+ android:minWidth="@dimen/notification_2025_min_tap_target_size"
+ android:minHeight="@dimen/notification_2025_min_tap_target_size"
+ android:maxWidth="200dp"
+ style="@style/TextAppearance.NotificationInfo.Button"
+ android:textSize="@*android:dimen/notification_2025_action_text_size"
+ />
<TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
+ android:paddingStart="@dimen/notification_importance_button_padding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
@@ -345,6 +362,8 @@
android:minWidth="@dimen/notification_2025_min_tap_target_size"
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="200dp"
+ app:layout_constraintStart_toEndOf="@id/inline_dismiss"
+ app:layout_constraintBaseline_toBaselineOf="@id/inline_dismiss"
style="@style/TextAppearance.NotificationInfo.Button"
android:textSize="@*android:dimen/notification_2025_action_text_size"/>
<TextView
@@ -354,12 +373,18 @@
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="@*android:dimen/notification_2025_margin"
- android:gravity="center"
+ android:gravity="end"
+ app:layout_constraintEnd_toEndOf="parent"
android:minWidth="@dimen/notification_2025_min_tap_target_size"
android:minHeight="@dimen/notification_2025_min_tap_target_size"
android:maxWidth="125dp"
style="@style/TextAppearance.NotificationInfo.Button"
android:textSize="@*android:dimen/notification_2025_action_text_size"/>
- </LinearLayout>
+ <androidx.constraintlayout.helper.widget.Flow
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:constraint_referenced_ids="inline_dismiss,turn_off_notifications,done"
+ app:flow_wrapMode="chain"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</com.android.systemui.statusbar.notification.row.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 089ceae..d4bd142 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -341,8 +341,8 @@
android:paddingEnd="4dp"
>
<TextView
- android:id="@+id/turn_off_notifications"
- android:text="@string/inline_turn_off_notifications"
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
@@ -350,6 +350,19 @@
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/turn_off_notifications"
+ android:text="@string/inline_turn_off_notifications"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@id/inline_dismiss"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 4850b35..d1755ef 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -143,8 +143,8 @@
android:paddingEnd="4dp"
>
<TextView
- android:id="@+id/turn_off_notifications"
- android:text="@string/inline_turn_off_notifications"
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
@@ -152,6 +152,19 @@
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/turn_off_notifications"
+ android:text="@string/inline_turn_off_notifications"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@id/inline_dismiss"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
index 2e0a0ca..3982a66 100644
--- a/packages/SystemUI/res/layout/promoted_notification_info.xml
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -373,8 +373,8 @@
android:paddingEnd="4dp"
>
<TextView
- android:id="@+id/turn_off_notifications"
- android:text="@string/inline_turn_off_notifications"
+ android:id="@+id/inline_dismiss"
+ android:text="@string/notification_inline_dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
@@ -382,6 +382,19 @@
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
+ android:paddingEnd="@dimen/notification_importance_button_padding"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/turn_off_notifications"
+ android:text="@string/inline_turn_off_notifications"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@id/inline_dismiss"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="200dp"
+ android:paddingStart="@dimen/notification_importance_button_padding"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f4c6904..15519ff 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -34,7 +34,7 @@
<!-- Base colors for notification shade/scrim, the alpha component is adjusted programmatically
to match the spec -->
- <color name="shade_panel_base">@android:color/system_accent1_900</color>
+ <color name="shade_panel_base">@android:color/system_accent1_100</color>
<color name="notification_scrim_base">@android:color/system_accent1_100</color>
<!-- Fallback colors for notification shade/scrim -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cd94a26..3fdb98b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1016,6 +1016,13 @@
<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: Title for hearing aids input routing control in hearing device dialog. [CHAR LIMIT=40]-->
+ <string name="hearing_devices_input_routing_label">Default microphone for calls</string>
+ <!-- QuickSettings: Option for hearing aids input routing control in hearing device dialog. It will alter input routing for calls for hearing aid. [CHAR LIMIT=40]-->
+ <string-array name="hearing_device_input_routing_options">
+ <item>Hearing aid microphone</item>
+ <item>This phone\'s microphone</item>
+ </string-array>
<!-- QuickSettings: Content description for the icon that indicates the item is selected [CHAR LIMIT=NONE]-->
<string name="hearing_devices_spinner_item_selected">Selected</string>
<!-- QuickSettings: Title for ambient controls. [CHAR LIMIT=40]-->
@@ -2056,7 +2063,7 @@
<string name="inline_ok_button">Apply</string>
<!-- Notification inline controls: button to show block screen [CHAR_LIMIT=35] -->
- <string name="inline_turn_off_notifications">Turn off notifications</string>
+ <string name="inline_turn_off_notifications">Turn off</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_silence_title">Silent</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 763b107..f2f1773 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -572,10 +572,6 @@
}
fun handleFidgetTap(x: Float, y: Float) {
- if (!com.android.systemui.Flags.clockFidgetAnimation()) {
- return
- }
-
clock?.run {
smallClock.animations.onFidgetTap(x, y)
largeClock.animations.onFidgetTap(x, y)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d84b034..60ec051 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -124,7 +124,6 @@
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -301,7 +300,8 @@
private final Provider<SceneInteractor> mSceneInteractor;
private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
private final Provider<CommunalSceneInteractor> mCommunalSceneInteractor;
- private final KeyguardServiceShowLockscreenInteractor mKeyguardServiceShowLockscreenInteractor;
+ private final Provider<KeyguardServiceShowLockscreenInteractor>
+ mKeyguardServiceShowLockscreenInteractor;
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -2219,7 +2219,8 @@
Provider<JavaAdapter> javaAdapter,
Provider<SceneInteractor> sceneInteractor,
Provider<CommunalSceneInteractor> communalSceneInteractor,
- KeyguardServiceShowLockscreenInteractor keyguardServiceShowLockscreenInteractor) {
+ Provider<KeyguardServiceShowLockscreenInteractor>
+ keyguardServiceShowLockscreenInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2553,7 +2554,7 @@
if (KeyguardWmStateRefactor.isEnabled()) {
mJavaAdapter.get().alwaysCollectFlow(
- mKeyguardServiceShowLockscreenInteractor.getShowNowEvents(),
+ mKeyguardServiceShowLockscreenInteractor.get().getShowNowEvents(),
this::onKeyguardServiceShowLockscreenNowEvents
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index c78f75a..0894667 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -894,8 +894,8 @@
if (NotificationBundleUi.isEnabled()) {
return enr.getEntryAdapter().canDragAndDrop();
} else {
- boolean canBubble = enr.getEntry().canBubble();
- Notification notif = enr.getEntry().getSbn().getNotification();
+ boolean canBubble = enr.getEntryLegacy().canBubble();
+ Notification notif = enr.getEntryLegacy().getSbn().getNotification();
PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
: notif.fullScreenIntent;
if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
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 fb3bc62..14b13d1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -121,6 +121,13 @@
private Spinner mPresetSpinner;
private HearingDevicesPresetsController mPresetController;
private HearingDevicesSpinnerAdapter mPresetInfoAdapter;
+
+ private View mInputRoutingLayout;
+ private Spinner mInputRoutingSpinner;
+ private HearingDevicesInputRoutingController.Factory mInputRoutingControllerFactory;
+ private HearingDevicesInputRoutingController mInputRoutingController;
+ private HearingDevicesSpinnerAdapter mInputRoutingAdapter;
+
private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
new HearingDevicesPresetsController.PresetCallback() {
@Override
@@ -169,7 +176,8 @@
@Background Executor bgExecutor,
AudioManager audioManager,
HearingDevicesUiEventLogger uiEventLogger,
- QSSettingsPackageRepository qsSettingsPackageRepository) {
+ QSSettingsPackageRepository qsSettingsPackageRepository,
+ HearingDevicesInputRoutingController.Factory inputRoutingControllerFactory) {
mShowPairNewDevice = showPairNewDevice;
mSystemUIDialogFactory = systemUIDialogFactory;
mActivityStarter = activityStarter;
@@ -182,6 +190,7 @@
mUiEventLogger = uiEventLogger;
mLaunchSourceId = launchSourceId;
mQSSettingsPackageRepository = qsSettingsPackageRepository;
+ mInputRoutingControllerFactory = inputRoutingControllerFactory;
}
@Override
@@ -239,6 +248,12 @@
mPresetLayout.setVisibility(
mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
}
+ if (mInputRoutingController != null) {
+ mInputRoutingController.setDevice(device);
+ mInputRoutingController.isInputRoutingControlAvailable(
+ available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+ available ? VISIBLE : GONE)));
+ }
if (mAmbientController != null) {
mAmbientController.loadDevice(device);
}
@@ -310,6 +325,11 @@
setupDeviceListView(dialog, hearingDeviceItemList);
setupPairNewDeviceButton(dialog);
setupPresetSpinner(dialog, activeHearingDevice);
+ if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()
+ && com.android.systemui.Flags
+ .hearingDevicesInputRoutingUiImprovement()) {
+ setupInputRoutingSpinner(dialog, activeHearingDevice);
+ }
if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
setupAmbientControls(activeHearingDevice);
}
@@ -383,6 +403,52 @@
mBgExecutor.execute(() -> mPresetController.registerHapCallback());
}
+ private void setupInputRoutingSpinner(SystemUIDialog dialog,
+ CachedBluetoothDevice activeHearingDevice) {
+ mInputRoutingController = mInputRoutingControllerFactory.create(dialog.getContext());
+ mInputRoutingController.setDevice(activeHearingDevice);
+
+ mInputRoutingSpinner = dialog.requireViewById(R.id.input_routing_spinner);
+ mInputRoutingAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext());
+ mInputRoutingAdapter.addAll(
+ HearingDevicesInputRoutingController.getInputRoutingOptions(dialog.getContext()));
+ mInputRoutingSpinner.setAdapter(mInputRoutingAdapter);
+ // Disable redundant Touch & Hold accessibility action for Switch Access
+ mInputRoutingSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfo info) {
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+ // Should call setSelection(index, false) for the spinner before setOnItemSelectedListener()
+ // to avoid extra onItemSelected() get called when first register the listener.
+ final int initialPosition =
+ mInputRoutingController.getUserPreferredInputRoutingValue();
+ mInputRoutingSpinner.setSelection(initialPosition, false);
+ mInputRoutingAdapter.setSelected(initialPosition);
+ mInputRoutingSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mInputRoutingAdapter.setSelected(position);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_INPUT_ROUTING_SELECT,
+ mLaunchSourceId);
+ mInputRoutingController.selectInputRouting(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Do nothing
+ }
+ });
+
+ mInputRoutingLayout = dialog.requireViewById(R.id.input_routing_layout);
+ mInputRoutingController.isInputRoutingControlAvailable(
+ available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+ available ? VISIBLE : GONE)));
+ }
+
private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) {
final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout);
ambientLayout.setUiEventLogger(mUiEventLogger, mLaunchSourceId);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
new file mode 100644
index 0000000..e8fa7ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid
+
+import android.content.Context
+import android.media.AudioManager
+import android.util.Log
+import androidx.collection.ArraySet
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HapClientProfile
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * The controller of the hearing device input routing.
+ *
+ * <p> It manages and update the input routing according to the value.
+ */
+open class HearingDevicesInputRoutingController
+@AssistedInject
+constructor(
+ @Assisted context: Context,
+ private val audioManager: AudioManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+ private val audioRoutingHelper = HearingAidAudioRoutingHelper(context)
+ private var cachedDevice: CachedBluetoothDevice? = null
+ private val bgCoroutineScope = CoroutineScope(backgroundDispatcher)
+
+ /** Factory to create a [HearingDevicesInputRoutingController] instance. */
+ @AssistedFactory
+ interface Factory {
+ fun create(context: Context): HearingDevicesInputRoutingController
+ }
+
+ /** Possible input routing UI. Need to align with [getInputRoutingOptions] */
+ enum class InputRoutingValue {
+ HEARING_DEVICE,
+ BUILTIN_MIC,
+ }
+
+ companion object {
+ private const val TAG = "HearingDevicesInputRoutingController"
+
+ /** Gets input routing options as strings. */
+ @JvmStatic
+ fun getInputRoutingOptions(context: Context): Array<String> {
+ return context.resources.getStringArray(R.array.hearing_device_input_routing_options)
+ }
+ }
+
+ fun interface InputRoutingControlAvailableCallback {
+ fun onResult(available: Boolean)
+ }
+
+ /**
+ * Sets the device for this controller to control the input routing.
+ *
+ * @param device the [CachedBluetoothDevice] set to the controller
+ */
+ fun setDevice(device: CachedBluetoothDevice?) {
+ this@HearingDevicesInputRoutingController.cachedDevice = device
+ }
+
+ fun isInputRoutingControlAvailable(callback: InputRoutingControlAvailableCallback) {
+ bgCoroutineScope.launch {
+ val result = isInputRoutingControlAvailableInternal()
+ callback.onResult(result)
+ }
+ }
+
+ /**
+ * Checks if input routing control is available for the currently set device.
+ *
+ * @return `true` if input routing control is available.
+ */
+ private suspend fun isInputRoutingControlAvailableInternal(): Boolean {
+ val device = cachedDevice ?: return false
+
+ val memberDevices = device.memberDevice
+
+ val inputInfos =
+ withContext(backgroundDispatcher) {
+ audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)
+ }
+ val supportedInputDeviceAddresses = ArraySet<String>()
+ supportedInputDeviceAddresses.add(device.address)
+ if (memberDevices.isNotEmpty()) {
+ memberDevices.forEach { supportedInputDeviceAddresses.add(it.address) }
+ }
+
+ val isValidInputDevice =
+ inputInfos.any { supportedInputDeviceAddresses.contains(it.address) }
+ // Not support ASHA hearing device for input routing feature
+ val isHapHearingDevice = device.profiles.any { profile -> profile is HapClientProfile }
+
+ if (isHapHearingDevice && !isValidInputDevice) {
+ Log.d(TAG, "Not supported input type hearing device.")
+ }
+ return isHapHearingDevice && isValidInputDevice
+ }
+
+ /** Gets the user's preferred [InputRoutingValue]. */
+ fun getUserPreferredInputRoutingValue(): Int {
+ val device = cachedDevice ?: return InputRoutingValue.HEARING_DEVICE.ordinal
+
+ return if (device.device.isMicrophonePreferredForCalls) {
+ InputRoutingValue.HEARING_DEVICE.ordinal
+ } else {
+ InputRoutingValue.BUILTIN_MIC.ordinal
+ }
+ }
+
+ /**
+ * Sets the input routing to [android.bluetooth.BluetoothDevice.setMicrophonePreferredForCalls]
+ * based on the input routing index.
+ *
+ * @param inputRoutingIndex The desired input routing index.
+ */
+ fun selectInputRouting(inputRoutingIndex: Int) {
+ val device = cachedDevice ?: return
+
+ val useBuiltinMic = (inputRoutingIndex == InputRoutingValue.BUILTIN_MIC.ordinal)
+ val status =
+ audioRoutingHelper.setPreferredInputDeviceForCalls(
+ device,
+ if (useBuiltinMic) HearingAidAudioRoutingConstants.RoutingValue.BUILTIN_DEVICE
+ else HearingAidAudioRoutingConstants.RoutingValue.AUTO,
+ )
+ if (!status) {
+ Log.d(TAG, "Fail to configure setPreferredInputDeviceForCalls")
+ }
+ setMicrophonePreferredForCallsForDeviceSet(device, !useBuiltinMic)
+ }
+
+ private fun setMicrophonePreferredForCallsForDeviceSet(
+ device: CachedBluetoothDevice?,
+ enabled: Boolean,
+ ) {
+ device ?: return
+ device.device.isMicrophonePreferredForCalls = enabled
+ val memberDevices = device.memberDevice
+ if (memberDevices.isNotEmpty()) {
+ memberDevices.forEach { d -> d.device.isMicrophonePreferredForCalls = enabled }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
index 4a695d6..82ac10d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -40,6 +40,8 @@
HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS(2153),
@UiEvent(doc = "Collapse the ambient volume controls")
HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS(2154),
+ @UiEvent(doc = "Select a input routing option from input routing spinner")
+ HEARING_DEVICES_INPUT_ROUTING_SELECT(2155),
@UiEvent(doc = "Click on the device settings to enter hearing devices page")
HEARING_DEVICES_SETTINGS_CLICK(2172);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
index df6c1b1..8cebe04 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
@@ -24,11 +24,17 @@
import android.net.Uri
import android.text.TextUtils
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
+import java.util.function.Consumer
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
@SysUISingleton
-class ActionIntentCreator @Inject constructor() : IntentCreator {
+class ActionIntentCreator
+@Inject
+constructor(@Application private val applicationScope: CoroutineScope) : IntentCreator {
override fun getTextEditorIntent(context: Context?) =
Intent(context, EditTextActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
@@ -65,7 +71,7 @@
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
- override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
+ suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent {
val editorPackage = context.getString(R.string.config_screenshotEditor)
return Intent(Intent.ACTION_EDIT).apply {
if (!TextUtils.isEmpty(editorPackage)) {
@@ -78,6 +84,14 @@
}
}
+ override fun getImageEditIntentAsync(
+ uri: Uri?,
+ context: Context,
+ outputConsumer: Consumer<Intent>,
+ ) {
+ applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri, context)) }
+ }
+
override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
return Intent(REMOTE_COPY_ACTION).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 314b6e7..984d247 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -558,8 +558,10 @@
private void editImage(Uri uri) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
- animateOut();
+ mIntentCreator.getImageEditIntentAsync(uri, mContext, intent -> {
+ mContext.startActivity(intent);
+ animateOut();
+ });
}
private void editText() {
@@ -747,8 +749,10 @@
mIntentCreator.getTextEditorIntent(mContext));
break;
case IMAGE:
- finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
- mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
+ mIntentCreator.getImageEditIntentAsync(mClipboardModel.getUri(), mContext,
+ intent -> {
+ finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, intent);
+ });
break;
default:
Log.w(TAG, "Got preview tapped callback for non-editable type "
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
index 4b24536..e9a9cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
@@ -27,6 +27,8 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
@SysUISingleton
@@ -73,7 +75,7 @@
return chooserIntent;
}
- public Intent getImageEditIntent(Uri uri, Context context) {
+ public void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer) {
String editorPackage = context.getString(R.string.config_screenshotEditor);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
if (!TextUtils.isEmpty(editorPackage)) {
@@ -83,7 +85,7 @@
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
- return editIntent;
+ outputConsumer.accept(editIntent);
}
public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index c8a6b05..283596f 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -21,9 +21,11 @@
import android.content.Intent;
import android.net.Uri;
+import java.util.function.Consumer;
+
public interface IntentCreator {
Intent getTextEditorIntent(Context context);
Intent getShareIntent(ClipData clipData, Context context);
- Intent getImageEditIntent(Uri uri, Context context);
+ void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer);
Intent getRemoteCopyIntent(ClipData clipData, Context context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
index 9db7b50..1301fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
@@ -17,9 +17,9 @@
package com.android.systemui.common.domain.interactor
import android.util.Log
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.model.StateChange
import com.android.systemui.model.SysUiState
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 06a14ea..440c300 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.widgets
+import android.appwidget.AppWidgetProviderInfo
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -101,6 +102,7 @@
val (_, isActive) = withPrev
// The validation is performed once the hub becomes active.
if (isActive) {
+ removeNotLockscreenWidgets(widgets)
validateWidgetsAndDeleteOrphaned(widgets)
}
}
@@ -144,6 +146,19 @@
}
}
+ private fun removeNotLockscreenWidgets(widgets: List<CommunalWidgetContentModel>) {
+ widgets
+ .filter { widget ->
+ when (widget) {
+ is CommunalWidgetContentModel.Available ->
+ widget.providerInfo.widgetCategory and
+ AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD != 0
+ else -> false
+ }
+ }
+ .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) }
+ }
+
/**
* Ensure the existence of all associated users for widgets, and remove widgets belonging to
* users who have been deleted.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
index 39708a7..3520439 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
@@ -16,9 +16,9 @@
package com.android.systemui.dagger
-import com.android.systemui.display.data.repository.DefaultDisplayOnlyInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
+import com.android.app.displaylib.DefaultDisplayOnlyInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.model.SysUIStateInstanceProvider
import com.android.systemui.model.SysUiState
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f08126a..854c610 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -65,7 +65,7 @@
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.deviceentry.DeviceEntryModule;
import com.android.systemui.display.DisplayModule;
-import com.android.systemui.display.data.repository.PerDisplayRepository;
+import com.android.app.displaylib.PerDisplayRepository;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dreams.dagger.DreamModule;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 9b181be..908d0aa 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -16,8 +16,15 @@
package com.android.systemui.display
+import android.hardware.display.DisplayManager
+import android.os.Handler
+import com.android.app.displaylib.DisplayLibBackground
+import com.android.app.displaylib.DisplayLibComponent
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.displaylib.createDisplayLibComponent
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
import com.android.systemui.display.data.repository.DisplayRepository
@@ -28,6 +35,7 @@
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
import com.android.systemui.display.data.repository.FocusedDisplayRepository
import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
+import com.android.systemui.display.data.repository.PerDisplayRepoDumpHelper
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule
@@ -40,9 +48,11 @@
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
/** Module binding display related classes. */
-@Module(includes = [DisplayWindowPropertiesInteractorModule::class])
+@Module(includes = [DisplayWindowPropertiesInteractorModule::class, DisplayLibModule::class])
interface DisplayModule {
@Binds
fun bindConnectedDisplayInteractor(
@@ -73,6 +83,13 @@
impl: DisplayWindowPropertiesRepositoryImpl
): DisplayWindowPropertiesRepository
+ @Binds
+ fun dumpRegistrationLambda(helper: PerDisplayRepoDumpHelper): PerDisplayRepository.InitCallback
+
+ @Binds
+ @DisplayLibBackground
+ fun bindDisplayLibBackground(@Background bgScope: CoroutineScope): CoroutineScope
+
companion object {
@Provides
@SysUISingleton
@@ -103,3 +120,31 @@
}
}
}
+
+/** Module to bind the DisplayRepository from displaylib to the systemui dagger graph. */
+@Module
+object DisplayLibModule {
+ @Provides
+ @SysUISingleton
+ fun displayLibComponent(
+ displayManager: DisplayManager,
+ @Background backgroundHandler: Handler,
+ @Background bgApplicationScope: CoroutineScope,
+ @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
+ ): DisplayLibComponent {
+ return createDisplayLibComponent(
+ displayManager,
+ backgroundHandler,
+ bgApplicationScope,
+ backgroundCoroutineDispatcher,
+ )
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesDisplayRepositoryFromLib(
+ displayLibComponent: DisplayLibComponent
+ ): com.android.app.displaylib.DisplayRepository {
+ return displayLibComponent.displayRepository
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt b/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
deleted file mode 100644
index 626a68f..0000000
--- a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
+++ /dev/null
@@ -1,24 +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.display.data
-
-sealed interface DisplayEvent {
- val displayId: Int
- data class Added(override val displayId: Int) : DisplayEvent
- data class Removed(override val displayId: Int) : DisplayEvent
- data class Changed(override val displayId: Int) : DisplayEvent
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 721d116..051fe7e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,112 +17,30 @@
package com.android.systemui.display.data.repository
import android.annotation.SuppressLint
-import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
-import android.hardware.display.DisplayManager.DisplayListener
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED
-import android.os.Handler
-import android.util.Log
-import android.view.Display
import android.view.IWindowManager
-import com.android.app.tracing.FlowTracing.traceEach
-import com.android.app.tracing.traceSection
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.display.data.DisplayEvent
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.Compile
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.flatMapLatest
-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.scan
import kotlinx.coroutines.flow.stateIn
/** Repository for providing access to display related information and events. */
-interface DisplayRepository {
- /** Display change event indicating a change to the given displayId has occurred. */
- val displayChangeEvent: Flow<Int>
-
- /** Display addition event indicating a new display has been added. */
- val displayAdditionEvent: Flow<Display?>
-
- /** Display removal event indicating a display has been removed. */
- val displayRemovalEvent: Flow<Int>
+interface DisplayRepository : DisplayRepositoryFromLib {
/** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
-
- /**
- * Provides the current set of displays.
- *
- * Consider using [displayIds] if only the [Display.getDisplayId] is needed.
- */
- val displays: StateFlow<Set<Display>>
-
- /**
- * Provides the current set of display ids.
- *
- * Note that it is preferred to use this instead of [displays] if only the
- * [Display.getDisplayId] is needed.
- */
- val displayIds: StateFlow<Set<Int>>
-
- /**
- * Pending display id that can be enabled/disabled.
- *
- * When `null`, it means there is no pending display waiting to be enabled.
- */
- val pendingDisplay: Flow<PendingDisplay?>
-
- /** Whether the default display is currently off. */
- val defaultDisplayOff: Flow<Boolean>
-
- /**
- * Given a display ID int, return the corresponding Display object, or null if none exist.
- *
- * This method is guaranteed to not result in any binder call.
- */
- fun getDisplay(displayId: Int): Display? =
- displays.value.firstOrNull { it.displayId == displayId }
-
- /** Represents a connected display that has not been enabled yet. */
- interface PendingDisplay {
- /** Id of the pending display. */
- val id: Int
-
- /** Enables the display, making it available to the system. */
- suspend fun enable()
-
- /**
- * Ignores the pending display. When called, this specific display id doesn't appear as
- * pending anymore until the display is disconnected and reconnected again.
- */
- suspend fun ignore()
-
- /** Disables the display, making it unavailable to the system. */
- suspend fun disable()
- }
}
@SysUISingleton
@@ -130,310 +48,11 @@
class DisplayRepositoryImpl
@Inject
constructor(
- private val displayManager: DisplayManager,
private val commandQueue: CommandQueue,
private val windowManager: IWindowManager,
- @Background backgroundHandler: Handler,
@Background bgApplicationScope: CoroutineScope,
- @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
-) : DisplayRepository {
- private val allDisplayEvents: Flow<DisplayEvent> =
- conflatedCallbackFlow {
- val callback =
- object : DisplayListener {
- override fun onDisplayAdded(displayId: Int) {
- trySend(DisplayEvent.Added(displayId))
- }
-
- override fun onDisplayRemoved(displayId: Int) {
- trySend(DisplayEvent.Removed(displayId))
- }
-
- override fun onDisplayChanged(displayId: Int) {
- trySend(DisplayEvent.Changed(displayId))
- }
- }
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- EVENT_TYPE_DISPLAY_ADDED or
- EVENT_TYPE_DISPLAY_CHANGED or
- EVENT_TYPE_DISPLAY_REMOVED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
- .onStart { emit(DisplayEvent.Changed(Display.DEFAULT_DISPLAY)) }
- .debugLog("allDisplayEvents")
- .flowOn(backgroundCoroutineDispatcher)
-
- override val displayChangeEvent: Flow<Int> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
-
- override val displayRemovalEvent: Flow<Int> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId }
-
- // This is necessary because there might be multiple displays, and we could
- // have missed events for those added before this process or flow started.
- // Note it causes a binder call from the main thread (it's traced).
- private val initialDisplays: Set<Display> =
- traceSection("$TAG#initialDisplays") { displayManager.displays?.toSet() ?: emptySet() }
- private val initialDisplayIds = initialDisplays.map { display -> display.displayId }.toSet()
-
- /** Propagate to the listeners only enabled displays */
- private val enabledDisplayIds: StateFlow<Set<Int>> =
- allDisplayEvents
- .scan(initial = initialDisplayIds) { previousIds: Set<Int>, event: DisplayEvent ->
- val id = event.displayId
- when (event) {
- is DisplayEvent.Removed -> previousIds - id
- is DisplayEvent.Added,
- is DisplayEvent.Changed -> previousIds + id
- }
- }
- .distinctUntilChanged()
- .debugLog("enabledDisplayIds")
- .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
-
- private val defaultDisplay by lazy {
- getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
- ?: error("Unable to get default display.")
- }
-
- /**
- * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
- *
- * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
- */
- private val enabledDisplays: StateFlow<Set<Display>> =
- enabledDisplayIds
- .mapElementsLazily { displayId -> getDisplayFromDisplayManager(displayId) }
- .onEach {
- if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
- }
- .flowOn(backgroundCoroutineDispatcher)
- .debugLog("enabledDisplays")
- .stateIn(
- bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- // This triggers a single binder call on the UI thread per process. The
- // alternative would be to use sharedFlows, but they are prohibited due to
- // performance concerns.
- // Ultimately, this is a trade-off between a one-time UI thread binder call and
- // the constant overhead of sharedFlows.
- initialValue = initialDisplays,
- )
-
- /**
- * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
- *
- * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
- */
- override val displays: StateFlow<Set<Display>> = enabledDisplays
-
- override val displayIds: StateFlow<Set<Int>> = enabledDisplayIds
-
- /**
- * Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
- * 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
- * calling [getDisplay] for the newly added display will be non-null.
- * 2. Reuse the existing instance of [Display] without a new call to [DisplayManager].
- */
- override val displayAdditionEvent: Flow<Display?> =
- displays
- .pairwiseBy { previousDisplays, currentDisplays -> currentDisplays - previousDisplays }
- .flatMapLatest { it.asFlow() }
-
- val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
- private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
-
- private fun getInitialConnectedDisplays(): Set<Int> =
- traceSection("$TAG#getInitialConnectedDisplays") {
- displayManager
- .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
- .map { it.displayId }
- .toSet()
- .also {
- if (DEBUG) {
- Log.d(TAG, "getInitialConnectedDisplays: $it")
- }
- }
- }
-
- /* keeps connected displays until they are disconnected. */
- private val connectedDisplayIds: StateFlow<Set<Int>> =
- conflatedCallbackFlow {
- val connectedIds = getInitialConnectedDisplays().toMutableSet()
- val callback =
- object : DisplayConnectionListener {
- override fun onDisplayConnected(id: Int) {
- if (DEBUG) {
- Log.d(TAG, "display with id=$id connected.")
- }
- connectedIds += id
- _ignoredDisplayIds.value -= id
- trySend(connectedIds.toSet())
- }
-
- override fun onDisplayDisconnected(id: Int) {
- connectedIds -= id
- if (DEBUG) {
- Log.d(TAG, "display with id=$id disconnected.")
- }
- _ignoredDisplayIds.value -= id
- trySend(connectedIds.toSet())
- }
- }
- trySend(connectedIds.toSet())
- displayManager.registerDisplayListener(
- callback,
- backgroundHandler,
- /* eventFlags */ 0,
- DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED,
- )
- awaitClose { displayManager.unregisterDisplayListener(callback) }
- }
- .distinctUntilChanged()
- .debugLog("connectedDisplayIds")
- .stateIn(
- bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- // The initial value is set to empty, but connected displays are gathered as soon as
- // the flow starts being collected. This is to ensure the call to get displays (an
- // IPC) happens in the background instead of when this object
- // is instantiated.
- initialValue = emptySet(),
- )
-
- private val connectedExternalDisplayIds: Flow<Set<Int>> =
- connectedDisplayIds
- .map { connectedDisplayIds ->
- traceSection("$TAG#filteringExternalDisplays") {
- connectedDisplayIds
- .filter { id -> getDisplayType(id) == Display.TYPE_EXTERNAL }
- .toSet()
- }
- }
- .flowOn(backgroundCoroutineDispatcher)
- .debugLog("connectedExternalDisplayIds")
-
- private fun getDisplayType(displayId: Int): Int? =
- traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
-
- private fun getDisplayFromDisplayManager(displayId: Int): Display? =
- traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
-
- /**
- * Pending displays are the ones connected, but not enabled and not ignored.
- *
- * A connected display is ignored after the user makes the decision to use it or not. For now,
- * the initial decision from the user is final and not reversible.
- */
- private val pendingDisplayIds: Flow<Set<Int>> =
- combine(enabledDisplayIds, connectedExternalDisplayIds, ignoredDisplayIds) {
- enabledDisplaysIds,
- connectedExternalDisplayIds,
- ignoredDisplayIds ->
- if (DEBUG) {
- Log.d(
- TAG,
- "combining enabled=$enabledDisplaysIds, " +
- "connectedExternalDisplayIds=$connectedExternalDisplayIds, " +
- "ignored=$ignoredDisplayIds",
- )
- }
- connectedExternalDisplayIds - enabledDisplaysIds - ignoredDisplayIds
- }
- .debugLog("allPendingDisplayIds")
-
- /** Which display id should be enabled among the pending ones. */
- private val pendingDisplayId: Flow<Int?> =
- pendingDisplayIds.map { it.maxOrNull() }.distinctUntilChanged().debugLog("pendingDisplayId")
-
- override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
- pendingDisplayId
- .map { displayId ->
- val id = displayId ?: return@map null
- object : DisplayRepository.PendingDisplay {
- override val id = id
-
- override suspend fun enable() {
- traceSection("DisplayRepository#enable($id)") {
- if (DEBUG) {
- Log.d(TAG, "Enabling display with id=$id")
- }
- displayManager.enableConnectedDisplay(id)
- }
- // After the display has been enabled, it is automatically ignored.
- ignore()
- }
-
- override suspend fun ignore() {
- traceSection("DisplayRepository#ignore($id)") {
- _ignoredDisplayIds.value += id
- }
- }
-
- override suspend fun disable() {
- ignore()
- traceSection("DisplayRepository#disable($id)") {
- if (DEBUG) {
- Log.d(TAG, "Disabling display with id=$id")
- }
- displayManager.disableConnectedDisplay(id)
- }
- }
- }
- }
- .debugLog("pendingDisplay")
-
- override val defaultDisplayOff: Flow<Boolean> =
- displayChangeEvent
- .filter { it == Display.DEFAULT_DISPLAY }
- .map { defaultDisplay.state == Display.STATE_OFF }
- .distinctUntilChanged()
-
- private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
- return if (DEBUG) {
- traceEach(flowName, logcat = true, traceEmissionCount = true)
- } else {
- this
- }
- }
-
- /**
- * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into
- * account the diff between each root flow emission.
- *
- * This is needed to minimize the number of [getDisplayFromDisplayManager] in this class. Note
- * that if the [createValue] returns a null element, it will not be added in the output set.
- */
- private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
- data class State<T, V>(
- val previousSet: Set<T>,
- // Caches T values from the previousSet that were already converted to V
- val valueMap: Map<T, V>,
- val resultSet: Set<V>,
- )
-
- val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
- return this.scan(emptyInitialState) { state, currentSet ->
- if (currentSet == state.previousSet) {
- state
- } else {
- val removed = state.previousSet - currentSet
- val added = currentSet - state.previousSet
- val newMap = state.valueMap.toMutableMap()
-
- added.forEach { key -> createValue(key)?.let { newMap[key] = it } }
- removed.forEach { key -> newMap.remove(key) }
-
- val resultSet = newMap.values.toSet()
- State(currentSet, newMap, resultSet)
- }
- }
- .filter { it != emptyInitialState }
- .map { it.resultSet }
- }
+ private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository,
+) : DisplayRepositoryFromLib by displayRepositoryFromLib, DisplayRepository {
private val decorationEvents: Flow<Event> = callbackFlow {
val callback =
@@ -487,20 +106,5 @@
private companion object {
const val TAG = "DisplayRepository"
- val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
}
}
-
-/** Used to provide default implementations for all methods. */
-private interface DisplayConnectionListener : DisplayListener {
-
- override fun onDisplayConnected(id: Int) {}
-
- override fun onDisplayDisconnected(id: Int) {}
-
- override fun onDisplayAdded(id: Int) {}
-
- override fun onDisplayRemoved(id: Int) {}
-
- override fun onDisplayChanged(id: Int) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
index a56710e..86c9d84c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
@@ -16,6 +16,9 @@
package com.android.systemui.display.data.repository
+import com.android.app.displaylib.PerDisplayRepository
+
+// TODO b/401305290 - move to displaylib
class FakePerDisplayRepository<T> : PerDisplayRepository<T> {
private val instances = mutableMapOf<Int, T>()
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
new file mode 100644
index 0000000..efbae5d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.display.data.repository
+
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpableFromToString
+import javax.inject.Inject
+
+/** Helper class to register PerDisplayRepository in the dump manager in SystemUI. */
+@SysUISingleton
+class PerDisplayRepoDumpHelper @Inject constructor(private val dumpManager: DumpManager) :
+ PerDisplayRepository.InitCallback {
+ /**
+ * Registers PerDisplayRepository in the dump manager.
+ *
+ * The repository will be identified by the given debug name.
+ */
+ override fun onInit(debugName: String, instance: Any) {
+ dumpManager.registerNormalDumpable(
+ "PerDisplayRepository-$debugName",
+ DumpableFromToString(instance),
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
deleted file mode 100644
index d1d0135..0000000
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.display.data.repository
-
-import android.util.Log
-import android.view.Display
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.tracing.traceSection
-import com.android.systemui.Dumpable
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dump.DumpManager
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import java.io.PrintWriter
-import java.util.concurrent.ConcurrentHashMap
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Used to create instances of type `T` for a specific display.
- *
- * This is useful for resources or objects that need to be managed independently for each connected
- * display (e.g., UI state, rendering contexts, or display-specific configurations).
- *
- * Note that in most cases this can be implemented by a simple `@AssistedFactory` with `displayId`
- * parameter
- *
- * ```kotlin
- * class SomeType @AssistedInject constructor(@Assisted displayId: Int,..)
- * @AssistedFactory
- * interface Factory {
- * fun create(displayId: Int): SomeType
- * }
- * }
- * ```
- *
- * Then it can be used to create a [PerDisplayRepository] as follows:
- * ```kotlin
- * // Injected:
- * val repositoryFactory: PerDisplayRepositoryImpl.Factory
- * val instanceFactory: PerDisplayRepositoryImpl.Factory
- * // repository creation:
- * repositoryFactory.create(instanceFactory::create)
- * ```
- *
- * @see PerDisplayRepository For how to retrieve and manage instances created by this factory.
- */
-fun interface PerDisplayInstanceProvider<T> {
- /** Creates an instance for a display. */
- fun createInstance(displayId: Int): T?
-}
-
-/**
- * Extends [PerDisplayInstanceProvider], adding support for destroying the instance.
- *
- * This is useful for releasing resources associated with a display when it is disconnected or when
- * the per-display instance is no longer needed.
- */
-interface PerDisplayInstanceProviderWithTeardown<T> : PerDisplayInstanceProvider<T> {
- /** Destroys a previously created instance of `T` forever. */
- fun destroyInstance(instance: T)
-}
-
-/**
- * Provides access to per-display instances of type `T`.
- *
- * Acts as a repository, managing the caching and retrieval of instances created by a
- * [PerDisplayInstanceProvider]. It ensures that only one instance of `T` exists per display ID.
- */
-interface PerDisplayRepository<T> {
- /** Gets the cached instance or create a new one for a given display. */
- operator fun get(displayId: Int): T?
-
- /** Debug name for this repository, mainly for tracing and logging. */
- val debugName: String
-}
-
-/**
- * Default implementation of [PerDisplayRepository].
- *
- * This class manages a cache of per-display instances of type `T`, creating them using a provided
- * [PerDisplayInstanceProvider] and optionally tearing them down using a
- * [PerDisplayInstanceProviderWithTeardown] when displays are disconnected.
- *
- * It listens to the [DisplayRepository] to detect when displays are added or removed, and
- * automatically manages the lifecycle of the per-display instances.
- *
- * Note that this is a [PerDisplayStoreImpl] 2.0 that doesn't require [CoreStartable] bindings,
- * providing all args in the constructor.
- */
-class PerDisplayInstanceRepositoryImpl<T>
-@AssistedInject
-constructor(
- @Assisted override val debugName: String,
- @Assisted private val instanceProvider: PerDisplayInstanceProvider<T>,
- @Background private val backgroundApplicationScope: CoroutineScope,
- private val displayRepository: DisplayRepository,
- private val dumpManager: DumpManager,
-) : PerDisplayRepository<T>, Dumpable {
-
- private val perDisplayInstances = ConcurrentHashMap<Int, T?>()
-
- init {
- backgroundApplicationScope.launch("$debugName#start") { start() }
- }
-
- private suspend fun start() {
- dumpManager.registerNormalDumpable("PerDisplayRepository-${debugName}", this)
- displayRepository.displayIds.collectLatest { displayIds ->
- val toRemove = perDisplayInstances.keys - displayIds
- toRemove.forEach { displayId ->
- Log.d(TAG, "<$debugName> destroying instance for displayId=$displayId.")
- perDisplayInstances.remove(displayId)?.let { instance ->
- (instanceProvider as? PerDisplayInstanceProviderWithTeardown)?.destroyInstance(
- instance
- )
- }
- }
- }
- }
-
- override fun get(displayId: Int): T? {
- if (displayRepository.getDisplay(displayId) == null) {
- Log.e(TAG, "<$debugName: Display with id $displayId doesn't exist.")
- return null
- }
-
- // If it doesn't exist, create it and put it in the map.
- return perDisplayInstances.computeIfAbsent(displayId) { key ->
- Log.d(TAG, "<$debugName> creating instance for displayId=$key, as it wasn't available.")
- val instance =
- traceSection({ "creating instance of $debugName for displayId=$key" }) {
- instanceProvider.createInstance(key)
- }
- if (instance == null) {
- Log.e(
- TAG,
- "<$debugName> returning null because createInstance($key) returned null.",
- )
- }
- instance
- }
- }
-
- @AssistedFactory
- interface Factory<T> {
- fun create(
- debugName: String,
- instanceProvider: PerDisplayInstanceProvider<T>,
- ): PerDisplayInstanceRepositoryImpl<T>
- }
-
- companion object {
- private const val TAG = "PerDisplayInstanceRepo"
- }
-
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println(perDisplayInstances)
- }
-}
-
-/**
- * Provides an instance of a given class **only** for the default display, even if asked for another
- * display.
- *
- * This is useful in case of **flag refactors**: it can be provided instead of an instance of
- * [PerDisplayInstanceRepositoryImpl] when a flag related to multi display refactoring is off.
- *
- * Note that this still requires all instances to be provided by a [PerDisplayInstanceProvider]. If
- * you want to provide an existing instance instead for the default display, either implement it in
- * a custom [PerDisplayInstanceProvider] (e.g. inject it in the constructor and return it if the
- * displayId is zero), or use [SingleInstanceRepositoryImpl].
- */
-class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
- override val debugName: String,
- private val instanceProvider: PerDisplayInstanceProvider<T>,
-) : PerDisplayRepository<T> {
- private val lazyDefaultDisplayInstance by lazy {
- instanceProvider.createInstance(Display.DEFAULT_DISPLAY)
- }
- override fun get(displayId: Int): T? = lazyDefaultDisplayInstance
-}
-
-/**
- * Always returns [instance] for any display.
- *
- * This can be used to provide a single instance based on a flag value during a refactor. Similar to
- * [DefaultDisplayOnlyInstanceRepositoryImpl], but also avoids creating the
- * [PerDisplayInstanceProvider]. This is useful when you want to provide an existing instance only,
- * without even instantiating a [PerDisplayInstanceProvider].
- */
-class SingleInstanceRepositoryImpl<T>(override val debugName: String, private val instance: T) :
- PerDisplayRepository<T> {
- override fun get(displayId: Int): T? = instance
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index 15a3cbd..84f103e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -18,6 +18,7 @@
import android.companion.virtual.VirtualDeviceManager
import android.view.Display
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -138,7 +139,8 @@
.distinctUntilChanged()
.flowOn(backgroundCoroutineDispatcher)
- private fun DisplayRepository.PendingDisplay.toInteractorPendingDisplay(): PendingDisplay =
+ private fun DisplayRepositoryFromLib.PendingDisplay.toInteractorPendingDisplay():
+ PendingDisplay =
object : PendingDisplay {
override suspend fun enable() = this@toInteractorPendingDisplay.enable()
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
new file mode 100644
index 0000000..438931a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dump
+
+import com.android.systemui.Dumpable
+import java.io.PrintWriter
+
+/** Dumpable implementation that just calls toString() on the instance. */
+class DumpableFromToString<T>(private val instance: T) : Dumpable {
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("$instance")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index cf5c340..f2a10cc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -147,14 +147,14 @@
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
+import dagger.Lazy;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
* on whether the keyguard is showing, and whether the device is provisioned.
@@ -270,6 +270,16 @@
private final UserLogoutInteractor mLogoutInteractor;
private final GlobalActionsInteractor mInteractor;
private final Lazy<DisplayWindowPropertiesRepository> mDisplayWindowPropertiesRepositoryLazy;
+ private final Handler mHandler;
+
+ private final UserTracker.Callback mOnUserSwitched = new UserTracker.Callback() {
+ @Override
+ public void onBeforeUserSwitching(int newUser) {
+ // Dismiss the dialog as soon as we start switching. This will schedule a message
+ // in a handler so it will be pretty quick.
+ dismissDialog();
+ }
+ };
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -425,6 +435,29 @@
mInteractor = interactor;
mDisplayWindowPropertiesRepositoryLazy = displayWindowPropertiesRepository;
+ mHandler = new Handler(mMainHandler.getLooper()) {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_DISMISS:
+ if (mDialog != null) {
+ if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
+ // Hide instantly.
+ mDialog.hide();
+ mDialog.dismiss();
+ } else {
+ mDialog.dismiss();
+ }
+ mDialog = null;
+ }
+ break;
+ case MESSAGE_REFRESH:
+ refreshSilentMode();
+ mAdapter.notifyDataSetChanged();
+ break;
+ }
+ }
+ };
+
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -537,6 +570,7 @@
expandable != null ? expandable.dialogTransitionController(
new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
INTERACTION_JANK_TAG)) : null;
+ mUserTracker.addCallback(mOnUserSwitched, mBackgroundExecutor);
if (controller != null) {
mDialogTransitionAnimator.show(mDialog, controller);
} else {
@@ -1404,6 +1438,7 @@
mWindowManagerFuncs.onGlobalActionsHidden();
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
mInteractor.onDismissed();
+ mUserTracker.removeCallback(mOnUserSwitched);
}
/**
@@ -2228,29 +2263,6 @@
mDialogPressDelay = 0; // ms
}
- private Handler mHandler = new Handler() {
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_DISMISS:
- if (mDialog != null) {
- if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
- // Hide instantly.
- mDialog.hide();
- mDialog.dismiss();
- } else {
- mDialog.dismiss();
- }
- mDialog = null;
- }
- break;
- case MESSAGE_REFRESH:
- refreshSilentMode();
- mAdapter.notifyDataSetChanged();
- break;
- }
- }
- };
-
private void onAirplaneModeChanged() {
// Let the service state callbacks handle the state.
if (mHasTelephony || mAirplaneModeOn == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 7574649..7968508 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -673,7 +673,8 @@
if (SceneContainerFlag.isEnabled()) {
mDeviceEntryInteractorLazy.get().lockNow("doKeyguardTimeout");
} else if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardServiceShowLockscreenInteractor.onKeyguardServiceDoKeyguardTimeout();
+ mKeyguardServiceShowLockscreenInteractor
+ .onKeyguardServiceDoKeyguardTimeout(options);
}
mKeyguardViewMediator.doKeyguardTimeout(options);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 5869274..51b953e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -22,11 +22,14 @@
import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationTarget
import android.view.WindowManager
+import com.android.internal.widget.LockPatternUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.window.flags.Flags
import com.android.wm.shell.keyguard.KeyguardTransitions
import java.util.concurrent.Executor
@@ -46,6 +49,9 @@
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
private val keyguardTransitions: KeyguardTransitions,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val lockPatternUtils: LockPatternUtils,
+ private val keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor,
) {
/**
@@ -92,12 +98,23 @@
* second timeout).
*/
private var isKeyguardGoingAway = false
- private set(value) {
+ private set(goingAway) {
// TODO(b/278086361): Extricate the keyguard state controller.
- keyguardStateController.notifyKeyguardGoingAway(value)
- field = value
+ keyguardStateController.notifyKeyguardGoingAway(goingAway)
+
+ if (goingAway) {
+ keyguardGoingAwayRequestedForUserId = selectedUserInteractor.getSelectedUserId()
+ }
+
+ field = goingAway
}
+ /**
+ * The current user ID when we asked WM to start the keyguard going away animation. This is used
+ * for validation when user switching occurs during unlock.
+ */
+ private var keyguardGoingAwayRequestedForUserId: Int = -1
+
/** Callback provided by WM to call once we're done with the going away animation. */
private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
@@ -171,6 +188,14 @@
nonApps: Array<RemoteAnimationTarget>,
finishedCallback: IRemoteAnimationFinishedCallback,
) {
+ goingAwayRemoteAnimationFinishedCallback = finishedCallback
+
+ if (maybeStartTransitionIfUserSwitchedDuringGoingAway()) {
+ Log.d(TAG, "User switched during keyguard going away - ending remote animation.")
+ endKeyguardGoingAwayAnimation()
+ return
+ }
+
// If we weren't expecting the keyguard to be going away, WM triggered this transition.
if (!isKeyguardGoingAway) {
// Since WM triggered this, we're likely not transitioning to GONE yet. See if we can
@@ -198,7 +223,6 @@
}
if (apps.isNotEmpty()) {
- goingAwayRemoteAnimationFinishedCallback = finishedCallback
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
} else {
// Nothing to do here if we have no apps, end the animation, which will cancel it and WM
@@ -211,6 +235,7 @@
// If WM cancelled the animation, we need to end immediately even if we're still using the
// animation.
endKeyguardGoingAwayAnimation()
+ maybeStartTransitionIfUserSwitchedDuringGoingAway()
}
/**
@@ -301,6 +326,29 @@
}
}
+ /**
+ * If necessary, start a transition to show/hide keyguard in response to a user switch during
+ * keyguard going away.
+ *
+ * Returns [true] if a transition was started, or false if a transition was not necessary.
+ */
+ private fun maybeStartTransitionIfUserSwitchedDuringGoingAway(): Boolean {
+ val currentUser = selectedUserInteractor.getSelectedUserId()
+ if (currentUser != keyguardGoingAwayRequestedForUserId) {
+ if (lockPatternUtils.isSecure(currentUser)) {
+ keyguardShowWhileAwakeInteractor.onSwitchedToSecureUserWhileKeyguardGoingAway()
+ } else {
+ keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+ reason = "User switch during keyguard going away, and new user is insecure"
+ )
+ }
+
+ return true
+ } else {
+ return false
+ }
+ }
+
companion object {
private val TAG = "WindowManagerLsVis"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
new file mode 100644
index 0000000..16c2d14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.os.IRemoteCallback
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Holds an IRemoteCallback along with the current user ID at the time the callback was provided.
+ */
+data class ShowLockscreenCallback(val userId: Int, val remoteCallback: IRemoteCallback)
+
+/** Maintains state related to KeyguardService requests to show the lockscreen. */
+@SysUISingleton
+class KeyguardServiceShowLockscreenRepository @Inject constructor() {
+ val showLockscreenCallbacks = ArrayList<ShowLockscreenCallback>()
+
+ /**
+ * Adds a callback that we'll notify when we show the lockscreen (or affirmatively decide not to
+ * show it).
+ */
+ fun addShowLockscreenCallback(forUser: Int, callback: IRemoteCallback) {
+ synchronized(showLockscreenCallbacks) {
+ showLockscreenCallbacks.add(ShowLockscreenCallback(forUser, callback))
+ }
+ }
+
+ companion object {
+ private const val TAG = "ShowLockscreenRepository"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index ab0efed..02e04aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -226,6 +226,10 @@
}
fun handleFidgetTap(x: Float, y: Float) {
+ if (!com.android.systemui.Flags.clockFidgetAnimation()) {
+ return
+ }
+
if (selectedClockSize.value == ClockSizeSetting.DYNAMIC) {
clockEventController.handleFidgetTap(x, y)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
index b55bb38..07a31e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
@@ -17,16 +17,27 @@
package com.android.systemui.keyguard.domain.interactor
import android.annotation.SuppressLint
+import android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.RemoteException
+import android.util.Log
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.data.repository.KeyguardServiceShowLockscreenRepository
+import com.android.systemui.keyguard.data.repository.ShowLockscreenCallback
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
/**
- * Logic around requests by [KeyguardService] to show keyguard right now, even though the device is
- * awake and not going to sleep.
+ * Logic around requests by [KeyguardService] and friends to show keyguard right now, even though
+ * the device is awake and not going to sleep.
*
* This can happen if WM#lockNow() is called, if KeyguardService#showDismissibleKeyguard is called
* because we're folding with "continue using apps on fold" set to "swipe up to continue", or if the
@@ -38,7 +49,28 @@
@SysUISingleton
class KeyguardServiceShowLockscreenInteractor
@Inject
-constructor(@Background val backgroundScope: CoroutineScope) {
+constructor(
+ @Background val backgroundScope: CoroutineScope,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val repository: KeyguardServiceShowLockscreenRepository,
+ private val userTracker: UserTracker,
+ private val wmLockscreenVisibilityInteractor: Lazy<WindowManagerLockscreenVisibilityInteractor>,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) : CoreStartable {
+
+ override fun start() {
+ backgroundScope.launch {
+ // Whenever we tell ATMS that lockscreen is visible, notify any showLockscreenCallbacks.
+ // This is not the only place we notify the lockNowCallbacks - there are cases where we
+ // decide not to show the lockscreen despite being asked to, and we need to notify the
+ // callback in those cases as well.
+ wmLockscreenVisibilityInteractor.get().lockscreenVisibility.collect { visible ->
+ if (visible) {
+ notifyShowLockscreenCallbacks()
+ }
+ }
+ }
+ }
/**
* Emits whenever [KeyguardService] receives a call that indicates we should show the lockscreen
@@ -57,9 +89,38 @@
/**
* Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
* the device locked while the screen was on.
+ *
+ * We'll show keyguard, and if provided, save the lock on user switch callback, to notify it
+ * later when we successfully show.
*/
- fun onKeyguardServiceDoKeyguardTimeout() {
+ fun onKeyguardServiceDoKeyguardTimeout(options: Bundle? = null) {
backgroundScope.launch {
+ if (options?.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) {
+ val userId = userTracker.userId
+
+ // This callback needs to be invoked after we show the lockscreen (or decide not to
+ // show it) otherwise System UI will crash in 20 seconds, as a security measure.
+ repository.addShowLockscreenCallback(
+ userId,
+ IRemoteCallback.Stub.asInterface(
+ options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK)
+ ),
+ )
+
+ Log.d(
+ TAG,
+ "Showing lockscreen now - setting required callback for user $userId. " +
+ "SysUI will crash if this callback is not invoked.",
+ )
+
+ // If the keyguard is disabled or suppressed, we'll never actually show the
+ // lockscreen. Notify the callback so we don't crash.
+ if (!keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()) {
+ Log.d(TAG, "Keyguard is disabled or suppressed, notifying callbacks now.")
+ notifyShowLockscreenCallbacks()
+ }
+ }
+
showNowEvents.emit(ShowWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
}
}
@@ -74,4 +135,33 @@
showNowEvents.emit(ShowWhileAwakeReason.FOLDED_WITH_SWIPE_UP_TO_CONTINUE)
}
}
+
+ /** Notifies the callbacks that we've either locked, or decided not to lock. */
+ private fun notifyShowLockscreenCallbacks() {
+ var callbacks: MutableList<ShowLockscreenCallback>
+ synchronized(repository.showLockscreenCallbacks) {
+ callbacks = ArrayList(repository.showLockscreenCallbacks)
+ repository.showLockscreenCallbacks.clear()
+ }
+
+ val iter: MutableIterator<ShowLockscreenCallback> = callbacks.listIterator()
+ while (iter.hasNext()) {
+ val callback = iter.next()
+ iter.remove()
+ if (callback.userId != selectedUserInteractor.getSelectedUserId()) {
+ Log.i(TAG, "Not notifying lockNowCallback due to user mismatch")
+ continue
+ }
+ Log.i(TAG, "Notifying lockNowCallback")
+ try {
+ callback.remoteCallback.sendResult(null)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Could not issue LockNowCallback sendResult", e)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "ShowLockscreenInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
index a8000a56..c67939a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
@@ -16,15 +16,20 @@
package com.android.systemui.keyguard.domain.interactor
+import android.annotation.SuppressLint
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
/** The reason we're showing lockscreen while awake, used for logging. */
enum class ShowWhileAwakeReason(private val logReason: String) {
@@ -38,6 +43,9 @@
),
KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
"Timed out while the screen was kept on, or WM#lockNow() was called."
+ ),
+ SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY(
+ "User switch to secure user occurred during keyguardGoingAway sequence, so we're locking."
);
override fun toString(): String {
@@ -68,6 +76,7 @@
class KeyguardShowWhileAwakeInteractor
@Inject
constructor(
+ @Background val backgroundScope: CoroutineScope,
biometricSettingsRepository: BiometricSettingsRepository,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
@@ -91,6 +100,15 @@
.filter { reshow -> reshow }
.map { ShowWhileAwakeReason.KEYGUARD_REENABLED }
+ /**
+ * Emits whenever a user switch to a secure user occurs during keyguard going away.
+ *
+ * This is an event flow, hence the SharedFlow.
+ */
+ @SuppressLint("SharedFlowCreation")
+ val switchedToSecureUserDuringGoingAway: MutableSharedFlow<ShowWhileAwakeReason> =
+ MutableSharedFlow()
+
/** Emits whenever we should show lockscreen while the screen is on, for any reason. */
val showWhileAwakeEvents: Flow<ShowWhileAwakeReason> =
merge(
@@ -108,5 +126,15 @@
keyguardServiceShowLockscreenInteractor.showNowEvents.filter {
keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()
},
+ switchedToSecureUserDuringGoingAway,
)
+
+ /** A user switch to a secure user occurred while we were going away. We need to re-lock. */
+ fun onSwitchedToSecureUserWhileKeyguardGoingAway() {
+ backgroundScope.launch {
+ switchedToSecureUserDuringGoingAway.emit(
+ ShowWhileAwakeReason.SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index 2b4582a..780b7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -29,6 +29,7 @@
private val auditLogger: KeyguardTransitionAuditLogger,
private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
private val keyguardStateCallbackInteractor: KeyguardStateCallbackInteractor,
+ private val keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
) : CoreStartable {
override fun start() {
@@ -54,6 +55,7 @@
auditLogger.start()
statusBarDisableFlagsInteractor.start()
keyguardStateCallbackInteractor.start()
+ keyguardServiceShowLockscreenInteractor.start()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index cedf661..5b65531 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -127,7 +127,6 @@
}
holder.seekBar.setMax(data.duration)
- val totalTimeDescription = data.durationDescription
if (data.scrubbing) {
holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
}
@@ -147,17 +146,9 @@
}
}
- val elapsedTimeDescription = data.elapsedTimeDescription
if (data.scrubbing) {
holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
}
-
- holder.seekBar.contentDescription =
- holder.seekBar.context.getString(
- R.string.controls_media_seekbar_description,
- elapsedTimeDescription,
- totalTimeDescription,
- )
}
}
@@ -166,6 +157,18 @@
return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
}
+ fun updateContentDescription(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ holder.seekBar.contentDescription =
+ holder.seekBar.context.getString(
+ R.string.controls_media_seekbar_description,
+ elapsedTimeDescription,
+ durationDescription,
+ )
+ }
+
@VisibleForTesting
open fun buildResetAnimator(targetTime: Int): Animator {
val animator =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 006eb20..f69985e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -224,6 +224,8 @@
this::setIsScrubbing;
private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
this::setIsSeekBarEnabled;
+ private final SeekBarViewModel.ContentDescriptionListener mContentDescriptionListener =
+ this::setSeekbarContentDescription;
private final BroadcastDialogController mBroadcastDialogController;
private boolean mIsCurrentBroadcastedApp = false;
@@ -327,6 +329,7 @@
}
mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
mSeekBarViewModel.removeEnabledChangeListener(mEnabledChangeListener);
+ mSeekBarViewModel.removeContentDescriptionListener(mContentDescriptionListener);
mSeekBarViewModel.onDestroy();
mMediaViewController.onDestroy();
}
@@ -395,6 +398,10 @@
});
}
+ private void setSeekbarContentDescription(CharSequence elapsedTime, CharSequence duration) {
+ mSeekBarObserver.updateContentDescription(elapsedTime, duration);
+ }
+
/**
* Reloads animator duration scale.
*/
@@ -424,6 +431,7 @@
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
+ mSeekBarViewModel.setContentDescriptionListener(mContentDescriptionListener);
mMediaViewController.attach(player);
vh.getPlayer().setOnLongClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index dba1900..e87d5de 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -229,6 +229,20 @@
}
}
+ private val seekbarDescriptionListener =
+ object : SeekBarViewModel.ContentDescriptionListener {
+ override fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ if (!SceneContainerFlag.isEnabled) return
+ seekBarObserver.updateContentDescription(
+ elapsedTimeDescription,
+ durationDescription,
+ )
+ }
+ }
+
/**
* Sets the listening state of the player.
*
@@ -350,6 +364,7 @@
}
seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
+ seekBarViewModel.removeContentDescriptionListener(seekbarDescriptionListener)
seekBarViewModel.onDestroy()
}
mediaHostStatesManager.removeController(this)
@@ -653,6 +668,7 @@
seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
+ seekBarViewModel.setContentDescriptionListener(seekbarDescriptionListener)
val mediaCard = mediaViewHolder.player
attach(mediaViewHolder.player)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 8744c5c..78a8cf8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -104,19 +104,20 @@
)
set(value) {
val enabledChanged = value.enabled != field.enabled
+ field = value
if (enabledChanged) {
enabledChangeListener?.onEnabledChanged(value.enabled)
}
+ _progress.postValue(value)
+
bgExecutor.execute {
val durationDescription = formatTimeContentDescription(value.duration)
val elapsedDescription =
value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
- field =
- value.copy(
- durationDescription = durationDescription,
- elapsedTimeDescription = elapsedDescription,
- )
- _progress.postValue(field)
+ contentDescriptionListener?.onContentDescriptionChanged(
+ elapsedDescription,
+ durationDescription,
+ )
}
}
@@ -175,6 +176,7 @@
private var scrubbingChangeListener: ScrubbingChangeListener? = null
private var enabledChangeListener: EnabledChangeListener? = null
+ private var contentDescriptionListener: ContentDescriptionListener? = null
/** Set to true when the user is touching the seek bar to change the position. */
private var scrubbing = false
@@ -394,6 +396,16 @@
}
}
+ fun setContentDescriptionListener(listener: ContentDescriptionListener) {
+ contentDescriptionListener = listener
+ }
+
+ fun removeContentDescriptionListener(listener: ContentDescriptionListener) {
+ if (listener == contentDescriptionListener) {
+ contentDescriptionListener = null
+ }
+ }
+
/** returns a pair of whether seekbar is enabled and the duration of media. */
private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
@@ -468,6 +480,13 @@
fun onEnabledChanged(enabled: Boolean)
}
+ interface ContentDescriptionListener {
+ fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ )
+ }
+
private class SeekBarChangeListener(
val viewModel: SeekBarViewModel,
val falsingManager: FalsingManager,
@@ -639,7 +658,5 @@
val duration: Int,
/** whether seekBar is listening to progress updates */
val listening: Boolean,
- val elapsedTimeDescription: CharSequence = "",
- val durationDescription: CharSequence = "",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
index 7543e0f..f072388 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
@@ -46,7 +46,6 @@
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -55,7 +54,6 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
@@ -75,6 +73,7 @@
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -101,6 +100,7 @@
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
@@ -110,6 +110,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastRoundToInt
import com.android.compose.PlatformButton
import com.android.compose.PlatformIconButton
import com.android.compose.PlatformOutlinedButton
@@ -138,6 +139,7 @@
import com.android.systemui.media.remedia.ui.viewmodel.MediaSecondaryActionViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaViewModel
import kotlin.math.max
+import kotlin.math.min
/**
* Renders a media controls UI element.
@@ -406,7 +408,7 @@
)
) {
// Always add the first/top row, regardless of presentation style.
- BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
+ Box(modifier = Modifier.fillMaxWidth()) {
// Icon.
Icon(
icon = viewModel.icon,
@@ -418,9 +420,26 @@
.clip(CircleShape),
)
+ var cardMaxWidth: Int by remember { mutableIntStateOf(0) }
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
- modifier = Modifier.align(Alignment.TopEnd),
+ modifier =
+ Modifier.align(Alignment.TopEnd)
+ // Output switcher chips must each be limited to at most 40% of the maximum
+ // width of the card.
+ //
+ // This saves the maximum possible width of the card so it can be referred
+ // to by child custom layout code below.
+ //
+ // The assumption is that the row can be as wide as the entire card.
+ .layout { measurable, constraints ->
+ cardMaxWidth = constraints.maxWidth
+ val placeable = measurable.measure(constraints)
+
+ layout(placeable.measuredWidth, placeable.measuredHeight) {
+ placeable.place(0, 0)
+ }
+ },
) {
viewModel.outputSwitcherChips.fastForEach { chip ->
OutputSwitcherChip(
@@ -433,9 +452,23 @@
//
// The underlying assumption is that there'll never be more than one
// chip with text and one more icon-only chip. Only the one with
- // text
- // can ever end up being too wide.
- .widthIn(max = this@BoxWithConstraints.maxWidth * 0.4f),
+ // text can ever end up being too wide.
+ .layout { measurable, constraints ->
+ val placeable =
+ measurable.measure(
+ constraints.copy(
+ maxWidth =
+ min(
+ (cardMaxWidth * 0.4f).fastRoundToInt(),
+ constraints.maxWidth,
+ )
+ )
+ )
+
+ layout(placeable.measuredWidth, placeable.measuredHeight) {
+ placeable.place(0, 0)
+ }
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
index 71cb745..68cd807 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
@@ -17,9 +17,9 @@
import android.util.Log
import android.view.Display
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.display.data.repository.PerDisplayInstanceProviderWithTeardown
import com.android.systemui.dump.DumpManager
import com.android.systemui.model.SysUiState.SysUiStateCallback
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 49fa3ba..88f679e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -72,4 +72,8 @@
/** @return {@link NavigationBar} on the default display. */
@Nullable
NavigationBar getDefaultNavigationBar();
+
+ /** @return {@link NavigationBar} for a specific display, or null if not available. */
+ @Nullable
+ NavigationBar getNavigationBar(int displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
index 45ff7f4..f096510 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -54,4 +54,6 @@
override fun isOverviewEnabled(displayId: Int) = false
override fun getDefaultNavigationBar(): NavigationBar? = null
+
+ override fun getNavigationBar(displayId: Int): NavigationBar? = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 50d0a45..8fbf8b60 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -469,7 +469,8 @@
return (navBar == null) ? null : navBar.getView();
}
- private @Nullable NavigationBar getNavigationBar(int displayId) {
+ @Override
+ public @Nullable NavigationBar getNavigationBar(int displayId) {
return mNavigationBars.get(displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 914e0f7..58ddbf6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -27,10 +27,13 @@
import com.android.app.viewcapture.ViewCapture;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.display.data.repository.PerDisplayRepository;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.views.NavigationBarFrame;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.res.R;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import dagger.Lazy;
import dagger.Module;
@@ -71,6 +74,20 @@
return context.getSystemService(WindowManager.class);
}
+ /** A SysUiState for the navigation bar display. */
+ @Provides
+ @NavigationBarScope
+ @DisplayId
+ static SysUiState provideSysUiState(@DisplayId Context context,
+ SysUiState defaultState,
+ PerDisplayRepository<SysUiState> repository) {
+ if (ShadeWindowGoesAround.isEnabled()) {
+ return repository.get(context.getDisplayId());
+ } else {
+ return defaultState;
+ }
+ }
+
/** A ViewCaptureAwareWindowManager specific to the display's context. */
@Provides
@NavigationBarScope
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 f95f459..8b5b3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -569,7 +569,7 @@
NavigationModeController navigationModeController,
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- SysUiState sysUiFlagsContainer,
+ @DisplayId SysUiState sysUiFlagsContainer,
UserTracker userTracker,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
@@ -1694,7 +1694,7 @@
(mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
- .commitUpdate(mDisplayId);
+ .commitUpdate();
}
private void updateAssistantEntrypoints(boolean assistantAvailable,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index 36cb8fa..cbc4c26 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -740,15 +740,13 @@
/** */
public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) {
- int displayId = mContext.getDisplayId();
-
sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
.setFlag(SYSUI_STATE_HOME_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
.setFlag(SYSUI_STATE_SEARCH_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0)
- .commitUpdate(displayId);
+ .commitUpdate();
}
public void setInScreenPinning(boolean active) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index a4386de..05a60a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -18,7 +18,6 @@
import android.annotation.SuppressLint
import android.content.Context
-import android.content.res.Configuration
import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
@@ -49,7 +48,6 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -72,8 +70,6 @@
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.layout.positionOnScreen
import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
@@ -255,7 +251,7 @@
@Composable
private fun Content() {
- PlatformTheme(isDarkTheme = true /* Delete AlwaysDarkMode when removing this */) {
+ PlatformTheme {
ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
// TODO(b/389985793): Make sure that there is no coroutine work or recompositions
// happening when alwaysCompose is true but isQsVisibleAndAnyShadeExpanded is false.
@@ -747,25 +743,22 @@
)
val BrightnessSlider =
@Composable {
- AlwaysDarkMode {
- Box(
- Modifier.systemGestureExclusionInShade(
- enabled = {
- layoutState.transitionState is TransitionState.Idle
- }
- )
- ) {
- BrightnessSliderContainer(
- viewModel =
- containerViewModel.brightnessSliderViewModel,
- containerColors =
- ContainerColors(
- Color.Transparent,
- ContainerColors.defaultContainerColor,
- ),
- modifier = Modifier.fillMaxWidth(),
- )
- }
+ Box(
+ Modifier.systemGestureExclusionInShade(
+ enabled = {
+ layoutState.transitionState is TransitionState.Idle
+ }
+ )
+ ) {
+ BrightnessSliderContainer(
+ viewModel = containerViewModel.brightnessSliderViewModel,
+ containerColors =
+ ContainerColors(
+ Color.Transparent,
+ ContainerColors.defaultContainerColor,
+ ),
+ modifier = Modifier.fillMaxWidth(),
+ )
}
}
val TileGrid =
@@ -1243,28 +1236,3 @@
private inline val alwaysCompose
get() = Flags.alwaysComposeQsUiFragment()
-
-/**
- * Forces the configuration and themes to be dark theme. This is needed in order to have
- * [colorResource] retrieve the dark mode colors.
- *
- * This should be removed when we remove the force dark mode in [PlatformTheme] at the root of the
- * compose hierarchy.
- */
-@Composable
-private fun AlwaysDarkMode(content: @Composable () -> Unit) {
- val currentConfig = LocalConfiguration.current
- val darkConfig =
- Configuration(currentConfig).apply {
- uiMode =
- (uiMode and (Configuration.UI_MODE_NIGHT_MASK.inv())) or
- Configuration.UI_MODE_NIGHT_YES
- }
- val newContext = LocalContext.current.createConfigurationContext(darkConfig)
- CompositionLocalProvider(
- LocalConfiguration provides darkConfig,
- LocalContext provides newContext,
- ) {
- content()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 22971a9..a7ebb22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -88,7 +88,11 @@
LaunchedEffect(listening, pagerState) {
snapshotFlow { listening() }
.collect {
- if (!listening()) {
+ // Whenever we go from not listening to listening, we should be in the first
+ // page. If we did this when going from listening to not listening, opening
+ // edit mode in second page will cause it to go to first page during the
+ // transition.
+ if (listening()) {
pagerState.scrollToPage(0)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 69b967a..ccbd8fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -63,6 +63,7 @@
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -89,6 +90,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.graphics.Color
@@ -113,7 +115,9 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.modifiers.height
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.common.ui.compose.load
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
@@ -131,6 +135,7 @@
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_SPEED
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AvailableTilesGridMinHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.GridBackgroundCornerRadius
import com.android.systemui.qs.panels.ui.compose.selection.InteractiveTileContainer
import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
import com.android.systemui.qs.panels.ui.compose.selection.ResizingState
@@ -163,14 +168,27 @@
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
-
+ val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
TopAppBar(
- colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
- title = { Text(text = stringResource(id = R.string.qs_edit)) },
+ colors =
+ TopAppBarDefaults.topAppBarColors(
+ containerColor = Color.Transparent,
+ titleContentColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ title = {
+ Text(
+ text = stringResource(id = R.string.qs_edit),
+ modifier = Modifier.padding(start = 24.dp),
+ )
+ },
navigationIcon = {
- IconButton(onClick = onStopEditing) {
+ IconButton(
+ onClick = onStopEditing,
+ modifier = Modifier.drawBehind { drawCircle(primaryContainerColor) },
+ ) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
+ tint = MaterialTheme.colorScheme.onSurface,
contentDescription =
stringResource(id = com.android.internal.R.string.action_bar_up_description),
)
@@ -178,11 +196,19 @@
},
actions = {
if (onReset != null) {
- TextButton(onClick = onReset) {
+ TextButton(
+ onClick = onReset,
+ colors =
+ ButtonDefaults.textButtonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ ),
+ ) {
Text(stringResource(id = com.android.internal.R.string.reset))
}
}
},
+ modifier = Modifier.padding(vertical = 8.dp),
)
}
@@ -215,7 +241,9 @@
containerColor = Color.Transparent,
topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
) { innerPadding ->
- CompositionLocalProvider(LocalOverscrollFactory provides null) {
+ CompositionLocalProvider(
+ LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+ ) {
val scrollState = rememberScrollState()
AutoScrollGrid(listState, scrollState, innerPadding)
@@ -244,7 +272,7 @@
targetState = listState.dragInProgress || selectionState.selected,
label = "QSEditHeader",
contentAlignment = Alignment.Center,
- modifier = Modifier.fillMaxWidth().heightIn(min = 80.dp),
+ modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
) { showRemoveTarget ->
EditGridHeader {
if (showRemoveTarget) {
@@ -289,10 +317,6 @@
spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
modifier = modifier.fillMaxSize(),
) {
- EditGridHeader {
- Text(text = stringResource(id = R.string.drag_to_add_tiles))
- }
-
val availableTiles = remember {
mutableStateListOf<AvailableTileGridCell>().apply {
addAll(toAvailableTiles(listState.tiles, otherTiles))
@@ -371,9 +395,7 @@
modifier: Modifier = Modifier,
content: @Composable BoxScope.() -> Unit,
) {
- CompositionLocalProvider(
- LocalContentColor provides MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
- ) {
+ CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxWidth()) { content() }
}
}
@@ -420,6 +442,7 @@
listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
}
+ val primaryColor = MaterialTheme.colorScheme.primary
TileLazyGrid(
state = gridState,
columns = GridCells.Fixed(columns),
@@ -428,9 +451,9 @@
Modifier.fillMaxWidth()
.height { totalHeight.roundToPx() }
.border(
- width = 1.dp,
- color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
- shape = RoundedCornerShape((TileHeight / 2) + CurrentTilesGridPadding),
+ width = 2.dp,
+ color = primaryColor,
+ shape = RoundedCornerShape(GridBackgroundCornerRadius),
)
.dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec ->
onSetTiles(currentListState.tileSpecs())
@@ -439,6 +462,13 @@
.onGloballyPositioned { coordinates ->
gridContentOffset = coordinates.positionInRoot()
}
+ .drawBehind {
+ drawRoundRect(
+ primaryColor,
+ cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+ alpha = .15f,
+ )
+ }
.testTag(CURRENT_TILES_GRID_TEST_TAG),
) {
EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) {
@@ -469,7 +499,6 @@
remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
groupAndSort(tiles)
}
- val labelColors = EditModeTileDefaults.editTileColors()
// Available tiles
Column(
@@ -480,32 +509,45 @@
) {
groupedTiles.forEach { (category, tiles) ->
key(category) {
- Text(
- text = category.label.load() ?: "",
- fontSize = 20.sp,
- color = labelColors.label,
+ val surfaceColor = MaterialTheme.colorScheme.surface
+ Column(
+ verticalArrangement = spacedBy(16.dp),
modifier =
- Modifier.fillMaxWidth().padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
- )
- tiles.chunked(columns).forEach { row ->
- Row(
- horizontalArrangement = spacedBy(TileArrangementPadding),
- modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
- ) {
- row.forEach { tileGridCell ->
- key(tileGridCell.key) {
- AvailableTileGridCell(
- cell = tileGridCell,
- dragAndDropState = dragAndDropState,
- selectionState = selectionState,
- onAddTile = onAddTile,
- modifier = Modifier.weight(1f).fillMaxHeight(),
+ Modifier.drawBehind {
+ drawRoundRect(
+ surfaceColor,
+ cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+ alpha = .32f,
)
}
- }
+ .padding(16.dp),
+ ) {
+ Text(
+ text = category.label.load() ?: "",
+ fontSize = 20.sp,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
+ )
+ tiles.chunked(columns).forEach { row ->
+ Row(
+ horizontalArrangement = spacedBy(TileArrangementPadding),
+ modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
+ ) {
+ row.forEach { tileGridCell ->
+ key(tileGridCell.key) {
+ AvailableTileGridCell(
+ cell = tileGridCell,
+ dragAndDropState = dragAndDropState,
+ selectionState = selectionState,
+ onAddTile = onAddTile,
+ modifier = Modifier.weight(1f).fillMaxHeight(),
+ )
+ }
+ }
- // Spacers for incomplete rows
- repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+ // Spacers for incomplete rows
+ repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+ }
}
}
}
@@ -761,7 +803,7 @@
color = colors.label,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
- modifier = Modifier.align(Alignment.Center),
+ modifier = Modifier.align(Alignment.TopCenter),
)
}
}
@@ -861,15 +903,16 @@
const val AUTO_SCROLL_SPEED = 2 // 2ms per pixel
val CurrentTilesGridPadding = 10.dp
val AvailableTilesGridMinHeight = 200.dp
+ val GridBackgroundCornerRadius = 42.dp
@Composable
fun editTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceVariant,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = Color.Transparent,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index bf63c3858..6bafd43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -25,6 +25,7 @@
import android.service.quicksettings.Tile.STATE_INACTIVE
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
@@ -59,6 +60,7 @@
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.semantics.Role
@@ -74,7 +76,9 @@
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.Expandable
import com.android.compose.animation.bounceable
+import com.android.compose.animation.rememberExpandableController
import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -165,6 +169,7 @@
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape by TileDefaults.animateTileShapeAsState(uiState.state)
val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
+ val animatedAlpha by animateFloatAsState(colors.alpha, label = "QSTileAlpha")
TileExpandable(
color = { animatedColor },
@@ -181,7 +186,8 @@
nextBounceable = currentBounceableInfo.nextTile,
orientation = Orientation.Horizontal,
bounceEnd = currentBounceableInfo.bounceEnd,
- ),
+ )
+ .graphicsLayer { alpha = animatedAlpha },
) { expandable ->
val longClick: (() -> Unit)? =
{
@@ -260,8 +266,7 @@
content: @Composable (Expandable) -> Unit,
) {
Expandable(
- color = color(),
- shape = shape,
+ controller = rememberExpandableController(color = color, shape = shape),
modifier = modifier.clip(shape).verticalSquish(squishiness),
useModifierBasedImplementation = true,
) {
@@ -370,6 +375,7 @@
val label: Color,
val secondaryLabel: Color,
val icon: Color,
+ val alpha: Float = 1f,
)
private object TileDefaults {
@@ -393,10 +399,10 @@
@ReadOnlyComposable
fun activeDualTargetTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
iconBackground = MaterialTheme.colorScheme.primary,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onPrimary,
)
@@ -404,30 +410,19 @@
@ReadOnlyComposable
fun inactiveDualTargetTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceContainerHighest,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = LocalAndroidColorScheme.current.surfaceEffect3,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
)
@Composable
@ReadOnlyComposable
fun inactiveTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.surfaceVariant,
- iconBackground = MaterialTheme.colorScheme.surfaceVariant,
- label = MaterialTheme.colorScheme.onSurfaceVariant,
- secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
- icon = MaterialTheme.colorScheme.onSurfaceVariant,
- )
-
- @Composable
- @ReadOnlyComposable
- fun unavailableTileColors(): TileColors =
- TileColors(
- background = MaterialTheme.colorScheme.surface,
- iconBackground = MaterialTheme.colorScheme.surface,
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = Color.Transparent,
label = MaterialTheme.colorScheme.onSurface,
secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onSurface,
@@ -435,6 +430,19 @@
@Composable
@ReadOnlyComposable
+ fun unavailableTileColors(): TileColors {
+ return TileColors(
+ background = LocalAndroidColorScheme.current.surfaceEffect2,
+ iconBackground = LocalAndroidColorScheme.current.surfaceEffect2,
+ label = MaterialTheme.colorScheme.onSurface,
+ secondaryLabel = MaterialTheme.colorScheme.onSurface,
+ icon = MaterialTheme.colorScheme.onSurface,
+ alpha = .38f,
+ )
+ }
+
+ @Composable
+ @ReadOnlyComposable
fun getColorForState(uiState: TileUiState, iconOnly: Boolean): TileColors {
return when (uiState.state) {
STATE_ACTIVE -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index a66b51f..57f63c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -62,7 +62,6 @@
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.zIndex
-import com.android.compose.modifiers.size
import com.android.compose.modifiers.thenIf
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BADGE_ANGLE_RAD
@@ -155,6 +154,7 @@
Icon(
Icons.Default.Remove,
contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier =
Modifier.size(size).align(Alignment.Center).graphicsLayer {
this.alpha = badgeIconAlpha
@@ -218,14 +218,15 @@
)
}
) {
- val secondaryColor = MaterialTheme.colorScheme.secondary
val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
+ val primaryColor = MaterialTheme.colorScheme.primary
Icon(
icon,
contentDescription = contentDescription,
+ tint = MaterialTheme.colorScheme.onPrimary,
modifier =
Modifier.size(size).align(Alignment.Center).drawBehind {
- drawCircle(secondaryColor, radius = BadgeSize.toPx() / 2)
+ drawCircle(primaryColor, radius = BadgeSize.toPx() / 2)
},
)
}
@@ -291,7 +292,7 @@
return animateColor { state ->
when (state) {
None -> Color.Transparent
- Removable -> MaterialTheme.colorScheme.secondary
+ Removable -> MaterialTheme.colorScheme.primaryContainer
Selected -> MaterialTheme.colorScheme.primary
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
index bc15bbb..263ef09e 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
@@ -20,6 +20,8 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.feature.flags.Flags
import androidx.annotation.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -28,8 +30,11 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
/**
* Provides a {@link com.android.systemui.statusbar.phone.SystemUIDialog} to be shown on the inner
@@ -46,6 +51,7 @@
private val rearDisplayStateInteractor: RearDisplayStateInteractor,
private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory,
@Application private val scope: CoroutineScope,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : CoreStartable, AutoCloseable {
companion object {
@@ -53,6 +59,16 @@
}
@VisibleForTesting var stateChangeListener: Job? = null
+ private val keyguardVisible = MutableStateFlow(false)
+ private val keyguardVisibleFlow = keyguardVisible.asStateFlow()
+
+ @VisibleForTesting
+ val keyguardCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onKeyguardVisibilityChanged(visible: Boolean) {
+ keyguardVisible.value = visible
+ }
+ }
override fun close() {
stateChangeListener?.cancel()
@@ -62,28 +78,39 @@
if (Flags.deviceStateRdmV2()) {
var dialog: SystemUIDialog? = null
- stateChangeListener =
- rearDisplayStateInteractor.state
- .map {
- when (it) {
- is RearDisplayStateInteractor.State.Enabled -> {
- val rearDisplayContext =
- context.createDisplayContext(it.innerDisplay)
- val delegate =
- rearDisplayInnerDialogDelegateFactory.create(
- rearDisplayContext,
- deviceStateManager::cancelStateRequest,
- )
- dialog = delegate.createDialog().apply { show() }
- }
+ keyguardUpdateMonitor.registerCallback(keyguardCallback)
- is RearDisplayStateInteractor.State.Disabled -> {
- dialog?.dismiss()
- dialog = null
+ stateChangeListener =
+ scope.launch {
+ combine(rearDisplayStateInteractor.state, keyguardVisibleFlow) {
+ rearDisplayState,
+ keyguardVisible ->
+ Pair(rearDisplayState, keyguardVisible)
+ }
+ .collectLatest { (rearDisplayState, keyguardVisible) ->
+ when (rearDisplayState) {
+ is RearDisplayStateInteractor.State.Enabled -> {
+ if (!keyguardVisible) {
+ val rearDisplayContext =
+ context.createDisplayContext(
+ rearDisplayState.innerDisplay
+ )
+ val delegate =
+ rearDisplayInnerDialogDelegateFactory.create(
+ rearDisplayContext,
+ deviceStateManager::cancelStateRequest,
+ )
+ dialog = delegate.createDialog().apply { show() }
+ }
+ }
+
+ is RearDisplayStateInteractor.State.Disabled -> {
+ dialog?.dismiss()
+ dialog = null
+ }
}
}
- }
- .launchIn(scope)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index 4be35f1..d263965 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -89,6 +89,8 @@
import com.android.systemui.contextualeducation.GestureType;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.display.data.repository.DisplayRepository;
+import com.android.systemui.display.data.repository.PerDisplayRepository;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
@@ -109,6 +111,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.shared.recents.ILauncherProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
@@ -156,7 +159,9 @@
private final Executor mMainExecutor;
private final ShellInterface mShellInterface;
private final Lazy<ShadeViewController> mShadeViewControllerLazy;
- private SysUiState mSysUiState;
+ private final PerDisplayRepository<SysUiState> mPerDisplaySysUiStateRepository;
+ private final DisplayRepository mDisplayRepository;
+ private SysUiState mDefaultDisplaySysUIState;
private final Handler mHandler;
private final Lazy<NavigationBarController> mNavBarControllerLazy;
private final ScreenPinningRequest mScreenPinningRequest;
@@ -586,9 +591,12 @@
// Force-update the systemui state flags
updateSystemUiStateFlags();
- // TODO b/398011576 - send the state for all displays.
- notifySystemUiStateFlags(mSysUiState.getFlags(), Display.DEFAULT_DISPLAY);
-
+ if (ShadeWindowGoesAround.isEnabled()) {
+ notifySysUiStateFlagsForAllDisplays();
+ } else {
+ notifySystemUiStateFlags(mDefaultDisplaySysUIState.getFlags(),
+ Display.DEFAULT_DISPLAY);
+ }
notifyConnectionChanged();
}
@@ -614,6 +622,18 @@
}
};
+ /** Propagates the flags for all displays to be notified to Launcher. */
+ @VisibleForTesting
+ public void notifySysUiStateFlagsForAllDisplays() {
+ var displays = mDisplayRepository.getDisplayIds().getValue();
+ for (int displayId : displays) {
+ var state = mPerDisplaySysUiStateRepository.get(displayId);
+ if (state != null) {
+ notifySystemUiStateFlags(state.getFlags(), displayId);
+ }
+ }
+ }
+
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
// This is the death handler for the binder from the launcher service
@@ -671,7 +691,7 @@
ScreenPinningRequest screenPinningRequest,
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController,
- SysUiState sysUiState,
+ PerDisplayRepository<SysUiState> perDisplaySysUiStateRepository,
Provider<SceneInteractor> sceneInteractor,
Provider<ShadeInteractor> shadeInteractor,
UserTracker userTracker,
@@ -686,7 +706,8 @@
Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
BroadcastDispatcher broadcastDispatcher,
Optional<BackAnimation> backAnimation,
- ProcessWrapper processWrapper
+ ProcessWrapper processWrapper,
+ DisplayRepository displayRepository
) {
// b/241601880: This component should only be running for primary users or
// secondaryUsers when visibleBackgroundUsers are supported.
@@ -718,10 +739,10 @@
com.android.internal.R.string.config_recentsComponentName));
mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
- // TODO b/398011576 - Here we're still only handling the default display state. We should
- // have a callback for any sysuiState change.
- mSysUiState = sysUiState;
- mSysUiState.addCallback(mSysUiStateCallback);
+ mPerDisplaySysUiStateRepository = perDisplaySysUiStateRepository;
+ mDisplayRepository = displayRepository;
+ mDefaultDisplaySysUIState = perDisplaySysUiStateRepository.get(Display.DEFAULT_DISPLAY);
+ mDefaultDisplaySysUIState.addCallback(mSysUiStateCallback);
mUiEventLogger = uiEventLogger;
mDisplayTracker = displayTracker;
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
@@ -770,7 +791,7 @@
if (mLauncherProxy != null) {
try {
if (DesktopModeStatus.canEnterDesktopMode(mContext)
- && (sysUiState.getFlags()
+ && (mDefaultDisplaySysUIState.getFlags()
& SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0) {
return;
}
@@ -795,7 +816,7 @@
}
public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
- mSysUiState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
+ mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
.commitUpdate(mContext.getDisplayId());
}
@@ -804,23 +825,42 @@
startConnectionToCurrentUser();
}
- private void updateSystemUiStateFlags() {
+ private void updateSysUIStateForNavbars() {
+ if (ShadeWindowGoesAround.isEnabled()) {
+ var displays = mDisplayRepository.getDisplayIds().getValue();
+ for (int displayId : displays) {
+ updateSysUIStateForNavbarWithDisplayId(displayId);
+ }
+ } else {
+ updateSysUIStateForNavbarWithDisplayId(Display.DEFAULT_DISPLAY);
+ }
+ }
+
+ private void updateSysUIStateForNavbarWithDisplayId(int displayId) {
final NavigationBar navBarFragment =
- mNavBarControllerLazy.get().getDefaultNavigationBar();
+ mNavBarControllerLazy.get().getNavigationBar(displayId);
final NavigationBarView navBarView =
- mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
+ mNavBarControllerLazy.get().getNavigationBarView(displayId);
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+ " navBarView=" + navBarView
+ " shadeViewController=" + mShadeViewControllerLazy.get());
}
+ final SysUiState displaySysuiState = mPerDisplaySysUiStateRepository.get(displayId);
+ if (displaySysuiState == null) return;
+
if (navBarFragment != null) {
navBarFragment.updateSystemUiStateFlags();
}
if (navBarView != null) {
- navBarView.updateDisabledSystemUiStateFlags(mSysUiState);
+ navBarView.updateDisabledSystemUiStateFlags(displaySysuiState);
}
+ }
+
+ /** Force updates SystemUI state flags prior to sending them to Launcher. */
+ public void updateSystemUiStateFlags() {
+ updateSysUIStateForNavbars();
mShadeViewControllerLazy.get().updateSystemUiStateFlags();
if (mStatusBarWinController != null) {
mStatusBarWinController.notifyStateChangedCallbacks();
@@ -845,7 +885,7 @@
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing,
boolean panelExpanded, boolean isDreaming, boolean communalShowing) {
- mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+ mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded)
.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
keyguardShowing && keyguardOccluded)
@@ -1122,7 +1162,7 @@
new WakefulnessLifecycle.Observer() {
@Override
public void onStartedWakingUp() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, true)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
.commitUpdate(mContext.getDisplayId());
@@ -1130,7 +1170,7 @@
@Override
public void onFinishedWakingUp() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, true)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
.commitUpdate(mContext.getDisplayId());
@@ -1138,7 +1178,7 @@
@Override
public void onStartedGoingToSleep() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, false)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
.commitUpdate(mContext.getDisplayId());
@@ -1146,7 +1186,7 @@
@Override
public void onFinishedGoingToSleep() {
- mSysUiState
+ mDefaultDisplaySysUIState
.setFlag(SYSUI_STATE_AWAKE, false)
.setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
.commitUpdate(mContext.getDisplayId());
@@ -1247,7 +1287,7 @@
pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
pw.print(" mNavBarMode="); pw.println(mNavBarMode);
pw.print(" mIsPrevServiceCleanedUp="); pw.println(mIsPrevServiceCleanedUp);
- mSysUiState.dump(pw, args);
+ mDefaultDisplaySysUIState.dump(pw, args);
}
public interface LauncherProxyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f45971b..2bacee1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -86,7 +86,7 @@
*/
fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
- !supportsBlursOnWindows() || earlyWakeupEnabled
+ !shouldBlur(radius) || earlyWakeupEnabled
) {
return
}
@@ -113,7 +113,7 @@
return
}
createTransaction().use {
- if (supportsBlursOnWindows()) {
+ if (shouldBlur(radius)) {
it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
Trace.asyncTraceForTrackBegin(
@@ -142,6 +142,14 @@
return SurfaceControl.Transaction()
}
+ private fun shouldBlur(radius: Int): Boolean {
+ return supportsBlursOnWindows() ||
+ ((Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) &&
+ supportsBlursOnWindowsBase() &&
+ lastAppliedBlur > 0 &&
+ radius == 0)
+ }
+
/**
* If this device can render blurs.
*
@@ -149,8 +157,11 @@
* @return {@code true} when supported.
*/
open fun supportsBlursOnWindows(): Boolean {
+ return supportsBlursOnWindowsBase() && crossWindowBlurListeners.isCrossWindowBlurEnabled
+ }
+
+ private fun supportsBlursOnWindowsBase(): Boolean {
return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
- crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
!SystemProperties.getBoolean("persist.sysui.disableBlur", false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 05ef164..d2f424a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -394,7 +394,7 @@
// Only drag down on sensitive views, otherwise the ExpandHelper will take this
return if (NotificationBundleUi.isEnabled)
view.entryAdapter?.isSensitive?.value == true
- else view.entry.isSensitive.value
+ else view.entryLegacy.isSensitive.value
}
}
return false
@@ -569,7 +569,7 @@
if (NotificationBundleUi.isEnabled) {
userId = expandView.entryAdapter?.sbn?.userId!!
} else {
- userId = expandView.entry.sbn.userId
+ userId = expandView.entryLegacy.sbn.userId
}
}
var fullShadeNeedsBouncer =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 339f898..9bf3d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -47,7 +47,6 @@
import android.net.Uri;
import android.os.Looper;
import android.os.Process;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -78,6 +77,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -920,7 +920,9 @@
// notification's "when" time, or the notification entry creation time
private long getEarliestNotificationTime(NotificationEntry notif) {
long notifWhenWallClock = notif.getSbn().getNotification().getWhen();
- long creationTimeDelta = SystemClock.uptimeMillis() - notif.getCreationTime();
+ long creationTimeDelta = UseElapsedRealtimeForCreationTime.getCurrentTime()
+ - notif.getCreationTime();
+
long creationTimeWallClock = System.currentTimeMillis() - creationTimeDelta;
return Math.min(notifWhenWallClock, creationTimeWallClock);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 041ed65..485d5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,7 +169,7 @@
if (NotificationBundleUi.isEnabled()) {
releaseNotificationIfKeptForRemoteInputHistory(row.getEntryAdapter());
} else {
- releaseNotificationIfKeptForRemoteInputHistory(row.getEntry());
+ releaseNotificationIfKeptForRemoteInputHistory(row.getEntryLegacy());
}
}
return started;
@@ -189,8 +189,8 @@
statusBarNotification = row.getEntryAdapter().getSbn();
}
} else {
- if (row.getEntry() != null) {
- statusBarNotification = row.getEntry().getSbn();
+ if (row.getEntryLegacy() != null) {
+ statusBarNotification = row.getEntryLegacy().getSbn();
}
}
if (statusBarNotification == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 2e83910..472dc82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,8 +19,6 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.Configuration
import android.os.SystemClock
import android.util.IndentingPrintWriter
import android.util.Log
@@ -42,16 +40,14 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.WallpaperController
import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
@@ -82,13 +78,11 @@
private val wallpaperInteractor: WallpaperInteractor,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val dozeParameters: DozeParameters,
- @ShadeDisplayAware private val context: Context,
- private val splitShadeStateController: SplitShadeStateController,
+ private val shadeModeInteractor: ShadeModeInteractor,
private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
private val appZoomOutOptional: Optional<AppZoomOut>,
@Application private val applicationScope: CoroutineScope,
dumpManager: DumpManager,
- configurationController: ConfigurationController,
) : ShadeExpansionListener, Dumpable {
companion object {
private const val WAKE_UP_ANIMATION_ENABLED = true
@@ -110,7 +104,6 @@
private var isOpen: Boolean = false
private var isBlurred: Boolean = false
private var listeners = mutableListOf<DepthListener>()
- private var inSplitShade: Boolean = false
private var prevTracking: Boolean = false
private var prevTimestamp: Long = -1
@@ -294,7 +287,7 @@
private fun blurRadiusToZoomOut(blurRadius: Float): Float {
var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(blurRadius))
- if (inSplitShade) {
+ if (shadeModeInteractor.isSplitShade) {
zoomOut = 0f
}
@@ -432,14 +425,6 @@
}
shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
- updateResources()
- configurationController.addCallback(
- object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- updateResources()
- }
- }
- )
applicationScope.launch {
wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
wallpaperSupportsAmbientMode = supported
@@ -469,10 +454,6 @@
}
}
- private fun updateResources() {
- inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
- }
-
fun addListener(listener: DepthListener) {
listeners.add(listener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f88c618..c2a87cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -675,7 +675,7 @@
}
StatusBarIconView icon = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getIcons().getShelfIcon()
- : row.getEntry().getIcons().getShelfIcon();
+ : row.getEntryLegacy().getIcons().getShelfIcon();
float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
int top = (int) (maxTop - shelfIconPosition);
@@ -689,7 +689,7 @@
private void updateContinuousClipping(final ExpandableNotificationRow row) {
StatusBarIconView icon = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getIcons().getShelfIcon()
- : row.getEntry().getIcons().getShelfIcon();
+ : row.getEntryLegacy().getIcons().getShelfIcon();
boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 6cebcd9..6d3c12d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -18,10 +18,10 @@
per-file *Notification* = file:notification/OWNERS
# Files that control blur effects on shade
per-file *NotificationShadeDepth* = set noparent
-per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com
+per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
per-file *NotificationShadeDepth* = file:../keyguard/OWNERS
per-file *Blur* = set noparent
-per-file *Blur* = shanh@google.com, rahulbanerjee@google.com
+per-file *Blur* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
# Not setting noparent here, since *Mode* matches many other classes (e.g., *ViewModel*)
per-file *Mode* = file:notification/OWNERS
per-file *SmartReply* = set noparent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 7e70312..03108de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -17,10 +17,13 @@
package com.android.systemui.statusbar.chips.call.ui.viewmodel
import android.app.PendingIntent
+import android.content.ComponentName
import android.content.Context
import android.view.View
import com.android.internal.jank.Cuj
import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ComposableControllerFactory
+import com.android.systemui.animation.DelegateTransitionAnimatorController
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
@@ -48,7 +51,10 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
/** View model for the ongoing phone call chip shown in the status bar. */
@@ -63,14 +69,62 @@
private val activityStarter: ActivityStarter,
@StatusBarChipsLog private val logger: LogBuffer,
) : OngoingActivityChipViewModel {
+ /** The transition cookie used to register and unregister launch and return animations. */
+ private val cookie =
+ ActivityTransitionAnimator.TransitionCookie("${CallChipViewModel::class.java}")
+
+ /**
+ * Used internally to determine when a launch or return animation is in progress, as these
+ * require special handling.
+ */
+ private val transitionState: MutableStateFlow<TransitionState> =
+ MutableStateFlow(TransitionState.NoTransition)
+
+ // Since we're combining the chip state and the transition state flows, getting the old value by
+ // using [pairwise()] would confuse things. This is because if the calculation is triggered by
+ // a change in transition state, the chip state will still show the previous and current values,
+ // making it difficult to figure out what actually changed. Instead we cache the old value here,
+ // so that at each update we can keep track of what actually changed.
+ private var latestState: OngoingCallModel = OngoingCallModel.NoCall
+ private var latestTransitionState: TransitionState = TransitionState.NoTransition
+
private val chipWithReturnAnimation: StateFlow<OngoingActivityChipModel> =
if (StatusBarChipsReturnAnimations.isEnabled) {
- interactor.ongoingCallState
- .map { state ->
- when (state) {
- is OngoingCallModel.NoCall -> OngoingActivityChipModel.Inactive()
+ combine(interactor.ongoingCallState, transitionState) { newState, newTransitionState ->
+ val oldState = latestState
+ latestState = newState
+ val oldTransitionState = latestTransitionState
+ latestTransitionState = newTransitionState
+
+ logger.log(
+ TAG,
+ LogLevel.DEBUG,
+ {},
+ {
+ "Call chip state updated: oldState=$oldState newState=$newState " +
+ "oldTransitionState=$oldTransitionState " +
+ "newTransitionState=$newTransitionState"
+ },
+ )
+
+ when (newState) {
+ is OngoingCallModel.NoCall ->
+ OngoingActivityChipModel.Inactive(
+ transitionManager = getTransitionManager(newState)
+ )
+
is OngoingCallModel.InCall ->
- prepareChip(state, systemClock, isHidden = state.isAppVisible)
+ prepareChip(
+ newState,
+ systemClock,
+ isHidden =
+ shouldChipBeHidden(
+ oldState = oldState,
+ newState = newState,
+ oldTransitionState = oldTransitionState,
+ newTransitionState = newTransitionState,
+ ),
+ )
}
}
.stateIn(
@@ -112,6 +166,12 @@
chipLegacy
}
+ /**
+ * The controller factory that the call chip uses to register and unregister its transition
+ * animations.
+ */
+ private var transitionControllerFactory: ComposableControllerFactory? = null
+
/** Builds an [OngoingActivityChipModel.Active] from all the relevant information. */
private fun prepareChip(
state: OngoingCallModel.InCall,
@@ -149,6 +209,7 @@
onClickListenerLegacy = getOnClickListener(state.intent),
clickBehavior = getClickBehavior(state.intent),
isHidden = isHidden,
+ transitionManager = getTransitionManager(state),
)
} else {
val startTimeInElapsedRealtime =
@@ -161,6 +222,7 @@
onClickListenerLegacy = getOnClickListener(state.intent),
clickBehavior = getClickBehavior(state.intent),
isHidden = isHidden,
+ transitionManager = getTransitionManager(state),
)
}
}
@@ -191,9 +253,21 @@
onClick = { expandable ->
StatusBarChipsModernization.unsafeAssertInNewMode()
val animationController =
- expandable.activityTransitionController(
- Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
- )
+ if (
+ !StatusBarChipsReturnAnimations.isEnabled ||
+ transitionControllerFactory == null
+ ) {
+ expandable.activityTransitionController(
+ Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
+ )
+ } else {
+ // When return animations are enabled, we use a long-lived registration
+ // with controllers created on-demand by the animation library instead
+ // of explicitly creating one at the time of the click. By not passing
+ // a controller here, we let the framework do its work. Otherwise, the
+ // explicit controller would take precedence and override the other one.
+ null
+ }
activityStarter.postStartActivityDismissingKeyguard(intent, animationController)
}
)
@@ -210,6 +284,120 @@
)
}
+ private fun getTransitionManager(
+ state: OngoingCallModel
+ ): OngoingActivityChipModel.TransitionManager? {
+ if (!StatusBarChipsReturnAnimations.isEnabled) return null
+ return if (state is OngoingCallModel.NoCall) {
+ OngoingActivityChipModel.TransitionManager(
+ unregisterTransition = { activityStarter.unregisterTransition(cookie) }
+ )
+ } else {
+ val component = (state as OngoingCallModel.InCall).intent?.intent?.component
+ if (component != null) {
+ val factory = getTransitionControllerFactory(component)
+ OngoingActivityChipModel.TransitionManager(
+ factory,
+ registerTransition = {
+ activityStarter.registerTransition(cookie, factory, scope)
+ },
+ )
+ } else {
+ // Without a component we can't instantiate a controller factory, and without a
+ // factory registering an animation is impossible. In this case, the transition
+ // manager is empty and inert.
+ OngoingActivityChipModel.TransitionManager()
+ }
+ }
+ }
+
+ private fun getTransitionControllerFactory(
+ component: ComponentName
+ ): ComposableControllerFactory {
+ var factory = transitionControllerFactory
+ if (factory?.component == component) return factory
+
+ factory =
+ object :
+ ComposableControllerFactory(
+ cookie,
+ component,
+ launchCujType = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ ) {
+ override suspend fun createController(
+ forLaunch: Boolean
+ ): ActivityTransitionAnimator.Controller {
+ transitionState.value =
+ if (forLaunch) {
+ TransitionState.LaunchRequested
+ } else {
+ TransitionState.ReturnRequested
+ }
+
+ val controller =
+ expandable
+ .mapNotNull {
+ it?.activityTransitionController(
+ launchCujType,
+ cookie,
+ component,
+ returnCujType,
+ isEphemeral = false,
+ )
+ }
+ .first()
+
+ return object : DelegateTransitionAnimatorController(controller) {
+ override val isLaunching: Boolean
+ get() = forLaunch
+
+ override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
+ delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+ transitionState.value =
+ if (isLaunching) {
+ TransitionState.Launching
+ } else {
+ TransitionState.Returning
+ }
+ }
+
+ override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
+ delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
+ transitionState.value = TransitionState.NoTransition
+ }
+
+ override fun onTransitionAnimationCancelled(
+ newKeyguardOccludedState: Boolean?
+ ) {
+ delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+ transitionState.value = TransitionState.NoTransition
+ }
+ }
+ }
+ }
+
+ transitionControllerFactory = factory
+ return factory
+ }
+
+ /** Define the current state of this chip's transition animation. */
+ private sealed interface TransitionState {
+ /** Idle. */
+ data object NoTransition : TransitionState
+
+ /** Launch animation has been requested but hasn't started yet. */
+ data object LaunchRequested : TransitionState
+
+ /** Launch animation in progress. */
+ data object Launching : TransitionState
+
+ /** Return animation has been requested but hasn't started yet. */
+ data object ReturnRequested : TransitionState
+
+ /** Return animation in progress. */
+ data object Returning : TransitionState
+ }
+
companion object {
private val phoneIcon =
Icon.Resource(
@@ -217,5 +405,42 @@
ContentDescription.Resource(R.string.ongoing_call_content_description),
)
private val TAG = "CallVM".pad()
+
+ /** Determines whether or not an active call chip should be hidden. */
+ private fun shouldChipBeHidden(
+ oldState: OngoingCallModel,
+ newState: OngoingCallModel.InCall,
+ oldTransitionState: TransitionState,
+ newTransitionState: TransitionState,
+ ): Boolean {
+ // The app is in the background and no transitions are ongoing (during transitions,
+ // [isAppVisible] must always be true). Show the chip.
+ if (!newState.isAppVisible) return false
+
+ // The call has just started and is visible. Hide the chip.
+ if (oldState is OngoingCallModel.NoCall) return true
+
+ // The state went from the app not being visible to visible. This happens when the chip
+ // is tapped and a launch animation is about to start. Keep the chip showing.
+ if (!(oldState as OngoingCallModel.InCall).isAppVisible) return false
+
+ // The app was and remains visible, but the transition state has changed. A launch or
+ // return animation has been requested or is ongoing. Keep the chip showing.
+ if (
+ newTransitionState is TransitionState.LaunchRequested ||
+ newTransitionState is TransitionState.Launching ||
+ newTransitionState is TransitionState.ReturnRequested ||
+ newTransitionState is TransitionState.Returning
+ ) {
+ return false
+ }
+
+ // The app was and remains visible, so we generally want to hide the chip. The only
+ // exception is if a return transition has just ended. In this case, the transition
+ // state changes shortly before the app visibility does. If we hide the chip between
+ // these two updates, this results in a flicker. We bridge the gap by keeping the chip
+ // showing.
+ return oldTransitionState != TransitionState.Returning
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 06656b7..1a802d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -200,8 +200,8 @@
colors,
startTimeMs = this.promotedContent.time.elapsedRealtimeMillis,
isEventInFuture = this.promotedContent.time.isCountDown,
- onClickListenerLegacy,
- clickBehavior,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
index f650d8d..fa8d256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
@@ -78,6 +78,7 @@
rememberChronometerState(
eventTimeMillis = viewModel.startTimeMs,
isCountDown = viewModel.isEventInFuture,
+ timeSource = viewModel.timeSource,
)
timerState.currentTimeText?.let { text ->
Text(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 4edb23d..58d3890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -42,6 +42,7 @@
import com.android.systemui.common.ui.compose.load
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -90,6 +91,8 @@
},
borderStroke = borderStroke,
onClick = onClick,
+ useModifierBasedImplementation = StatusBarChipsReturnAnimations.isEnabled,
+ transitionControllerFactory = model.transitionManager?.controllerFactory,
) {
ChipBody(model, iconViewStore, isClickable = onClick != null)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
index 407849b..700e6d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
@@ -21,12 +21,14 @@
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
@@ -36,6 +38,18 @@
iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
modifier: Modifier = Modifier,
) {
+ if (StatusBarChipsReturnAnimations.isEnabled) {
+ SideEffect {
+ // Active chips must always be capable of animating to/from activities, even when they
+ // are hidden. Therefore we always register their transitions.
+ for (chip in chips.active) chip.transitionManager?.registerTransition?.invoke()
+ // Inactive chips and chips in the overflow are never shown, so they must not have any
+ // registered transition.
+ for (chip in chips.overflow) chip.transitionManager?.unregisterTransition?.invoke()
+ for (chip in chips.inactive) chip.transitionManager?.unregisterTransition?.invoke()
+ }
+ }
+
val shownChips = chips.active.filter { !it.isHidden }
if (shownChips.isNotEmpty()) {
Row(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 106a363..3876d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -18,12 +18,15 @@
import android.annotation.CurrentTimeMillisLong
import android.annotation.ElapsedRealtimeLong
+import android.os.SystemClock
import android.view.View
+import com.android.systemui.animation.ComposableControllerFactory
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ui.viewmodel.TimeSource
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
/** Model representing the display of an ongoing activity as a chip in the status bar. */
@@ -31,6 +34,9 @@
/** Condensed name representing the model, used for logs. */
abstract val logName: String
+ /** Object used to manage the behavior of this chip during activity launch and returns. */
+ abstract val transitionManager: TransitionManager?
+
/**
* This chip shouldn't be shown.
*
@@ -38,7 +44,10 @@
* animated, and false if that transition should *not* be animated (i.e. the chip view should
* immediately disappear).
*/
- data class Inactive(val shouldAnimate: Boolean = true) : OngoingActivityChipModel() {
+ data class Inactive(
+ val shouldAnimate: Boolean = true,
+ override val transitionManager: TransitionManager? = null,
+ ) : OngoingActivityChipModel() {
override val logName = "Inactive(anim=$shouldAnimate)"
}
@@ -59,6 +68,7 @@
open val onClickListenerLegacy: View.OnClickListener?,
/** Data class that determines how clicks on the chip should be handled. */
open val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager?,
/**
* Whether this chip should be hidden. This can be the case depending on system states (like
* which apps are in the foreground and whether there is an ongoing transition.
@@ -75,6 +85,7 @@
override val colors: ColorsModel,
override val onClickListenerLegacy: View.OnClickListener?,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -84,6 +95,7 @@
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -105,6 +117,13 @@
* [android.widget.Chronometer.setBase].
*/
@ElapsedRealtimeLong val startTimeMs: Long,
+
+ /**
+ * The [TimeSource] that should be used to track the current time for this timer. Should
+ * be compatible with [startTimeMs].
+ */
+ val timeSource: TimeSource = TimeSource { SystemClock.elapsedRealtime() },
+
/**
* True if this chip represents an event starting in the future and false if this chip
* represents an event that has already started. If true, [startTimeMs] should be in the
@@ -113,6 +132,7 @@
val isEventInFuture: Boolean = false,
override val onClickListenerLegacy: View.OnClickListener?,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -122,6 +142,7 @@
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -148,6 +169,7 @@
@CurrentTimeMillisLong val time: Long,
override val onClickListenerLegacy: View.OnClickListener?,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -157,6 +179,7 @@
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -176,6 +199,7 @@
override val colors: ColorsModel,
/** The number of seconds until an event is started. */
val secondsUntilStarted: Long,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -185,6 +209,7 @@
colors,
onClickListenerLegacy = null,
clickBehavior = ClickBehavior.None,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -200,6 +225,7 @@
val text: String,
override val onClickListenerLegacy: View.OnClickListener? = null,
override val clickBehavior: ClickBehavior,
+ override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
) :
@@ -209,6 +235,7 @@
colors,
onClickListenerLegacy,
clickBehavior,
+ transitionManager,
isHidden,
shouldAnimate,
) {
@@ -262,4 +289,17 @@
/** Clicking the chip will show the heads up notification associated with the chip. */
data class ShowHeadsUpNotification(val onClick: () -> Unit) : ClickBehavior
}
+
+ /** Defines the behavior of the chip with respect to activity launch and return transitions. */
+ data class TransitionManager(
+ /** The factory used to create the controllers that animate the chip. */
+ val controllerFactory: ComposableControllerFactory? = null,
+ /**
+ * Used to create a registration for this chip using [controllerFactory]. Must be
+ * idempotent.
+ */
+ val registerTransition: () -> Unit = {},
+ /** Used to remove the existing registration for this chip, if any. */
+ val unregisterTransition: () -> Unit = {},
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
index 7402583..0fc7f82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
import android.annotation.ElapsedRealtimeLong
-import android.os.SystemClock
import android.text.format.DateUtils.formatElapsedTime
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -90,7 +89,7 @@
fun rememberChronometerState(
eventTimeMillis: Long,
isCountDown: Boolean,
- timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
+ timeSource: TimeSource,
): ChronometerState {
val state =
remember(timeSource, eventTimeMillis, isCountDown) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
index 3168a22..cb1002a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.data.repository
import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.app.displaylib.PerDisplayInstanceProvider
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.displaylib.SingleInstanceRepositoryImpl
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.ConfigurationStateImpl
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
-import com.android.systemui.display.data.repository.PerDisplayInstanceProvider
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
-import com.android.systemui.display.data.repository.SingleInstanceRepositoryImpl
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import dagger.Lazy
import dagger.Module
@@ -39,7 +39,6 @@
private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
private val factory: ConfigurationStateImpl.Factory,
) : PerDisplayInstanceProvider<ConfigurationState> {
-
override fun createInstance(displayId: Int): ConfigurationState? {
val displayWindowProperties =
displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 8be9e41..fcdcc3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -105,7 +105,7 @@
mBubblesOptional.get().collapseStack();
}
} else {
- if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
+ if (!row.getEntryLegacy().isBubble() && mBubblesOptional.isPresent()) {
mBubblesOptional.get().collapseStack();
}
}
@@ -130,7 +130,7 @@
} else {
row.setBubbleClickListener(v ->
mNotificationActivityStarter.onNotificationBubbleIconClicked(
- row.getEntry()));
+ row.getEntryLegacy()));
}
row.setOnClickListener(this);
row.setOnDragSuccessListener(mOnDragSuccessListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 874a059..8163128 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -74,7 +74,6 @@
const val ANIMATION_DURATION_TOP_ROUNDING = 100L
}
- private val notificationEntry = notification.entry
private val notificationKey = notification.key
override val isLaunching: Boolean = true
@@ -160,7 +159,7 @@
private val headsUpNotificationRow: ExpandableNotificationRow?
get() {
val pipelineParent = if (NotificationBundleUi.isEnabled)
- notification.entryAdapter?.parent else notificationEntry.parent
+ notification.entryAdapter?.parent else notification.entryLegacy.parent
val summaryEntry = (pipelineParent as? GroupEntry)?.summary
return when {
headsUpManager.isHeadsUpEntry(notificationKey) -> notification
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 26c302b..b1a26af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -113,6 +113,10 @@
return false
}
+ override fun isPromotedOngoing(): Boolean {
+ return false
+ }
+
override fun isFullScreenCapable(): Boolean {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 3118ce5..4299825 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -132,6 +132,11 @@
boolean isAmbient();
+ /**
+ * Returns whether this row represents promoted ongoing notification.
+ */
+ boolean isPromotedOngoing();
+
default boolean isFullScreenCapable() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index caa7abb..8f7f61f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -34,7 +34,7 @@
}
/**
- * The SystemClock.uptimeMillis() when this object was created. In general, this means the
+ * The SystemClock.elapsedRealtime() when this object was created. In general, this means the
* moment when NotificationManager notifies our listener about the existence of this entry.
*
* This value will not change if the notification is updated, although it will change if the
@@ -65,13 +65,4 @@
@Nullable public PipelineEntry getPreviousParent() {
return mPreviousAttachState.getParent();
}
-
- /**
- * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
- * fresh attach state (all entries will be null/default-initialized).
- */
- void beginNewAttachState() {
- mPreviousAttachState.clone(mAttachState);
- mAttachState.reset();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9795edf..b7fe39e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -522,7 +522,7 @@
}
private void onNotificationsInitialized() {
- mInitializedTimestamp = mClock.uptimeMillis();
+ mInitializedTimestamp = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
}
private void postNotification(
@@ -532,7 +532,8 @@
if (entry == null) {
// A new notification!
- entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+ entry = new NotificationEntry(sbn, ranking,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
mEventQueue.add(new InitEntryEvent(entry));
mEventQueue.add(new BindEntryEvent(entry, sbn));
mNotificationSet.put(sbn.getKey(), entry);
@@ -861,7 +862,7 @@
// messages from system server.
private void crashIfNotInitializing(RuntimeException exception) {
final boolean isRecentlyInitialized = mInitializedTimestamp == 0
- || mClock.uptimeMillis() - mInitializedTimestamp
+ || UseElapsedRealtimeForCreationTime.getCurrentTime(mClock) - mInitializedTimestamp
< INITIALIZATION_FORGIVENESS_WINDOW;
if (isRecentlyInitialized) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
index 1f8d365..698fed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
@@ -89,9 +89,9 @@
return true
}
- // Using uptimeMillis since it's guaranteed to be monotonic, as we don't want a
+ // Using elapsedRealtime since it's guaranteed to be monotonic, as we don't want a
// timezone/clock change to break us
- val now = systemClock.uptimeMillis()
+ val now = UseElapsedRealtimeForCreationTime.getCurrentTime(systemClock)
// Cannot purge the same entry from two threads simultaneously
synchronized(key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d031d83..4558017 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -251,7 +251,7 @@
/**
* @param sbn the StatusBarNotification from system server
* @param ranking also from system server
- * @param creationTime SystemClock.uptimeMillis of when we were created
+ * @param creationTime SystemClock.elapsedRealtime of when we were created
*/
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@@ -508,7 +508,7 @@
ArrayList<NotificationEntry> children = new ArrayList<>();
for (ExpandableNotificationRow child : rowChildren) {
- children.add(child.getEntry());
+ children.add(child.getEntryLegacy());
}
return children;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 1168c64..345b6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -137,6 +137,10 @@
return entry.ranking.isAmbient
}
+ override fun isPromotedOngoing(): Boolean {
+ return entry.isPromotedOngoing
+ }
+
override fun isFullScreenCapable(): Boolean {
return entry.sbn.notification.fullScreenIntent != null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index 872cd68..e9c4efc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -101,4 +101,13 @@
public void setBucket(@PriorityBucket int bucket) {
mBucket = bucket;
}
+
+ /**
+ * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
+ * fresh attach state (all entries will be null/default-initialized).
+ */
+ void beginNewAttachState() {
+ mPreviousAttachState.clone(mAttachState);
+ mAttachState.reset();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 238ba8d..5cea821 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -289,6 +289,9 @@
mIdToBundleEntry.clear();
for (String id: mNotifBundler.getBundleIds()) {
+ if (BundleCoordinator.debugBundleUi) {
+ Log.i(TAG, "create BundleEntry with id: " + id);
+ }
mIdToBundleEntry.put(id, new BundleEntry(id));
}
}
@@ -562,6 +565,11 @@
entry.beginNewAttachState();
}
+ for (BundleEntry be : mIdToBundleEntry.values()) {
+ be.beginNewAttachState();
+ // TODO(b/399736937) Clear bundle children
+ // BundleEntry has not representative summary so we do not need to clear it here.
+ }
mNotifList.clear();
}
@@ -570,7 +578,7 @@
List<PipelineEntry> out,
List<NotifFilter> filters) {
Trace.beginSection("ShadeListBuilder.filterNotifs");
- final long now = mSystemClock.uptimeMillis();
+ final long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock);
for (PipelineEntry entry : entries) {
if (entry instanceof GroupEntry) {
final GroupEntry groupEntry = (GroupEntry) entry;
@@ -614,7 +622,8 @@
GroupEntry group = mGroups.get(topLevelKey);
if (group == null) {
- group = new GroupEntry(topLevelKey, mSystemClock.uptimeMillis());
+ group = new GroupEntry(topLevelKey,
+ UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock));
mGroups.put(topLevelKey, group);
}
if (group.getParent() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
new file mode 100644
index 0000000..23f90f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Flags
+import com.android.systemui.util.time.SystemClock
+
+/** A helper class for replacing uptimeMillis with elapsedRealtime for entry creation times */
+public object UseElapsedRealtimeForCreationTime {
+ @JvmStatic
+ fun getCurrentTime(clock: SystemClock): Long {
+ if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+ return clock.elapsedRealtime()
+ }
+ return clock.uptimeMillis()
+ }
+
+ @JvmStatic
+ fun getCurrentTime(): Long {
+ if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+ return android.os.SystemClock.elapsedRealtime()
+ }
+ return android.os.SystemClock.uptimeMillis()
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index 2eec68b..fb7772e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
@@ -25,7 +25,7 @@
* Represents a set of notification post events for a particular notification group.
*/
public class EventBatch {
- /** SystemClock.uptimeMillis() */
+ /** SystemClock.elapsedRealtime() */
final long mCreatedTimestamp;
/** SBN.getGroupKey -- same for all members */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 96b3542..944e313 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -182,11 +183,12 @@
private void maybeEmitBatch(StatusBarNotification sbn) {
final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
final EventBatch batch = mBatches.get(sbn.getGroupKey());
+ long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
if (event != null) {
mLogger.logEarlyEmit(sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey);
emitBatch(requireNonNull(event.getBatch()));
} else if (batch != null
- && mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
+ && now - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
mLogger.logMaxBatchTimeout(sbn.getKey(), batch.mGroupKey);
emitBatch(batch);
}
@@ -228,7 +230,8 @@
private EventBatch getOrBuildBatch(final String groupKey) {
EventBatch batch = mBatches.get(groupKey);
if (batch == null) {
- batch = new EventBatch(mClock.uptimeMillis(), groupKey);
+ batch = new EventBatch(UseElapsedRealtimeForCreationTime.getCurrentTime(mClock),
+ groupKey);
mBatches.put(groupKey, batch);
}
return batch;
@@ -268,7 +271,8 @@
}
events.sort(mEventComparator);
- long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+ long batchAge = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock)
+ - batch.mCreatedTimestamp;
mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
mHandler.onNotificationBatchPosted(events);
@@ -298,7 +302,7 @@
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- long now = mClock.uptimeMillis();
+ long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
int eventCount = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index 8833ff1..4478d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -98,9 +98,14 @@
object : NotifBundler("NotifBundler") {
// Use list instead of set to keep fixed order
- override val bundleIds: List<String> = SYSTEM_RESERVED_IDS
+ override val bundleIds: List<String> =
+ if (debugBundleUi) SYSTEM_RESERVED_IDS + "notify"
+ else SYSTEM_RESERVED_IDS
override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+ if (debugBundleUi && entry?.key?.contains("notify") == true) {
+ return "notify"
+ }
return entry?.representativeEntry?.channel?.id?.takeIf { it in this.bundleIds }
}
}
@@ -110,4 +115,9 @@
pipeline.setNotifBundler(bundler)
}
}
+
+ companion object {
+ @kotlin.jvm.JvmField
+ var debugBundleUi: Boolean = true
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index b54f21b..1be415d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -241,8 +241,7 @@
isMemberOfDelayedGroup = shouldWaitForGroupToInflate(parent, now);
mIsDelayedGroupCache.put(parent, isMemberOfDelayedGroup);
}
-
- return !isInflated(entry) || isMemberOfDelayedGroup;
+ return !isInflated(entry) || (isMemberOfDelayedGroup != null && isMemberOfDelayedGroup);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index a0a8671..f43767d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -22,7 +22,6 @@
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -147,9 +146,7 @@
traceSection("updateNotifOnUiModeChanged") {
mPipeline?.allNotifs?.forEach { entry ->
entry.row?.onUiModeChanged()
- if (Flags.notificationUndoGutsOnConfigChanged()) {
- mGutsManager.closeAndUndoGuts()
- }
+ mGutsManager.closeAndUndoGuts()
}
}
}
@@ -158,16 +155,7 @@
colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
- if (Flags.notificationUndoGutsOnConfigChanged()) {
- mGutsManager.closeAndUndoGuts()
- } else {
- // This property actually gets reset when the guts are re-inflated, so we're never
- // actually calling onDensityOrFontScaleChanged below.
- val exposedGuts = entry.areGutsExposed()
- if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry)
- }
- }
+ mGutsManager.closeAndUndoGuts()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index bdbdc53..0466c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -39,9 +39,9 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
@@ -113,6 +113,8 @@
@VisibleForTesting
protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500;
+ private final boolean mCheckLockScreenTransitionEnabled = Flags.checkLockscreenGoneTransition();
+
@Inject
public VisualStabilityCoordinator(
@Background DelayableExecutor delayableExecutor,
@@ -182,7 +184,7 @@
this::onTrackingHeadsUpModeChanged);
}
- if (Flags.checkLockscreenGoneTransition()) {
+ if (mCheckLockScreenTransitionEnabled) {
if (SceneContainerFlag.isEnabled()) {
mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.isInTransition(
Edge.create(KeyguardState.LOCKSCREEN, Scenes.Gone), null),
@@ -437,7 +439,7 @@
boolean wasReorderingAllowed = mReorderingAllowed;
// No need to run notification pipeline when the lockscreen is in fading animation.
mPipelineRunAllowed = !(isPanelCollapsingOrLaunchingActivity()
- || (Flags.checkLockscreenGoneTransition() && mLockscreenInGoneTransition));
+ || (mCheckLockScreenTransitionEnabled && mLockscreenInGoneTransition));
mReorderingAllowed = isReorderingAllowed();
if (wasPipelineRunAllowed != mPipelineRunAllowed
|| wasReorderingAllowed != mReorderingAllowed) {
@@ -499,7 +501,7 @@
* notification and we are reordering based on the user's change.
*
* @param entry notification entry that can change sections even if isReorderingAllowed is false
- * @param now current time SystemClock.uptimeMillis
+ * @param now current time SystemClock.elapsedRealtime
*/
public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) {
final String entryKey = entry.getKey();
@@ -566,7 +568,7 @@
pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
pw.println(" notifPanelCollapsing: " + mNotifPanelCollapsing);
pw.println(" launchingNotifActivity: " + mNotifPanelLaunchingActivity);
- if (Flags.checkLockscreenGoneTransition()) {
+ if (mCheckLockScreenTransitionEnabled) {
pw.println(" lockscreenInGoneTransition: " + mLockscreenInGoneTransition);
}
pw.println("reorderingAllowed: " + mReorderingAllowed);
@@ -627,7 +629,7 @@
}
private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
- if (!Flags.checkLockscreenGoneTransition()) {
+ if (!mCheckLockScreenTransitionEnabled) {
return;
}
if (inGoneTransition == mLockscreenInGoneTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 07fa6ae..03b4076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -20,7 +20,6 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import android.os.SystemClock;
import android.service.notification.NotificationStats;
import androidx.annotation.NonNull;
@@ -30,6 +29,7 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -85,7 +85,7 @@
public void onImportanceChanged(NotificationEntry entry) {
mVisualStabilityCoordinator.temporarilyAllowSectionChanges(
entry,
- SystemClock.uptimeMillis());
+ UseElapsedRealtimeForCreationTime.getCurrentTime());
}
@NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
index 776c7d5..389bb31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -41,8 +41,8 @@
* this entry will not have any grouping nor sorting information.
* If this filter is registered via {@link NotifPipeline#addFinalizeFilter},
* this entry will have grouping and sorting information.
- * @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
- * pipeline execution. This value will be the same for all pluggable calls made
+ * @param now A timestamp in SystemClock.elapsedRealtime that represents "now" for the purposes
+ * of pipeline execution. This value will be the same for all pluggable calls made
* during this pipeline run, giving pluggables a stable concept of "now" to compare
* various entries against.
* @return True if the notif should be removed from the list
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 8021d8f..a552ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -234,21 +234,21 @@
fun getChildCount(): Int = controller.getChildCount()
fun addChildAt(child: ShadeNode, index: Int) {
- traceSection("ShadeNode#addChildAt") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#addChildAt" }) {
controller.addChildAt(child.controller, index)
child.controller.onViewAdded()
}
}
fun moveChildTo(child: ShadeNode, index: Int) {
- traceSection("ShadeNode#moveChildTo") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#moveChildTo" }) {
controller.moveChildTo(child.controller, index)
child.controller.onViewMoved()
}
}
fun removeChild(child: ShadeNode, isTransfer: Boolean) {
- traceSection("ShadeNode#removeChild") {
+ traceSection({ "ShadeNode#${controller::class.simpleName}#removeChild" }) {
controller.removeChild(child.controller, isTransfer)
child.controller.onViewRemoved()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index cdbe0fd..8d1e611 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -81,7 +81,7 @@
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(emptySet())
} else {
- activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }
+ activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }.distinctUntilChanged()
}
}
@@ -90,9 +90,9 @@
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(emptySet())
} else {
- activeHeadsUpRows.map {
- it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet()
- }
+ activeHeadsUpRows
+ .map { it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet() }
+ .distinctUntilChanged() // TODO(b/402428276) stop sending duplicate updates instead
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index 147a5af..619d48f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -18,9 +18,9 @@
import android.view.Display
import androidx.lifecycle.lifecycleScope
+import com.android.app.displaylib.PerDisplayRepository
import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index ec8fbc0..5bdd769 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -19,7 +19,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -44,6 +43,7 @@
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.UpdateSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -112,7 +112,7 @@
@Override
public void run() {
- mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
+ mLastVisibilityReportUptimeMs = UseElapsedRealtimeForCreationTime.getCurrentTime();
// 1. Loop over active entries:
// A. Keep list of visible notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 2a01a14..777392d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -19,19 +19,26 @@
import android.app.Flags
import android.app.Flags.notificationsRedesignTemplates
import android.app.Notification
+import android.content.Context
import android.graphics.PorterDuff
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
+import android.view.View.MeasureSpec.AT_MOST
+import android.view.View.MeasureSpec.EXACTLY
+import android.view.View.MeasureSpec.UNSPECIFIED
+import android.view.View.MeasureSpec.makeMeasureSpec
import android.view.View.VISIBLE
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewStub
import android.widget.Chronometer
import android.widget.DateTimeView
+import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
+import androidx.annotation.DimenRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
@@ -42,7 +49,9 @@
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
@@ -88,22 +97,12 @@
}
key(content.identity) {
- val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
- val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
-
- val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
-
- val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
- val borderShape = RoundedCornerShape(borderRadius)
-
- Box(modifier = modifier.padding(sidePaddingValues)) {
- AODPromotedNotificationView(
- layoutResource = layoutResource,
- content = content,
- audiblyAlertedIconVisible = audiblyAlertedIconVisible,
- modifier = Modifier.border(borderStroke, borderShape),
- )
- }
+ AODPromotedNotificationView(
+ layoutResource = layoutResource,
+ content = content,
+ audiblyAlertedIconVisible = audiblyAlertedIconVisible,
+ modifier = modifier,
+ )
}
}
@@ -114,27 +113,91 @@
audiblyAlertedIconVisible: Boolean,
modifier: Modifier = Modifier,
) {
- AndroidView(
- factory = { context ->
- val view =
- traceSection("$TAG.inflate") {
- LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
- }
+ val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
+ val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
- val updater =
- traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(view) }
+ val boxModifier = modifier.padding(sidePaddingValues)
- view.setTag(viewUpdaterTagId, updater)
+ val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
- view
- },
- update = { view ->
- val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+ val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
+ val borderShape = RoundedCornerShape(borderRadius)
- traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
- },
- modifier = modifier,
- )
+ val maxHeight =
+ with(LocalDensity.current) {
+ scaledFontHeight(systemuiR.dimen.notification_max_height_for_promoted_ongoing)
+ .toPx()
+ }
+ .toInt()
+
+ val viewModifier = Modifier.border(borderStroke, borderShape)
+
+ Box(modifier = boxModifier) {
+ AndroidView(
+ factory = { context ->
+ val notif =
+ traceSection("$TAG.inflate") {
+ LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
+ }
+ val updater =
+ traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notif) }
+
+ val frame = FrameLayoutWithMaxHeight(maxHeight, context)
+ frame.addView(notif)
+ frame.setTag(viewUpdaterTagId, updater)
+
+ frame
+ },
+ update = { frame ->
+ val updater = frame.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+
+ traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
+ frame.maxHeight = maxHeight
+ },
+ modifier = viewModifier,
+ )
+ }
+}
+
+private class FrameLayoutWithMaxHeight(maxHeight: Int, context: Context) : FrameLayout(context) {
+ var maxHeight = maxHeight
+ set(value) {
+ if (field != value) {
+ field = value
+ requestLayout()
+ }
+ }
+
+ // This mirrors the logic in NotificationContentView.onMeasure.
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (childCount < 1) {
+ return
+ }
+
+ val child = getChildAt(0)
+ val childLayoutHeight = child.layoutParams.height
+ val childHeightSpec =
+ if (childLayoutHeight >= 0) {
+ makeMeasureSpec(maxHeight.coerceAtMost(childLayoutHeight), EXACTLY)
+ } else {
+ makeMeasureSpec(maxHeight, AT_MOST)
+ }
+ measureChildWithMargins(child, widthMeasureSpec, 0, childHeightSpec, 0)
+ val childMeasuredHeight = child.measuredHeight
+
+ val ownHeightMode = MeasureSpec.getMode(heightMeasureSpec)
+ val ownHeightSize = MeasureSpec.getSize(heightMeasureSpec)
+
+ val ownMeasuredWidth = MeasureSpec.getSize(widthMeasureSpec)
+ val ownMeasuredHeight =
+ if (ownHeightMode != UNSPECIFIED) {
+ childMeasuredHeight.coerceAtMost(ownHeightSize)
+ } else {
+ childMeasuredHeight
+ }
+
+ setMeasuredDimension(ownMeasuredWidth, ownMeasuredHeight)
+ }
}
private val PromotedNotificationContentModel.layoutResource: Int?
@@ -521,6 +584,11 @@
val brush = SolidColor(androidx.compose.ui.graphics.Color(colorInt))
}
+@Composable
+private fun scaledFontHeight(@DimenRes dimenId: Int): Dp {
+ return dimensionResource(dimenId) * LocalDensity.current.fontScale.coerceAtLeast(1f)
+}
+
private val viewUpdaterTagId = systemuiR.id.aod_promoted_notification_view_updater_tag
private const val TAG = "AODPromotedNotification"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
new file mode 100644
index 0000000..1429535
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class which can receive both a demotion signal and a single handler of that signal */
+@SysUISingleton
+class PackageDemotionInteractor @Inject constructor() {
+ private var demotionSignalHandler: ((packageName: String, uid: Int) -> Unit)? = null
+
+ /**
+ * called after sending a the demotion signal to
+ * [android.app.INotificationManager.setCanBePromoted]
+ */
+ fun onPackageDemoted(packageName: String, uid: Int) {
+ demotionSignalHandler?.invoke(packageName, uid)
+ }
+
+ /** sets the [handler] that will be called when [onPackageDemoted] is called. */
+ fun setOnPackageDemotionHandler(handler: (packageName: String, uid: Int) -> Unit) {
+ demotionSignalHandler = handler
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 6892226..e768673 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -40,6 +40,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
+import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.common.shared.colors.SurfaceEffectColors;
import com.android.systemui.res.R;
@@ -101,7 +102,8 @@
private ValueAnimator mBackgroundColorAnimator;
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
- private int mNormalColor;
+ protected int mNormalColor;
+ protected int mOpaqueColor;
private boolean mIsBelowSpeedBump;
private long mLastActionUpTime;
@@ -130,17 +132,13 @@
protected void updateColors() {
if (notificationRowTransparency()) {
- if (mIsBlurSupported) {
- mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
- } else {
- mNormalColor = mContext.getColor(
- com.android.internal.R.color.materialColorSurfaceContainer);
- }
+ mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ mOpaqueColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainer);
} else {
mNormalColor = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceContainerHigh);
}
- setBackgroundToNormalColor();
mTintedRippleColor = mContext.getColor(
R.color.notification_ripple_tinted_color);
mNormalRippleColor = mContext.getColor(
@@ -151,12 +149,6 @@
mOverrideAmount = 0.0f;
}
- private void setBackgroundToNormalColor() {
- if (mBackgroundNormal != null) {
- mBackgroundNormal.setNormalColor(mNormalColor);
- }
- }
-
/**
* Reload background colors from resources and invalidate views.
*/
@@ -186,7 +178,6 @@
mBackgroundNormal = findViewById(R.id.backgroundNormal);
mFakeShadow = findViewById(R.id.fake_shadow);
mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
- setBackgroundToNormalColor();
initBackground();
updateBackgroundTint();
updateOutlineAlpha();
@@ -352,7 +343,7 @@
}
protected boolean usesTransparentBackground() {
- return mIsBlurSupported && notificationRowTransparency();
+ return false;
}
@Override
@@ -709,7 +700,11 @@
if (withTint && mBgTint != NO_COLOR) {
return mBgTint;
} else {
- return mNormalColor;
+ if (Flags.notificationRowTransparency()) {
+ return usesTransparentBackground() ? mNormalColor : mOpaqueColor;
+ } else {
+ return mNormalColor;
+ }
}
}
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 8da2f76..2c3676a 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
@@ -82,6 +82,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -421,9 +422,10 @@
}
onExpansionChanged(true /* userAction */, wasExpanded);
} else {
- final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
- boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ final boolean wasExpanded =
+ mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
+ boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(getEntryLegacy());
+ mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
if (shouldLogExpandClickMetric) {
mMetricsLogger.action(
MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
@@ -453,7 +455,7 @@
if (NotificationBundleUi.isEnabled()) {
mOnExpandClickListener.onExpandClicked(this, mEntryAdapter, nowExpanded);
} else {
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
}
if (shouldLogExpandClickMetric) {
mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
@@ -558,7 +560,7 @@
if (NotificationBundleUi.isEnabled()) {
return mKey;
} else {
- return mEntry.getKey();
+ return getEntryLegacy().getKey();
}
}
@@ -661,14 +663,14 @@
*/
public boolean getIsNonblockable() {
NotificationBundleUi.assertInLegacyMode();
- if (mEntry == null) {
+ if (getEntryLegacy() == null) {
return true;
}
- return !mEntry.isBlockable();
+ return !getEntryLegacy().isBlockable();
}
private boolean isConversation() {
- return mPeopleNotificationIdentifier.getPeopleNotificationType(mEntry)
+ return mPeopleNotificationIdentifier.getPeopleNotificationType(getEntry())
!= PeopleNotificationIdentifier.TYPE_NON_PERSON;
}
@@ -679,7 +681,7 @@
Trace.beginSection("ExpNotRow#onNotifUpdated (leaf)");
}
for (NotificationContentView l : mLayouts) {
- l.onNotificationUpdated(mEntry);
+ l.onNotificationUpdated(getEntry());
}
mShowingPublicInitialized = false;
if (mMenuRow != null) {
@@ -746,7 +748,7 @@
*/
public void updateBubbleButton() {
for (NotificationContentView l : mLayouts) {
- l.updateBubbleButton(mEntry);
+ l.updateBubbleButton(getEntry());
}
}
@@ -777,7 +779,7 @@
return mEntryAdapter.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
getBackgroundColorWithoutTint());
} else {
- return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+ return getEntryLegacy().getContrastedColor(mContext, mIsMinimized && !isExpanded(),
getBackgroundColorWithoutTint());
}
}
@@ -885,7 +887,7 @@
if (NotificationBundleUi.isEnabled()) {
targetSdk = mEntryAdapter.getTargetSdk();
} else {
- targetSdk = mEntry.targetSdk;
+ targetSdk = getEntryLegacy().targetSdk;
}
boolean beforeN = targetSdk < Build.VERSION_CODES.N;
@@ -901,7 +903,7 @@
if (NotificationBundleUi.isEnabled()) {
summarization = mEntryAdapter.getSummarization();
} else {
- summarization = mEntry.getRanking().getSummarization();
+ summarization = getEntryLegacy().getRanking().getSummarization();
}
if (customView && beforeS && !mIsSummaryWithChildren) {
@@ -945,7 +947,25 @@
layout.setHeights(smallHeight, headsUpHeight, maxExpandedHeight);
}
+ /**
+ * Check {@link NotificationBundleUi#isEnabled()}
+ * and use {@link #getEntryAdapter()} when true
+ * and {@link #getEntryLegacy()} when false.
+ */
@NonNull
+ @Deprecated
+ public NotificationEntry getEntryLegacy() {
+ NotificationBundleUi.assertInLegacyMode();
+ return mEntry;
+ }
+
+ /**
+ * Check {@link NotificationBundleUi#isEnabled()}
+ * and use {@link #getEntryAdapter()} when true
+ * and {@link #getEntryLegacy()} when false.
+ */
+ @NonNull
+ @Deprecated
public NotificationEntry getEntry() {
return mEntry;
}
@@ -979,7 +999,7 @@
} else if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
- updateColors();
+ updateBackgroundTint();
}
/**
@@ -1481,8 +1501,8 @@
public void setBubbleClickListener(@Nullable OnClickListener l) {
mBubbleClickListener = l;
// ensure listener is passed to the content views
- mPrivateLayout.updateBubbleButton(mEntry);
- mPublicLayout.updateBubbleButton(mEntry);
+ mPrivateLayout.updateBubbleButton(getEntry());
+ mPublicLayout.updateBubbleButton(getEntry());
}
/**
@@ -1554,7 +1574,7 @@
return initializationTime != -1
&& SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
} else {
- return getEntry().hasFinishedInitialization();
+ return getEntryLegacy().hasFinishedInitialization();
}
}
@@ -1633,14 +1653,14 @@
if (NotificationBundleUi.isEnabled()) {
mEntryAdapter.prepareForInflation();
} else {
- mEntry.getSbn().clearPackageContext();
+ getEntryLegacy().getSbn().clearPackageContext();
}
// TODO: Move content inflation logic out of this call
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setNeedsReinflation(true);
var rebindEndCallback = mRebindingTracker.trackRebinding(NotificationBundleUi.isEnabled()
- ? mEntryAdapter.getKey() : mEntry.getKey());
+ ? mEntryAdapter.getKey() : getEntryLegacy().getKey());
mRowContentBindStage.requestRebind(mEntry, (e) -> rebindEndCallback.onFinished());
Trace.endSection();
}
@@ -1678,21 +1698,36 @@
@Override
protected void setBackgroundTintColor(int color) {
+ if (notificationRowTransparency()) {
+ boolean isColorized = false;
+ if (NotificationBundleUi.isEnabled()) {
+ if (mEntryAdapter != null) {
+ isColorized = mEntryAdapter.isColorized();
+ }
+ } else {
+ if (mEntry != null) {
+ isColorized = mEntry.getSbn().getNotification().isColorized();
+ }
+ }
+ boolean isTransparent = usesTransparentBackground();
+ if (isColorized) {
+ // For colorized notifications, use a color that matches the tint color at 90% alpha
+ // when the row is transparent.
+ color = ColorUtils.setAlphaComponent(
+ color, (int) (0xFF * (isTransparent ? 0.9f : 1)));
+ } else {
+ // For non-colorized notifications, use the semi-transparent normal color token
+ // when the row is transparent, and the opaque color token otherwise.
+ if (!isTransparent && mBgTint == NO_COLOR) {
+ color = mOpaqueColor;
+ }
+ }
+ }
super.setBackgroundTintColor(color);
NotificationContentView view = getShowingLayout();
if (view != null) {
view.setBackgroundTintColor(color);
}
- if (notificationRowTransparency() && mBackgroundNormal != null) {
- if (NotificationBundleUi.isEnabled() && mEntryAdapter != null) {
- mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized());
- } else {
- if (mEntry != null) {
- mBackgroundNormal.setBgIsColorized(
- mEntry.getSbn().getNotification().isColorized());
- }
- }
- }
}
public void closeRemoteInput() {
@@ -2310,7 +2345,9 @@
}
@VisibleForTesting
- protected void setEntry(NotificationEntry entry) {
+ @Deprecated
+ protected void setEntryLegacy(NotificationEntry entry) {
+ NotificationBundleUi.assertInLegacyMode();
mEntry = entry;
}
@@ -2403,7 +2440,7 @@
if (NotificationBundleUi.isEnabled()) {
return traceTag + "(" + getEntryAdapter().getStyle() + ")";
} else {
- return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+ return traceTag + "(" + getEntryLegacy().getNotificationStyle() + ")";
}
}
@@ -2908,7 +2945,7 @@
if (NotificationBundleUi.isEnabled()) {
return getEntryAdapter().getIcons().getShelfIcon();
} else {
- return mEntry.getIcons().getShelfIcon();
+ return getEntryLegacy().getIcons().getShelfIcon();
}
}
@@ -3016,7 +3053,7 @@
mGroupExpansionManager.setGroupExpanded(mEntryAdapter, userExpanded);
}
} else {
- mGroupExpansionManager.setGroupExpanded(mEntry, userExpanded);
+ mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), userExpanded);
}
onExpansionChanged(true /* userAction */, wasExpanded);
return;
@@ -3113,7 +3150,7 @@
mChildrenContainer.setOnKeyguard(onKeyguard);
}
}
- updateColors();
+ updateBackgroundTint();
}
}
@@ -3159,7 +3196,7 @@
public boolean canShowHeadsUp() {
boolean canEntryHun = NotificationBundleUi.isEnabled()
? mEntryAdapter.canPeek()
- : mEntry.isStickyAndNotDemoted();
+ : getEntryLegacy().isStickyAndNotDemoted();
if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
(!canEntryHun
|| (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
@@ -3181,13 +3218,13 @@
if (NotificationBundleUi.isEnabled()) {
return mGroupExpansionManager.isGroupExpanded(mEntryAdapter);
}
- return mGroupExpansionManager.isGroupExpanded(mEntry);
+ return mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
}
private boolean isGroupRoot() {
return NotificationBundleUi.isEnabled()
? mGroupMembershipManager.isGroupRoot(mEntryAdapter)
- : mGroupMembershipManager.isGroupSummary(mEntry);
+ : mGroupMembershipManager.isGroupSummary(getEntryLegacy());
}
private void onAttachedChildrenCountChanged() {
@@ -3209,7 +3246,8 @@
if (NotificationBundleUi.isEnabled()) {
mPublicLayout.setNotificationWhen(mEntryAdapter.getWhen());
} else {
- mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+ mPublicLayout.setNotificationWhen(
+ getEntryLegacy().getSbn().getNotification().getWhen());
}
}
getShowingLayout().updateBackgroundColor(false /* animate */);
@@ -3243,12 +3281,19 @@
return false;
}
- final NotificationEntry entry = mEntry;
- if (entry == null) {
- return false;
+ if (NotificationBundleUi.isEnabled()) {
+ final EntryAdapter entryAdapter = mEntryAdapter;
+ if (entryAdapter == null) {
+ return false;
+ }
+ return entryAdapter.isPromotedOngoing();
+ } else {
+ final NotificationEntry entry = mEntry;
+ if (entry == null) {
+ return false;
+ }
+ return entry.isPromotedOngoing();
}
-
- return entry.isPromotedOngoing();
}
private boolean isPromotedNotificationExpanded(boolean allowOnKeyguard) {
@@ -3537,7 +3582,8 @@
return mEntryAdapter.isClearable()
&& (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
} else {
- return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ return getEntryLegacy().isClearable()
+ && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
}
}
@@ -3551,7 +3597,7 @@
if (!NotificationBundleUi.isEnabled()) {
// this is only called if row.getParent() instanceof NotificationStackScrollLayout,
// so there is never a group to expand
- mGroupExpansionManager.setGroupExpanded(mEntry, true);
+ mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), true);
}
}
notifyHeightChanged(/* needsAnimation= */ false);
@@ -3784,7 +3830,7 @@
return true;
}
} else {
- if (getEntry().getSbn().getNotification().isColorized()) {
+ if (getEntryLegacy().getSbn().getNotification().isColorized()) {
return true;
}
}
@@ -4255,7 +4301,7 @@
public boolean isMediaRow() {
NotificationBundleUi.assertInLegacyMode();
- return mEntry.getSbn().getNotification().isMediaNotification();
+ return getEntryLegacy().getSbn().getNotification().isMediaNotification();
}
public void setAboveShelf(boolean aboveShelf) {
@@ -4377,11 +4423,7 @@
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
// Skip super call; dump viewState ourselves
- if (NotificationBundleUi.isEnabled()) {
- pw.println("Notification: " + mEntryAdapter.getKey());
- } else {
- pw.println("Notification: " + mEntry.getKey());
- }
+ pw.println("Notification: " + getKey());
DumpUtilsKt.withIncreasedIndent(pw, () -> {
pw.println(this);
pw.print("visibility: " + getVisibility());
@@ -4618,7 +4660,7 @@
if (NotificationBundleUi.isEnabled()) {
mLaunchAnimationRunning = launchAnimationRunning;
} else {
- getEntry().setExpandAnimationRunning(launchAnimationRunning);
+ getEntryLegacy().setExpandAnimationRunning(launchAnimationRunning);
}
}
@@ -4627,12 +4669,16 @@
if (NotificationBundleUi.isEnabled()) {
return mLaunchAnimationRunning;
} else {
- return getEntry().isExpandAnimationRunning();
+ return getEntryLegacy().isExpandAnimationRunning();
}
}
@Override
protected boolean usesTransparentBackground() {
- return super.usesTransparentBackground() && !mIsHeadsUp && !mOnKeyguard;
+ // Row background should be opaque when it's displayed as a heads-up notification or
+ // displayed on keyguard.
+ // TODO(b/388891313): Account for isBlurSupported when it is initialized and updated
+ // correctly.
+ return notificationRowTransparency() && !mIsHeadsUp && !mOnKeyguard;
}
}
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 ac55930..7c0ee668 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
@@ -139,7 +139,7 @@
}
final int viewUserId = NotificationBundleUi.isEnabled()
? mView.getEntryAdapter().getSbn().getUserId()
- : mView.getEntry().getSbn().getUserId();
+ : mView.getEntryLegacy().getSbn().getUserId();
if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
mView.getPrivateLayout().setBubblesEnabledForUser(
BUBBLES_SETTING_ENABLED_VALUE.equals(value));
@@ -395,7 +395,7 @@
mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
}
} else {
- mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+ mView.getEntryLegacy().setInitializationTime(mClock.elapsedRealtime());
mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
}
mPluginManager.addPluginListener(mView,
@@ -429,7 +429,9 @@
@Override
@NonNull
public String getNodeLabel() {
- return NotificationBundleUi.isEnabled() ? mView.getLoggingKey() : logKey(mView.getEntry());
+ return NotificationBundleUi.isEnabled()
+ ? mView.getLoggingKey()
+ : logKey(mView.getEntryLegacy());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 9ae2eb1..20b826a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -109,7 +109,7 @@
StatusBarNotification sn = NotificationBundleUi.isEnabled()
? enr.getEntryAdapter().getSbn()
- : enr.getEntry().getSbn();
+ : enr.getEntryLegacy().getSbn();
Notification notification = sn.getNotification();
final PendingIntent contentIntent = notification.contentIntent != null
? notification.contentIntent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4914e10..e4997e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,9 +36,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dumpable;
+import com.android.systemui.common.shared.colors.SurfaceEffectColors;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
import com.android.systemui.util.DrawableDumpKt;
@@ -52,7 +52,6 @@
public class NotificationBackgroundView extends View implements Dumpable,
ExpandableNotificationRow.DismissButtonTargetVisibilityListener {
- private static final int MAX_ALPHA = 0xFF;
private final boolean mDontModifyCorners;
private Drawable mBackground;
private int mClipTopAmount;
@@ -73,8 +72,6 @@
private final ColorStateList mLightColoredStatefulColors;
private final ColorStateList mDarkColoredStatefulColors;
private int mNormalColor;
- private boolean mBgIsColorized = false;
- private boolean mForceOpaque = false;
private final int convexR = 9;
private final int concaveR = 22;
@@ -88,13 +85,15 @@
R.color.notification_state_color_light);
mDarkColoredStatefulColors = getResources().getColorStateList(
R.color.notification_state_color_dark);
+ if (notificationRowTransparency()) {
+ mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+ } else {
+ mNormalColor = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
+ }
mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
}
- public void setNormalColor(int color) {
- mNormalColor = color;
- }
-
@Override
public void onTargetVisibilityChanged(boolean targetVisible) {
if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
@@ -140,21 +139,6 @@
}
}
- /**
- * A way to tell whether the background has been colorized.
- */
- public boolean isColorized() {
- return mBgIsColorized;
- }
-
- /**
- * A way to inform this class whether the background has been colorized.
- * We need to know this, in order to *not* override that color.
- */
- public void setBgIsColorized(boolean b) {
- mBgIsColorized = b;
- }
-
private Path calculateDismissButtonCutoutPath(Rect backgroundBounds) {
// TODO(b/365585705): Adapt to RTL after the UX design is finalized.
@@ -311,28 +295,21 @@
return ((LayerDrawable) mBackground).getDrawable(1);
}
- private void updateBaseLayerColor() {
- // BG base layer being a drawable, there isn't a method like setColor() to color it.
- // Instead, we set a color filter that essentially replaces every pixel of the drawable.
- // For non-colorized notifications, this function specifies a new color token.
- // For colorized notifications, this uses a color that matches the tint color at 90% alpha.
- int color = isColorized()
- ? ColorUtils.setAlphaComponent(mTintColor, (int) (MAX_ALPHA * 0.9f))
- : mNormalColor;
- getBaseBackgroundLayer().setColorFilter(
- new PorterDuffColorFilter(
- color,
- PorterDuff.Mode.SRC)); // SRC operator discards the drawable's color+alpha
- }
-
public void setTint(int tintColor) {
Drawable baseLayer = getBaseBackgroundLayer();
- baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
- baseLayer.setTint(tintColor);
- mTintColor = tintColor;
if (notificationRowTransparency()) {
- updateBaseLayerColor();
+ // BG base layer being a drawable, there isn't a method like setColor() to color it.
+ // Instead, we set a color filter that essentially replaces every pixel of the drawable.
+ baseLayer.setColorFilter(
+ new PorterDuffColorFilter(
+ tintColor,
+ // SRC operator discards the drawable's color+alpha
+ PorterDuff.Mode.SRC));
+ } else {
+ baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
+ baseLayer.setTint(tintColor);
}
+ mTintColor = tintColor;
setStatefulColors();
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index ff4b835..d97e25f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -473,7 +473,7 @@
result.newPublicView = createSensitiveContentMessageNotification(
NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn().getNotification()
- : row.getEntry().getSbn().getNotification(),
+ : row.getEntryLegacy().getSbn().getNotification(),
builder.getStyle(),
systemUiContext, packageContext).createContentView();
} else {
@@ -814,7 +814,7 @@
existingWrapper.onReinflated();
}
} catch (Exception e) {
- handleInflationError(runningInflations, e, row, callback, logger,
+ handleInflationError(runningInflations, e, row, entry, callback, logger,
"applying view synchronously");
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
@@ -836,7 +836,7 @@
String invalidReason = isValidView(v, entry, row.getResources());
if (invalidReason != null) {
handleInflationError(runningInflations, new InflationException(invalidReason),
- row, callback, logger, "applied invalid view");
+ row, entry, callback, logger, "applied invalid view");
runningInflations.remove(inflationId);
return;
}
@@ -873,7 +873,7 @@
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, row,
+ handleInflationError(runningInflations, e, row, entry,
callback, logger, "applying view");
}
}
@@ -969,13 +969,14 @@
private static void handleInflationError(
HashMap<Integer, CancellationSignal> runningInflations, Exception e,
- ExpandableNotificationRow row, @Nullable InflationCallback callback,
+ ExpandableNotificationRow row, NotificationEntry entry,
+ @Nullable InflationCallback callback,
NotificationRowContentBinderLogger logger, String logContext) {
Assert.isMainThread();
logger.logAsyncTaskException(row.getLoggingKey(), logContext, e);
runningInflations.values().forEach(CancellationSignal::cancel);
if (callback != null) {
- callback.handleInflationException(row.getEntry(), e);
+ callback.handleInflationException(entry, e);
}
}
@@ -1443,7 +1444,7 @@
+ Integer.toHexString(sbn.getId());
Log.e(CentralSurfaces.TAG, "couldn't inflate view for notification " + ident, e);
if (mCallback != null) {
- mCallback.handleInflationException(mRow.getEntry(),
+ mCallback.handleInflationException(mEntry,
new InflationException("Couldn't inflate contentViews" + e));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index b3357d0..26d318b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -269,6 +269,7 @@
mNotificationMaxHeight = maxHeight;
}
+ // This logic is mirrored in FrameLayoutWithMaxHeight.onMeasure in AODPromotedNotification.kt.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -600,7 +601,7 @@
if (NotificationBundleUi.isEnabled()) {
return mContainingNotification.getEntryAdapter().getSbn();
} else {
- return mContainingNotification.getEntry().getSbn();
+ return mContainingNotification.getEntryLegacy().getSbn();
}
}
@@ -1592,10 +1593,13 @@
return;
}
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
- View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
- ViewGroup actionListMarginTarget = layout.findViewById(
- com.android.internal.R.id.notification_action_list_margin_target);
- if (bubbleButton == null || actionContainer == null) {
+ // With the new design, the actions_container should always be visible to act as padding
+ // when there are no actions. We're making its child visible/invisible instead.
+ View actionsContainerForVisibilityChange = layout.findViewById(
+ notificationsRedesignTemplates()
+ ? com.android.internal.R.id.actions_container_layout
+ : com.android.internal.R.id.actions_container);
+ if (bubbleButton == null || actionsContainerForVisibilityChange == null) {
return;
}
@@ -1613,17 +1617,14 @@
bubbleButton.setImageDrawable(d);
bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
bubbleButton.setVisibility(VISIBLE);
- actionContainer.setVisibility(VISIBLE);
- // Set notification_action_list_margin_target's bottom margin to 0 when showing bubble
- if (actionListMarginTarget != null) {
- removeBottomMargin(actionListMarginTarget);
- }
- if (notificationsRedesignTemplates()) {
- // Similar treatment for smart reply margin
- LinearLayout smartReplyContainer = layout.findViewById(
- com.android.internal.R.id.smart_reply_container);
- if (smartReplyContainer != null) {
- removeBottomMargin(smartReplyContainer);
+ actionsContainerForVisibilityChange.setVisibility(VISIBLE);
+ if (!notificationsRedesignTemplates()) {
+ // Set notification_action_list_margin_target's bottom margin to 0 when showing
+ // bubble
+ ViewGroup actionListMarginTarget = layout.findViewById(
+ com.android.internal.R.id.notification_action_list_margin_target);
+ if (actionListMarginTarget != null) {
+ removeBottomMargin(actionListMarginTarget);
}
}
} else {
@@ -1664,8 +1665,13 @@
return;
}
ImageView snoozeButton = layout.findViewById(com.android.internal.R.id.snooze_button);
- View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
- if (snoozeButton == null || actionContainer == null) {
+ // With the new design, the actions_container should always be visible to act as padding
+ // when there are no actions. We're making its child visible/invisible instead.
+ View actionsContainerForVisibilityChange = layout.findViewById(
+ notificationsRedesignTemplates()
+ ? com.android.internal.R.id.actions_container_layout
+ : com.android.internal.R.id.actions_container);
+ if (snoozeButton == null || actionsContainerForVisibilityChange == null) {
return;
}
// Notification.Builder can 'disable' the snooze button to prevent it from being shown here
@@ -1691,7 +1697,7 @@
snoozeButton.setOnClickListener(
mContainingNotification.getSnoozeClickListener(snoozeMenuItem));
snoozeButton.setVisibility(VISIBLE);
- actionContainer.setVisibility(VISIBLE);
+ actionsContainerForVisibilityChange.setVisibility(VISIBLE);
}
private void applySmartReplyView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 9a75253..cdb78d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -47,7 +47,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -71,6 +70,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -100,6 +100,7 @@
private final AccessibilityManager mAccessibilityManager;
private final HighPriorityProvider mHighPriorityProvider;
private final ChannelEditorDialogController mChannelEditorDialogController;
+ private final PackageDemotionInteractor mPackageDemotionInteractor;
private final OnUserInteractionCallback mOnUserInteractionCallback;
// Dependencies:
@@ -155,6 +156,7 @@
LauncherApps launcherApps,
ShortcutManager shortcutManager,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
UserContextProvider contextTracker,
AssistantFeedbackController assistantFeedbackController,
Optional<BubblesManager> bubblesManagerOptional,
@@ -184,6 +186,7 @@
mShortcutManager = shortcutManager;
mContextTracker = contextTracker;
mChannelEditorDialogController = channelEditorDialogController;
+ mPackageDemotionInteractor = packageDemotionInteractor;
mAssistantFeedbackController = assistantFeedbackController;
mBubblesManagerOptional = bubblesManagerOptional;
mUiEventLogger = uiEventLogger;
@@ -231,15 +234,6 @@
}
}
- public void onDensityOrFontScaleChanged(NotificationEntry entry) {
- if (!Flags.notificationUndoGutsOnConfigChanged()) {
- Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
- + " notificationUndoGutsOnConfigChanged is off");
- }
- setExposedGuts(entry.getGuts());
- bindGuts(entry.getRow());
- }
-
/**
* Sends an intent to open the notification settings for a particular package and optional
* channel.
@@ -291,11 +285,6 @@
mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
}
- private boolean bindGuts(final ExpandableNotificationRow row) {
- row.ensureGutsInflated();
- return bindGuts(row, mGutsMenuItem);
- }
-
@VisibleForTesting
protected boolean bindGuts(final ExpandableNotificationRow row,
NotificationMenuRowPlugin.MenuItem item) {
@@ -429,6 +418,7 @@
mIconStyleProvider,
mOnUserInteractionCallback,
mChannelEditorDialogController,
+ mPackageDemotionInteractor,
packageName,
row.getEntry().getChannel(),
row.getEntry(),
@@ -440,6 +430,7 @@
NotificationBundleUi.isEnabled()
? !row.getEntry().isBlockable()
: row.getIsNonblockable(),
+ row.canViewBeDismissed(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
mAssistantFeedbackController,
mMetricsLogger,
@@ -605,6 +596,7 @@
return mNotificationGutsExposed;
}
+ @VisibleForTesting
public void setExposedGuts(NotificationGuts guts) {
mNotificationGutsExposed = guts;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 6611225..b6f4ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -74,6 +74,7 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -120,6 +121,7 @@
private boolean mIsAutomaticChosen;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
+ private boolean mIsDismissable;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
@@ -160,6 +162,7 @@
mPressedApply = true;
mGutsContainer.closeControls(v, /* save= */ true);
};
+ private OnClickListener mOnCloseClickListener;
public NotificationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -193,6 +196,7 @@
NotificationIconStyleProvider iconStyleProvider,
OnUserInteractionCallback onUserInteractionCallback,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
String pkg,
NotificationChannel notificationChannel,
NotificationEntry entry,
@@ -202,6 +206,7 @@
UiEventLogger uiEventLogger,
boolean isDeviceProvisioned,
boolean isNonblockable,
+ boolean isDismissable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
MetricsLogger metricsLogger,
@@ -226,11 +231,13 @@
mStartingChannelImportance = mSingleNotificationChannel.getImportance();
mWasShownHighPriority = wasShownHighPriority;
mIsNonblockable = isNonblockable;
+ mIsDismissable = isDismissable;
mAppUid = mSbn.getUid();
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled();
mUiEventLogger = uiEventLogger;
+ mOnCloseClickListener = onCloseClick;
mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class)
&& mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid());
@@ -277,6 +284,11 @@
turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
? VISIBLE : GONE);
+ View dismissButton = findViewById(R.id.inline_dismiss);
+ dismissButton.setOnClickListener(mOnCloseClickListener);
+ dismissButton.setVisibility(dismissButton.hasOnClickListeners() && mIsDismissable
+ ? VISIBLE : GONE);
+
View done = findViewById(R.id.done);
done.setOnClickListener(mOnDismissSettings);
done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 2f94d32..ae52db8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -541,7 +541,7 @@
val ident: String = (sbn.packageName + "/0x" + Integer.toHexString(sbn.id))
Log.e(TAG, "couldn't inflate view for notification $ident", e)
callback?.handleInflationException(
- if (NotificationBundleUi.isEnabled) entry else row.entry,
+ if (NotificationBundleUi.isEnabled) entry else row.entryLegacy,
InflationException("Couldn't inflate contentViews$e"),
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 83897f5..cec0ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,7 +51,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.res.R;
@@ -477,7 +476,7 @@
@Override
public boolean handleCloseControls(boolean save, boolean force) {
- if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+ if (!save) {
// Undo changes and let the guts handle closing the view
mSelectedOption = null;
showSnoozeOptions(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
index 6ff711d..01ee788 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -31,6 +31,7 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -42,6 +43,7 @@
public class PromotedNotificationInfo extends NotificationInfo {
private static final String TAG = "PromotedNotifInfoGuts";
private INotificationManager mNotificationManager;
+ private PackageDemotionInteractor mPackageDemotionInteractor;
private NotificationGuts mGutsContainer;
public PromotedNotificationInfo(Context context, AttributeSet attrs) {
@@ -56,6 +58,7 @@
NotificationIconStyleProvider iconStyleProvider,
OnUserInteractionCallback onUserInteractionCallback,
ChannelEditorDialogController channelEditorDialogController,
+ PackageDemotionInteractor packageDemotionInteractor,
String pkg,
NotificationChannel notificationChannel,
NotificationEntry entry,
@@ -65,16 +68,19 @@
UiEventLogger uiEventLogger,
boolean isDeviceProvisioned,
boolean isNonblockable,
+ boolean isDismissable,
boolean wasShownHighPriority,
AssistantFeedbackController assistantFeedbackController,
MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
super.bindNotification(pm, iNotificationManager, appIconProvider, iconStyleProvider,
- onUserInteractionCallback, channelEditorDialogController, pkg, notificationChannel,
+ onUserInteractionCallback, channelEditorDialogController, packageDemotionInteractor,
+ pkg, notificationChannel,
entry, onSettingsClick, onAppSettingsClick, feedbackClickListener, uiEventLogger,
- isDeviceProvisioned, isNonblockable, wasShownHighPriority,
+ isDeviceProvisioned, isNonblockable, isDismissable, wasShownHighPriority,
assistantFeedbackController, metricsLogger, onCloseClick);
mNotificationManager = iNotificationManager;
+ mPackageDemotionInteractor = packageDemotionInteractor;
bindDemote(entry.getSbn(), pkg);
}
@@ -94,8 +100,8 @@
private OnClickListener getDemoteClickListener(StatusBarNotification sbn, String packageName) {
return ((View v) -> {
try {
- // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
mNotificationManager.setCanBePromoted(packageName, sbn.getUid(), false, true);
+ mPackageDemotionInteractor.onPackageDemoted(packageName, sbn.getUid());
mGutsContainer.closeControls(v, true);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't revoke live update permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
index 4082a5b..2c5b9f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -61,7 +61,7 @@
row: ExpandableNotificationRow,
context: Context,
): NotificationIconProvider {
- val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else row.entry.sbn
+ val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else row.entryLegacy.sbn
if (sbn == null) {
return object : NotificationIconProvider {
override fun shouldShowAppIcon(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index f492b25..e266dad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -50,7 +50,7 @@
resolveViews();
updateImageTag(NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn()
- : row.getEntry().getSbn());
+ : row.getEntryLegacy().getSbn());
}
private void resolveViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index dec674c..71bb9a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -47,7 +47,7 @@
// the transformation types and we need to have our values set by then.
resolveViews(NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn()
- : row.getEntry().getSbn());
+ : row.getEntryLegacy().getSbn());
super.onContentUpdated(row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 585051a..e6dadcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -225,7 +225,7 @@
super.onContentUpdated(row);
mIsLowPriority = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().isAmbient()
- : row.getEntry().isAmbient();
+ : row.getEntryLegacy().isAmbient();
mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
@@ -236,7 +236,7 @@
updateCropToPaddingForImageViews();
Notification n = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn().getNotification()
- : row.getEntry().getSbn().getNotification();
+ : row.getEntryLegacy().getSbn().getNotification();
mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
// We need to reset all views that are no longer transforming in case a view was previously
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 99db1db..19321dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -327,7 +327,7 @@
// the transformation types and we need to have our values set by then.
resolveTemplateViews(NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn()
- : row.getEntry().getSbn());
+ : row.getEntryLegacy().getSbn());
super.onContentUpdated(row);
// With the modern templates, a large icon visually overlaps the header, so we can't
// hide the header, we must show it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 64babb2..35e286c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -83,7 +83,7 @@
if (NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSbn().getNotification().isStyle(
Notification.DecoratedCustomViewStyle.class)
- : row.getEntry().getSbn().getNotification().isStyle(
+ : row.getEntryLegacy().getSbn().getNotification().isStyle(
Notification.DecoratedCustomViewStyle.class)) {
return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
}
@@ -141,7 +141,7 @@
// Apps targeting Q should fix their dark mode bugs.
int targetSdk = NotificationBundleUi.isEnabled()
? mRow.getEntryAdapter().getTargetSdk()
- : mRow.getEntry().targetSdk;
+ : mRow.getEntryLegacy().targetSdk;
if (targetSdk >= Build.VERSION_CODES.Q) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 315d37e..f9d8c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -444,7 +444,7 @@
mIsConversation = isConversation;
StatusBarNotification notification = NotificationBundleUi.isEnabled()
? mContainingNotification.getEntryAdapter().getSbn()
- : mContainingNotification.getEntry().getSbn();
+ : mContainingNotification.getEntryLegacy().getSbn();
if (notification == null) {
return;
}
@@ -615,7 +615,7 @@
RemoteViews header;
StatusBarNotification notification = NotificationBundleUi.isEnabled()
? mContainingNotification.getEntryAdapter().getSbn()
- : mContainingNotification.getEntry().getSbn();
+ : mContainingNotification.getEntryLegacy().getSbn();
if (notification == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 96f0e6f..b5562ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -167,7 +167,7 @@
view === promoHeaderView -> BUCKET_PROMO
view is ExpandableNotificationRow ->
if (NotificationBundleUi.isEnabled) view.entryAdapter?.sectionBucket
- else view.entry.bucket
+ else view.entryLegacy.bucket
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e6bb1b9..9fea750 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -64,6 +64,7 @@
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
+import android.util.Pair;
import android.view.DisplayCutout;
import android.view.InputDevice;
import android.view.LayoutInflater;
@@ -140,6 +141,7 @@
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
+import com.android.systemui.statusbar.ui.SystemBarUtilsProxy;
import com.android.systemui.util.Assert;
import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.DumpUtilsKt;
@@ -1037,10 +1039,10 @@
}
int bucket = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSectionBucket()
- : row.getEntry().getBucket();
+ : row.getEntryLegacy().getBucket();
boolean isAmbient = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().isAmbient()
- : row.getEntry().isAmbient();
+ : row.getEntryLegacy().isAmbient();
currentIndex++;
boolean beforeSpeedBump;
if (mHighPriorityBeforeSpeedBump) {
@@ -1845,7 +1847,7 @@
} else {
if (row.isChildInGroup()) {
final NotificationEntry groupSummary =
- mGroupMembershipManager.getGroupSummary(row.getEntry());
+ mGroupMembershipManager.getGroupSummary(row.getEntryLegacy());
if (groupSummary != null) {
row = groupSummary.getRow();
}
@@ -1998,16 +2000,16 @@
if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow row) {
- NotificationEntry entry = row.getEntry();
boolean isEntrySummaryForTopHun;
if (NotificationBundleUi.isEnabled()) {
isEntrySummaryForTopHun = Objects.equals(
((ExpandableNotificationRow) slidingChild).getNotificationParent(),
mTopHeadsUpRow);
} else {
+ NotificationEntry entry = row.getEntryLegacy();
isEntrySummaryForTopHun = mTopHeadsUpRow != null &&
- mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
- == entry;
+ mGroupMembershipManager.getGroupSummary(
+ mTopHeadsUpRow.getEntryLegacy()) == entry;
}
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
&& mTopHeadsUpRow != row
@@ -3007,7 +3009,7 @@
ExpandableNotificationRow childRow = (ExpandableNotificationRow) child;
return NotificationBundleUi.isEnabled()
? mGroupMembershipManager.isChildInGroup(childRow.getEntryAdapter())
- : mGroupMembershipManager.isChildInGroup(childRow.getEntry());
+ : mGroupMembershipManager.isChildInGroup(childRow.getEntryLegacy());
}
return false;
}
@@ -3852,8 +3854,6 @@
// existing overScroll, we have to scroll the view
customOverScrollBy((int) scrollAmount, getOwnScrollY(),
range, getHeight() / 2);
- // If we're scrolling, leavebehinds should be dismissed
- mController.checkSnoozeLeavebehind();
}
}
break;
@@ -6002,7 +6002,6 @@
* LockscreenShadeTransitionController resets fraction to 0
* where it remains until the next lockscreen-to-shade transition.
*/
- @Override
public void setFractionToShade(float fraction) {
mAmbientState.setFractionToShade(fraction);
updateContentHeight(); // Recompute stack height with different section gap.
@@ -6474,7 +6473,7 @@
@SelectedRows int selection) {
int bucket = NotificationBundleUi.isEnabled()
? row.getEntryAdapter().getSectionBucket()
- : row.getEntry().getBucket();
+ : row.getEntryLegacy().getBucket();
switch (selection) {
case ROWS_ALL:
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f7f8acf..f3d8ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -648,11 +648,11 @@
public void onChildSnappedBack(View animView, float targetLeft) {
mView.onSwipeEnd();
if (animView instanceof ExpandableNotificationRow row) {
- if (row.isPinned() && !canChildBeDismissed(row)
- && NotificationBundleUi.isEnabled()
+ boolean cannotFullScreen = NotificationBundleUi.isEnabled()
? !row.getEntryAdapter().isFullScreenCapable()
- : (row.getEntry().getSbn().getNotification().fullScreenIntent
- == null)) {
+ : (row.getEntryLegacy().getSbn().getNotification().fullScreenIntent
+ == null);
+ if (row.isPinned() && !canChildBeDismissed(row) && cannotFullScreen) {
mHeadsUpManager.removeNotification(
row.getKey(),
/* removeImmediately= */ true,
@@ -1735,7 +1735,6 @@
* they remain until the next lockscreen-to-shade transition.
*/
public void setTransitionToFullShadeAmount(float fraction) {
- SceneContainerFlag.assertInLegacyMode();
mView.setFractionToShade(fraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index fcb63df..e5071d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -413,7 +413,7 @@
(currentNotification as? ExpandableNotificationRow)?.entryAdapter
counter.incrementForBucket(entryAdapter?.sectionBucket)
} else {
- val entry = (currentNotification as? ExpandableNotificationRow)?.entry
+ val entry = (currentNotification as? ExpandableNotificationRow)?.entryLegacy
counter.incrementForBucket(entry?.bucket)
}
}
@@ -470,7 +470,7 @@
calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
val canPeek = view is ExpandableNotificationRow &&
if (NotificationBundleUi.isEnabled) view.entryAdapter?.canPeek() == true
- else view.entry.isStickyAndNotDemoted
+ else view.entryLegacy.isStickyAndNotDemoted
var size =
if (onLockscreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2821822..da14423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -927,7 +927,7 @@
childState.headsUpIsVisible, row.showingPulsing(),
ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
? row.getEntryAdapter().canPeek()
- : row.getEntry().isStickyAndNotDemoted())) {
+ : row.getEntryLegacy().isStickyAndNotDemoted())) {
// the height of this child before clamping it to the top
float unmodifiedChildHeight = childState.height;
clampHunToTop(
@@ -984,7 +984,7 @@
childState.headsUpIsVisible, row.showingPulsing(),
ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
? row.getEntryAdapter().canPeek()
- : row.getEntry().isStickyAndNotDemoted())) {
+ : row.getEntryLegacy().isStickyAndNotDemoted())) {
// Ensure that the heads up is always visible even when scrolled off.
// NSSL y starts at top of screen in non-split-shade, but below the qs
// offset
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index ac89f3a..9c855e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -107,9 +107,6 @@
/** sets the current expand fraction */
fun setExpandFraction(expandFraction: Float)
- /** Sets the fraction of the LockScreen -> Shade transition. */
- fun setFractionToShade(fraction: Float)
-
/** sets the current QS expand fraction */
fun setQsExpandFraction(expandFraction: Float)
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 40739b3..653344a 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
@@ -95,11 +95,6 @@
view.setExpandFraction(it.coerceIn(0f, 1f))
}
}
- launch {
- viewModel.lockScreenToShadeTransitionProgress.collectTraced {
- view.setFractionToShade(it.coerceIn(0f, 1f))
- }
- }
launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } }
launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) }
launch {
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 940b2e5..c1aa5f1 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
@@ -207,44 +207,6 @@
val qsExpandFraction: Flow<Float> =
shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction")
- /**
- * Fraction of the LockScreen -> Shade transition. 0..1 while the transition in progress, and
- * snaps back to 0 when it is Idle.
- */
- val lockScreenToShadeTransitionProgress: Flow<Float> =
- combine(
- shadeInteractor.shadeExpansion,
- shadeModeInteractor.shadeMode,
- sceneInteractor.transitionState,
- ) { shadeExpansion, _, transitionState ->
- when (transitionState) {
- is Idle -> 0f
- is ChangeScene ->
- if (
- transitionState.isTransitioning(
- from = Scenes.Lockscreen,
- to = Scenes.Shade,
- )
- ) {
- shadeExpansion
- } else {
- 0f
- }
-
- is Transition.OverlayTransition ->
- if (
- transitionState.currentScene == Scenes.Lockscreen &&
- transitionState.isTransitioning(to = Overlays.NotificationsShade)
- ) {
- shadeExpansion
- } else {
- 0f
- }
- }
- }
- .distinctUntilChanged()
- .dumpWhileCollecting("lockScreenToShadeTransitionProgress")
-
val isOccluded: Flow<Boolean> =
bouncerInteractor.bouncerExpansion
.map { it == 1f }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index bc53314..afe7971 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -60,7 +60,7 @@
if (animationsEnabled) {
added.forEach { key ->
val row = obtainView(key)
- val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+ val hasStatusBarChip = statusBarChips.contains(row.key)
parentView.generateHeadsUpAnimation(
row,
/* isHeadsUp = */ true,
@@ -69,7 +69,7 @@
}
removed.forEach { key ->
val row = obtainView(key)
- val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+ val hasStatusBarChip = statusBarChips.contains(row.key)
if (!parentView.isBeingDragged()) {
parentView.generateHeadsUpAnimation(
row,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74a42ef..f3d7202 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,6 +29,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.util.Log;
@@ -226,6 +227,8 @@
private ScrimState mState = ScrimState.UNINITIALIZED;
+ private Context mContext;
+
private ScrimView mScrimInFront;
private ScrimView mNotificationsScrim;
private ScrimView mScrimBehind;
@@ -365,7 +368,9 @@
@Main CoroutineDispatcher mainDispatcher,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
BlurConfig blurConfig,
+ @Main Context context,
Lazy<WindowRootViewBlurInteractor> windowRootViewBlurInteractor) {
+ mContext = context;
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mBlurConfig = blurConfig;
@@ -1627,16 +1632,16 @@
private void updateThemeColors() {
if (mScrimBehind == null) return;
- int background = mScrimBehind.getContext().getColor(
+ int background = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceDim);
- int accent = mScrimBehind.getContext().getColor(
+ int accent = mContext.getColor(
com.android.internal.R.color.materialColorPrimary);
mColors.setMainColor(background);
mColors.setSecondaryColor(accent);
final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
mColors.setSupportsDarkText(isBackgroundLight);
- int surface = mScrimBehind.getContext().getColor(
+ int surface = mContext.getColor(
com.android.internal.R.color.materialColorSurface);
for (ScrimState state : ScrimState.values()) {
state.setSurfaceColor(surface);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 05a46cd..8389aab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -219,7 +219,7 @@
if (NotificationBundleUi.isEnabled()) {
mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
} else {
- mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
}
} else if (!row.isChildInGroup()) {
final boolean expandNotification;
@@ -241,7 +241,7 @@
if (NotificationBundleUi.isEnabled()) {
mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
} else {
- mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index ba66651..2f9cff4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -196,9 +196,8 @@
setContent {
PlatformTheme {
- val chipsVisibilityModel by
+ val chipsVisibilityModel =
statusBarViewModel.ongoingActivityChips
- .collectAsStateWithLifecycle()
if (chipsVisibilityModel.areChipsAllowed) {
OngoingActivityChips(
chips = chipsVisibilityModel.chips,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index c717b18..540baba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -21,6 +21,8 @@
import android.view.Display
import android.view.View
import androidx.compose.runtime.getValue
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.app.tracing.TrackGroupUtils.trackGroup
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -130,7 +132,7 @@
val primaryOngoingActivityChip: StateFlow<OngoingActivityChipModel>
/** All supported activity chips, whether they are currently active or not. */
- val ongoingActivityChips: StateFlow<ChipsVisibilityModel>
+ val ongoingActivityChips: ChipsVisibilityModel
/**
* The multiple ongoing activity chips that should be shown on the left-hand side of the status
@@ -386,11 +388,9 @@
}
override val isHomeStatusBarAllowed =
- isHomeStatusBarAllowedCompat.stateIn(
- bgScope,
- SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
+ isHomeStatusBarAllowedCompat
+ .traceEach(trackGroup(TRACK_GROUP, "isHomeStatusBarAllowed"), logcat = true)
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), initialValue = false)
private val shouldHomeStatusBarBeVisible =
combine(
@@ -461,24 +461,29 @@
isHomeStatusBarAllowed && !isSecureCameraActive && !hideStartSideContentForHeadsUp
}
- override val ongoingActivityChips =
+ private val chipsVisibilityModel: Flow<ChipsVisibilityModel> =
combine(ongoingActivityChipsViewModel.chips, canShowOngoingActivityChips) { chips, canShow
->
ChipsVisibilityModel(chips, areChipsAllowed = canShow)
}
- .stateIn(
- bgScope,
- SharingStarted.WhileSubscribed(),
- initialValue =
- ChipsVisibilityModel(
- chips = MultipleOngoingActivityChipsModel(),
- areChipsAllowed = false,
- ),
- )
+ .traceEach(trackGroup(TRACK_GROUP, "chips"), logcat = true) {
+ "Chips[allowed=${it.areChipsAllowed} numChips=${it.chips.active.size}]"
+ }
+
+ override val ongoingActivityChips: ChipsVisibilityModel by
+ hydrator.hydratedStateOf(
+ traceName = "ongoingActivityChips",
+ initialValue =
+ ChipsVisibilityModel(
+ chips = MultipleOngoingActivityChipsModel(),
+ areChipsAllowed = false,
+ ),
+ source = chipsVisibilityModel,
+ )
private val hasOngoingActivityChips =
if (StatusBarChipsModernization.isEnabled) {
- ongoingActivityChips.map { it.chips.active.any { chip -> !chip.isHidden } }
+ chipsVisibilityModel.map { it.chips.active.any { chip -> !chip.isHidden } }
} else if (StatusBarNotifChips.isEnabled) {
ongoingActivityChipsLegacy.map { it.primary is OngoingActivityChipModel.Active }
} else {
@@ -607,6 +612,8 @@
private const val COL_PREFIX_NOTIF_CONTAINER = "notifContainer"
private const val COL_PREFIX_SYSTEM_INFO = "systemInfo"
+ private const val TRACK_GROUP = "StatusBar"
+
fun tableLogBufferName(displayId: Int) = "HomeStatusBarViewModel[$displayId]"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index b13e01b..fa022b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -27,6 +27,7 @@
import androidx.annotation.NonNull;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
@@ -56,8 +57,8 @@
private int mDeviceState = -1;
@Nullable
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
- private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
- mDeviceStateRotationLockSettingsListener;
+ private DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+ mDeviceStateAutoRotateSettingListener;
@Inject
public DeviceStateRotationLockSettingController(
@@ -83,17 +84,17 @@
// is no user action.
mDeviceStateCallback = this::updateDeviceState;
mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
- mDeviceStateRotationLockSettingsListener = () ->
+ mDeviceStateAutoRotateSettingListener = () ->
readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
mDeviceStateRotationLockSettingsManager.registerListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
} else {
if (mDeviceStateCallback != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
}
- if (mDeviceStateRotationLockSettingsListener != null) {
+ if (mDeviceStateAutoRotateSettingListener != null) {
mDeviceStateRotationLockSettingsManager.unregisterListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
index 47e27bc..1cc7a31 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
@@ -50,6 +50,7 @@
onScreenTurningOnToOnDrawnMs,
onDrawnToOnScreenTurnedOnMs,
trackingResult,
+ screenWakelockstatus
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 66de522..5800d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -344,6 +344,8 @@
val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN,
val trackingResult: Int =
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__UNKNOWN_RESULT,
+ val screenWakelockstatus: Int =
+ SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_UNKNOWN,
)
enum class TrackingResult {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1bc3096..9475bdb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2746,7 +2746,7 @@
() -> mJavaAdapter,
() -> mSceneInteractor,
() -> mCommunalSceneInteractor,
- mKeyguardServiceShowLockscreenInteractor);
+ () -> mKeyguardServiceShowLockscreenInteractor);
setAlternateBouncerVisibility(false);
setPrimaryBouncerVisibility(false);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 5c893da..6a4f3da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -44,6 +44,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
@@ -77,6 +78,7 @@
import org.mockito.stubbing.Answer;
import java.util.Optional;
+import java.util.function.Consumer;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -158,6 +160,31 @@
* is false are removed.[
*/
private void initController() {
+ IntentCreator fakeIntentCreator = new IntentCreator() {
+ @Override
+ public Intent getTextEditorIntent(Context context) {
+ return new Intent();
+ }
+
+ @Override
+ public Intent getShareIntent(ClipData clipData, Context context) {
+ Intent intent = Intent.createChooser(new Intent(Intent.ACTION_SEND), null);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ @Override
+ public void getImageEditIntentAsync(Uri uri, Context context,
+ Consumer<Intent> outputConsumer) {
+ outputConsumer.accept(new Intent(Intent.ACTION_EDIT));
+ }
+
+ @Override
+ public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+ return new Intent();
+ }
+ };
+
mOverlayController = new ClipboardOverlayController(
mContext,
mClipboardOverlayView,
@@ -171,7 +198,7 @@
mClipboardTransitionExecutor,
mClipboardIndicationProvider,
mUiEventLogger,
- new ActionIntentCreator());
+ fakeIntentCreator);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index fb70846..061f798 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -134,7 +134,6 @@
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private PackageManager mPackageManager;
- @Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private VibratorHelper mVibratorHelper;
@Mock private ShadeController mShadeController;
@@ -148,6 +147,7 @@
private TestableLooper mTestableLooper;
private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private GlobalActionsInteractor mInteractor;
+ private Handler mHandler;
@Before
public void setUp() throws Exception {
@@ -166,6 +166,7 @@
mGlobalSettings = new FakeGlobalSettings();
mSecureSettings = new FakeSettings();
mInteractor = mKosmos.getGlobalActionsInteractor();
+ mHandler = new Handler(mTestableLooper.getLooper());
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -771,6 +772,31 @@
mGlobalActionsDialogLite.showOrHideDialog(false, false, null, Display.DEFAULT_DISPLAY);
}
+ @Test
+ public void userSwitching_dismissDialog() {
+ String[] actions = {
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mResources)
+ .getStringArray(com.android.internal.R.array.config_globalActionsList);
+
+ mGlobalActionsDialogLite.showOrHideDialog(false, true, null, Display.DEFAULT_DISPLAY);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mGlobalActionsDialogLite.mDialog.isShowing()).isTrue();
+
+ ArgumentCaptor<UserTracker.Callback> captor =
+ ArgumentCaptor.forClass(UserTracker.Callback.class);
+
+ verify(mUserTracker).addCallback(captor.capture(), any());
+
+ captor.getValue().onBeforeUserSwitching(100);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mGlobalActionsDialogLite.mDialog).isNull();
+ }
+
private UserInfo mockCurrentUser(int flags) {
return new UserInfo(10, "A User", flags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index f394c80..8f1d07b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -855,6 +855,20 @@
@Test
fun contentDescriptionUpdated() {
+ var elapsedTimeDesc: CharSequence? = null
+ var durationDesc: CharSequence? = null
+ val listener =
+ object : SeekBarViewModel.ContentDescriptionListener {
+ override fun onContentDescriptionChanged(
+ elapsedTimeDescription: CharSequence,
+ durationDescription: CharSequence,
+ ) {
+ elapsedTimeDesc = elapsedTimeDescription
+ durationDesc = durationDescription
+ }
+ }
+ viewModel.setContentDescriptionListener(listener)
+
// When there is a duration and position
val duration = (1.5 * 60 * 60 * 1000).toLong()
val metadata =
@@ -875,9 +889,7 @@
viewModel.updateController(mockController)
fakeExecutor.runNextReady()
- // Then the content description is set
- val result = viewModel.progress.value!!
-
+ // Then the content description listener gets an update
val expectedProgress =
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
.formatMeasures(Measure(3, MeasureUnit.SECOND))
@@ -888,7 +900,7 @@
Measure(30, MeasureUnit.MINUTE),
Measure(0, MeasureUnit.SECOND),
)
- assertThat(result.durationDescription).isEqualTo(expectedDuration)
- assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
+ assertThat(elapsedTimeDesc).isEqualTo(expectedProgress)
+ assertThat(durationDesc).isEqualTo(expectedDuration)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 92b26ea..7e42ec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -30,6 +30,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -55,19 +56,21 @@
listState: EditTileListState,
onSetTiles: (List<TileSpec>) -> Unit,
) {
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = listOf(),
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = {},
- onRemoveTile = {},
- onSetTiles = onSetTiles,
- onResize = { _, _ -> },
- onStopEditing = {},
- onReset = null,
- )
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = listOf(),
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = {},
+ onRemoveTile = {},
+ onSetTiles = onSetTiles,
+ onResize = { _, _ -> },
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
index e76be82c..9d4a425 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
@@ -33,6 +33,7 @@
import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -56,19 +57,22 @@
var tiles by remember { mutableStateOf(TestEditTiles) }
val (currentTiles, otherTiles) = tiles.partition { it.tile.isCurrent }
val listState = EditTileListState(currentTiles, columns = 4, largeTilesSpan = 2)
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = otherTiles,
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = { tiles = tiles.add(it) },
- onRemoveTile = { tiles = tiles.remove(it) },
- onSetTiles = {},
- onResize = { _, _ -> },
- onStopEditing = {},
- onReset = null,
- )
+
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = otherTiles,
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = { tiles = tiles.add(it) },
+ onRemoveTile = { tiles = tiles.remove(it) },
+ onSetTiles = {},
+ onResize = { _, _ -> },
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index 021be32..5e76000 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -35,6 +35,7 @@
import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -61,19 +62,21 @@
listState: EditTileListState,
onResize: (TileSpec, Boolean) -> Unit,
) {
- DefaultEditTileGrid(
- listState = listState,
- otherTiles = listOf(),
- columns = 4,
- largeTilesSpan = 4,
- modifier = Modifier.fillMaxSize(),
- onAddTile = {},
- onRemoveTile = {},
- onSetTiles = {},
- onResize = onResize,
- onStopEditing = {},
- onReset = null,
- )
+ PlatformTheme {
+ DefaultEditTileGrid(
+ listState = listState,
+ otherTiles = listOf(),
+ columns = 4,
+ largeTilesSpan = 4,
+ modifier = Modifier.fillMaxSize(),
+ onAddTile = {},
+ onRemoveTile = {},
+ onSetTiles = {},
+ onResize = onResize,
+ onStopEditing = {},
+ onReset = null,
+ )
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
index cf54df8..997cf41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -22,6 +22,7 @@
import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceStateManager
import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
@@ -37,6 +38,7 @@
import kotlinx.coroutines.flow.MutableSharedFlow
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.times
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -59,6 +61,7 @@
fakeRearDisplayStateInteractor,
kosmos.rearDisplayInnerDialogDelegateFactory,
kosmos.testScope,
+ kosmos.keyguardUpdateMonitor,
)
@Before
@@ -96,6 +99,26 @@
}
}
+ @Test
+ @EnableFlags(FLAG_DEVICE_STATE_RDM_V2)
+ fun testDialogResumesAfterKeyguardGone() =
+ kosmos.runTest {
+ impl.use {
+ it.start()
+ fakeRearDisplayStateInteractor.emitRearDisplay()
+
+ verify(mockDialog).show()
+
+ it.keyguardCallback.onKeyguardVisibilityChanged(true)
+ // Do not need to check that the dialog is dismissed, since SystemUIDialog
+ // implementation handles that. We just toggle keyguard here so that the flow
+ // emits.
+
+ it.keyguardCallback.onKeyguardVisibilityChanged(false)
+ verify(mockDialog, times(2)).show()
+ }
+ }
+
private class FakeRearDisplayStateInteractor(private val kosmos: Kosmos) :
RearDisplayStateInteractor {
private val stateFlow = MutableSharedFlow<RearDisplayStateInteractor.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
index e0118b1..9b03833 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
@@ -22,6 +22,7 @@
import android.content.pm.ResolveInfo
import android.os.PowerManager
import android.os.UserManager
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableContext
import android.testing.TestableLooper
import android.view.Display
@@ -29,17 +30,23 @@
import androidx.test.filters.SmallTest
import com.android.internal.app.AssistUtils
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
+import com.android.systemui.kosmos.testScope
import com.android.systemui.log.assertLogsWtf
+import com.android.systemui.model.fakeSysUIStatePerDisplayRepository
import com.android.systemui.model.sysUiState
+import com.android.systemui.model.sysUiStateFactory
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.navigationbar.views.NavigationBar
import com.android.systemui.process.ProcessWrapper
import com.android.systemui.recents.LauncherProxyService.ACTION_QUICKSTEP
import com.android.systemui.settings.FakeDisplayTracker
@@ -56,18 +63,20 @@
import com.android.systemui.testKosmos
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.wm.shell.back.BackAnimation
import com.android.wm.shell.sysui.ShellInterface
import com.google.common.util.concurrent.MoreExecutors
import java.util.Optional
import java.util.concurrent.Executor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
@@ -81,6 +90,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -96,8 +106,10 @@
private val displayTracker = FakeDisplayTracker(mContext)
private val fakeSystemClock = FakeSystemClock()
private val sysUiState = kosmos.sysUiState
+ private val sysUiStateFactory = kosmos.sysUiStateFactory
private val wakefulnessLifecycle =
WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
+ private val sysuiStatePerDisplayRepository = kosmos.fakeSysUIStatePerDisplayRepository
@Mock private lateinit var launcherProxy: ILauncherProxy.Stub
@Mock private lateinit var packageManager: PackageManager
@@ -149,6 +161,8 @@
// return isSystemUser as true by default.
`when`(processWrapper.isSystemUser).thenReturn(true)
+ sysuiStatePerDisplayRepository.add(Display.DEFAULT_DISPLAY, sysUiState)
+ runBlocking { kosmos.displayRepository.apply { addDisplay(0) } }
subject = createLauncherProxyService(context)
}
@@ -249,6 +263,48 @@
verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
}
+ @Test
+ fun notifySysUiStateFlagsForAllDisplays_triggersUpdateInAllDisplays() =
+ kosmos.testScope.runTest {
+ kosmos.displayRepository.apply {
+ addDisplay(0)
+ addDisplay(1)
+ addDisplay(2)
+ }
+ kosmos.fakeSysUIStatePerDisplayRepository.apply {
+ add(1, sysUiStateFactory.create(1))
+ add(2, sysUiStateFactory.create(2))
+ }
+ clearInvocations(launcherProxy)
+ subject.notifySysUiStateFlagsForAllDisplays()
+
+ verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(0))
+ verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(1))
+ verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(2))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun updateSystemUiStateFlags_updatesAllNavBars() =
+ kosmos.testScope.runTest {
+ kosmos.displayRepository.apply {
+ addDisplay(0)
+ addDisplay(1)
+ }
+ kosmos.fakeSysUIStatePerDisplayRepository.apply {
+ add(1, sysUiStateFactory.create(1))
+ }
+ val navBar0 = mock<NavigationBar>()
+ val navBar1 = mock<NavigationBar>()
+ whenever(navBarController.getNavigationBar(eq(0))).thenReturn(navBar0)
+ whenever(navBarController.getNavigationBar(eq(1))).thenReturn(navBar1)
+
+ subject.updateSystemUiStateFlags()
+
+ verify(navBar0).updateSystemUiStateFlags()
+ verify(navBar1).updateSystemUiStateFlags()
+ }
+
private fun createLauncherProxyService(ctx: Context): LauncherProxyService {
return LauncherProxyService(
ctx,
@@ -260,7 +316,7 @@
screenPinningRequest,
navModeController,
statusBarWinController,
- sysUiState,
+ kosmos.fakeSysUIStatePerDisplayRepository,
mock(),
mock(),
userTracker,
@@ -276,6 +332,7 @@
broadcastDispatcher,
backAnimation,
processWrapper,
+ kosmos.displayRepository,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
index c231be1..826c547 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
@@ -23,6 +23,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.UserHandle;
+import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -37,36 +38,34 @@
@RunWith(AndroidJUnit4.class)
public class RingtonePlayerTest extends SysuiTestCase {
- private AudioManager mAudioManager;
-
private static final String TAG = "RingtonePlayerTest";
- @Before
- public void setup() throws Exception {
- mAudioManager = getContext().getSystemService(AudioManager.class);
- }
-
@Test
public void testRingtonePlayerUriUserCheck() {
- android.media.IRingtonePlayer irp = mAudioManager.getRingtonePlayer();
- final AudioAttributes aa = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
- // get a UserId that doesn't belong to mine
- final int otherUserId = UserHandle.myUserId() == 0 ? 10 : 0;
- // build a URI that I shouldn't have access to
- final Uri uri = new Uri.Builder()
- .scheme("content").authority(otherUserId + "@media")
- .appendPath("external").appendPath("downloads")
- .appendPath("bogusPathThatDoesNotMatter.mp3")
- .build();
- if (android.media.audio.Flags.ringtoneUserUriCheck()) {
- assertThrows(SecurityException.class, () ->
- irp.play(new Binder(), uri, aa, 1.0f /*volume*/, false /*looping*/)
- );
+ // temporarily skipping this test
+ Log.i(TAG, "skipping testRingtonePlayerUriUserCheck");
+ return;
- assertThrows(SecurityException.class, () ->
- irp.getTitle(uri));
- }
+ // TODO change how IRingtonePlayer is created
+// android.media.IRingtonePlayer irp = mAudioManager.getRingtonePlayer();
+// final AudioAttributes aa = new AudioAttributes.Builder()
+// .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
+// // get a UserId that doesn't belong to mine
+// final int otherUserId = UserHandle.myUserId() == 0 ? 10 : 0;
+// // build a URI that I shouldn't have access to
+// final Uri uri = new Uri.Builder()
+// .scheme("content").authority(otherUserId + "@media")
+// .appendPath("external").appendPath("downloads")
+// .appendPath("bogusPathThatDoesNotMatter.mp3")
+// .build();
+// if (android.media.audio.Flags.ringtoneUserUriCheck()) {
+// assertThrows(SecurityException.class, () ->
+// irp.play(new Binder(), uri, aa, 1.0f /*volume*/, false /*looping*/)
+// );
+//
+// assertThrows(SecurityException.class, () ->
+// irp.getTitle(uri));
+// }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 3937d3d46..ff17a36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -20,7 +20,6 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -97,7 +96,6 @@
fun themeChangePropagatesToEntry() {
configurationListener.onThemeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
}
@@ -105,7 +103,6 @@
fun densityChangePropagatesToEntry() {
configurationListener.onDensityOrFontScaleChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
}
@@ -129,7 +126,6 @@
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
@@ -160,7 +156,6 @@
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
@@ -196,14 +191,7 @@
verify(entry).row
verify(row).onUiModeChanged()
verify(entry).onDensityOrFontScaleChanged()
- checkGutsExposedCalled()
verifyNoMoreInteractions(entry, row)
clearInvocations(entry, row)
}
-
- private fun checkGutsExposedCalled() {
- if (!Flags.notificationUndoGutsOnConfigChanged()) {
- verify(entry).areGutsExposed()
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 84f39be..2ea4e7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -61,6 +61,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.CachingIconView;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
@@ -71,12 +72,18 @@
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
@@ -173,6 +180,7 @@
}
@Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void testUpdateBackgroundColors_isRecursive() throws Exception {
ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
group.setTintColor(Color.RED);
@@ -597,14 +605,14 @@
public void testGetIsNonblockable() throws Exception {
ExpandableNotificationRow row =
mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
- row.setEntry(null);
+ row.setEntryLegacy(null);
assertTrue(row.getIsNonblockable());
NotificationEntry entry = mock(NotificationEntry.class);
Mockito.doReturn(false, true).when(entry).isBlockable();
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
assertTrue(row.getIsNonblockable());
assertFalse(row.getIsNonblockable());
}
@@ -939,12 +947,14 @@
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_sensitivePromotedNotification_notExpanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
@@ -954,12 +964,14 @@
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationNotOnKeyguard_expanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(false);
// THEN
@@ -968,12 +980,14 @@
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationAllowOnKeyguard_expanded() throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
// THEN
@@ -982,13 +996,15 @@
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationIgnoreLockscreenConstraints_expanded()
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setIgnoreLockscreenConstraints(true);
@@ -996,15 +1012,35 @@
assertThat(row.isExpanded()).isTrue();
}
+ private static void setRowPromotedOngoing(ExpandableNotificationRow row) {
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ when(entry.isPromotedOngoing()).thenReturn(true);
+ if (NotificationBundleUi.isEnabled()) {
+ final EntryAdapter entryAdapter = new NotificationEntryAdapter(
+ mock(NotificationActivityStarter.class),
+ mock(MetricsLogger.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(NotificationIconStyleProvider.class),
+ mock(VisualStabilityCoordinator.class),
+ mock(NotificationActionClickManager.class),
+ entry);
+ row.setEntryAdapter(entryAdapter);
+ } else {
+ row.setEntryLegacy(entry);
+ }
+ }
+
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationSaveSpaceOnLockScreen_notExpanded()
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setSaveSpaceOnLockscreen(true);
@@ -1014,13 +1050,15 @@
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void isExpanded_promotedNotificationNotSaveSpaceOnLockScreen_expanded()
throws Exception {
// GIVEN
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isPromotedOngoing()).thenReturn(true);
- row.setEntry(entry);
+ row.setEntryLegacy(entry);
+ setRowPromotedOngoing(row);
row.setOnKeyguard(true);
row.setSaveSpaceOnLockscreen(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index c874bc6..cf8278e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -19,6 +19,7 @@
import android.annotation.DimenRes
import android.content.res.Resources
import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
import android.service.notification.StatusBarNotification
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -93,13 +94,12 @@
/* attrs= */ null,
UserHandle.CURRENT
).apply {
- entry = mockEntry
entryAdapter = mockEntryAdapter
}
}
false -> {
ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply {
- entry = mockEntry
+ entryLegacy = mockEntry
}
}
}
@@ -402,6 +402,7 @@
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun setExpandedChild_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
// Bubble button should not be shown for the given NotificationEntry
@@ -429,6 +430,7 @@
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun setExpandedChild_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
// Bubble button should be shown for the given NotificationEntry
@@ -458,6 +460,7 @@
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun onNotificationUpdated_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
val mockNotificationEntry = createMockNotificationEntry()
@@ -486,6 +489,7 @@
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun onNotificationUpdated_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
val mockNotificationEntry = createMockNotificationEntry()
@@ -514,7 +518,7 @@
// Given: controller says bubbles are enabled for the user
view.setBubblesEnabledForUser(true)
- // Then: bottom margin of actionListMarginTarget should not change, still be 20
+ // Then: bottom margin of actionListMarginTarget should be changed to 0
assertEquals(0, getMarginBottom(actionListMarginTarget))
}
@@ -628,8 +632,7 @@
whenever(sbnMock.user).thenReturn(userMock)
}
- private fun createMockNotificationEntryAdapter() =
- mock<EntryAdapter>()
+ private fun createMockNotificationEntryAdapter() = mock<EntryAdapter>()
private fun createLinearLayoutWithBottomMargin(bottomMargin: Int): LinearLayout {
val outerLayout = LinearLayout(mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 0c0ef9d..10de866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -67,6 +67,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -146,6 +147,7 @@
@Mock private lateinit var notificationManager: INotificationManager
@Mock private lateinit var shortcutManager: ShortcutManager
@Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+ @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
@Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock private lateinit var contextTracker: UserContextProvider
@Mock private lateinit var bubblesManager: BubblesManager
@@ -185,6 +187,7 @@
launcherApps,
shortcutManager,
channelEditorDialogController,
+ packageDemotionInteractor,
contextTracker,
assistantFeedbackController,
Optional.of(bubblesManager),
@@ -297,45 +300,6 @@
}
@Test
- fun testChangeDensityOrFontScale() {
- val guts = spy(NotificationGuts(mContext))
- whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
- handler.post((invocation.arguments[0] as Runnable))
- null
- }
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing()
- .whenever(guts)
- .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
- val realRow = createTestNotificationRow()
- val menuItem = createTestMenuItem(realRow)
- val row = spy(realRow)
- whenever(row!!.windowToken).thenReturn(Binder())
- whenever(row.guts).thenReturn(guts)
- doNothing().whenever(row).ensureGutsInflated()
- val realEntry = realRow!!.entry
- val entry = spy(realEntry)
- whenever(entry.row).thenReturn(row)
- whenever(entry.getGuts()).thenReturn(guts)
- Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
- executor.runAllReady()
- verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
-
- // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
- verify(row).setGutsView(any())
- row.onDensityOrFontScaleChanged()
- gutsManager.onDensityOrFontScaleChanged(entry)
- executor.runAllReady()
- gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
- verify(guts)
- .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>())
-
- // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
- verify(row, times(2)).setGutsView(any())
- }
-
- @Test
fun testAppOpsSettingsIntent_camera() {
val ops = ArraySet<Int>()
ops.add(AppOpsManager.OP_CAMERA)
@@ -427,6 +391,7 @@
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -438,6 +403,7 @@
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -447,7 +413,8 @@
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(true), /* wasShownHighPriority */
+ eq(true),
+ eq(true),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
@@ -462,6 +429,7 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -473,6 +441,7 @@
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -482,7 +451,8 @@
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(false), /* wasShownHighPriority */
+ eq(true), /* wasShownHighPriority */
+ eq(false),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
@@ -497,6 +467,7 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
+ whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -508,6 +479,7 @@
eq(iconStyleProvider),
eq(onUserInteractionCallback),
eq(channelEditorDialogController),
+ eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
any<NotificationChannel>(),
eq(entry),
@@ -517,7 +489,8 @@
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(false), /* wasShownHighPriority */
+ eq(true), /* wasShownHighPriority */
+ eq(false),
eq(assistantFeedbackController),
any<MetricsLogger>(),
any<View.OnClickListener>(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index ffb861d..063b546 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -296,6 +296,7 @@
mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator,
new BlurConfig(0.0f, 0.0f),
+ mContext,
mKosmos::getWindowRootViewBlurInteractor);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
@@ -1247,6 +1248,7 @@
mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator,
new BlurConfig(0.0f, 0.0f),
+ mContext,
mKosmos::getWindowRootViewBlurInteractor);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
index 5f34420..422b20e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -45,7 +45,7 @@
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@SmallTest
@@ -92,7 +92,7 @@
engine.onSurfaceRedrawNeeded(surfaceHolder)
- verifyZeroInteractions(canvas)
+ verifyNoMoreInteractions(canvas)
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 663a853..122e6a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.display.data.repository
import android.view.Display
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.mockito.mock
import dagger.Binds
@@ -41,16 +42,15 @@
}
/** Creates a mock [DisplayRepository.PendingDisplay]. */
-fun createPendingDisplay(id: Int = 0): DisplayRepository.PendingDisplay =
- mock<DisplayRepository.PendingDisplay> { whenever(this.id).thenReturn(id) }
+fun createPendingDisplay(id: Int = 0): PendingDisplay =
+ mock<PendingDisplay> { whenever(this.id).thenReturn(id) }
@SysUISingleton
/** Fake [DisplayRepository] implementation for testing. */
class FakeDisplayRepository @Inject constructor() : DisplayRepository {
private val flow = MutableStateFlow<Set<Display>>(emptySet())
private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
- private val pendingDisplayFlow =
- MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
+ private val pendingDisplayFlow = MutableSharedFlow<PendingDisplay?>(replay = 1)
private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
private val displayIdsWithSystemDecorationsFlow = MutableStateFlow<Set<Int>>(emptySet())
@@ -101,7 +101,7 @@
suspend fun emit(value: Set<Display>) = flow.emit(value)
/** Emits [value] as [pendingDisplay] flow value. */
- suspend fun emit(value: DisplayRepository.PendingDisplay?) = pendingDisplayFlow.emit(value)
+ suspend fun emit(value: PendingDisplay?) = pendingDisplayFlow.emit(value)
override val displays: StateFlow<Set<Display>>
get() = flow
@@ -109,7 +109,7 @@
override val displayIds: StateFlow<Set<Int>>
get() = displayIdFlow
- override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
+ override val pendingDisplay: Flow<PendingDisplay?>
get() = pendingDisplayFlow
private val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
index aa23aa3..4b516e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -16,6 +16,8 @@
package com.android.systemui.display.data.repository
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -66,6 +68,7 @@
val Kosmos.fakePerDisplayInstanceProviderWithTeardown by
Kosmos.Fixture { FakePerDisplayInstanceProviderWithTeardown() }
+val Kosmos.perDisplayDumpHelper by Kosmos.Fixture { PerDisplayRepoDumpHelper(dumpManager) }
val Kosmos.fakePerDisplayInstanceRepository by
Kosmos.Fixture {
PerDisplayInstanceRepositoryImpl(
@@ -73,6 +76,6 @@
instanceProvider = fakePerDisplayInstanceProviderWithTeardown,
testScope.backgroundScope,
displayRepository,
- dumpManager,
+ perDisplayDumpHelper,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
similarity index 66%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
index 3cec5a9..361d21d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.keyguard.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-val Kosmos.keyguardServiceShowLockscreenInteractor by
- Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+val Kosmos.keyguardServiceShowLockscreenRepository by
+ Kosmos.Fixture { KeyguardServiceShowLockscreenRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
new file mode 100644
index 0000000..447aa12
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardServiceShowLockscreenRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.keyguardServiceShowLockscreenInteractor by
+ Kosmos.Fixture {
+ KeyguardServiceShowLockscreenInteractor(
+ backgroundScope = testScope,
+ selectedUserInteractor = selectedUserInteractor,
+ repository = keyguardServiceShowLockscreenRepository,
+ userTracker = userTracker,
+ wmLockscreenVisibilityInteractor = { windowManagerLockscreenVisibilityInteractor },
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
index f7caeb6..dd868e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
@@ -18,10 +18,12 @@
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
val Kosmos.keyguardShowWhileAwakeInteractor by
Kosmos.Fixture {
KeyguardShowWhileAwakeInteractor(
+ backgroundScope = testScope,
biometricSettingsRepository = biometricSettingsRepository,
keyguardEnabledInteractor = keyguardEnabledInteractor,
keyguardServiceShowLockscreenInteractor = keyguardServiceShowLockscreenInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 43fa718..c89fb70 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -27,7 +27,7 @@
import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.time.systemClock
-val Kosmos.keyguardWakeDirectlyToGoneInteractor by
+val Kosmos.keyguardWakeDirectlyToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor by
Kosmos.Fixture {
KeyguardWakeDirectlyToGoneInteractor(
applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
index 11bd4c7..54261c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
@@ -38,7 +38,9 @@
}
}
-val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture { FakePerDisplayRepository<SysUiState>() }
+val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture {
+ FakePerDisplayRepository<SysUiState>().apply { add(Display.DEFAULT_DISPLAY, sysUiState) }
+}
val Kosmos.sysuiStateInteractor by Fixture {
SysUIStateDisplaysInteractor(fakeSysUIStatePerDisplayRepository, displayRepository)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index 4efcada..215df9d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -58,7 +58,7 @@
return this;
}
- /** Sets the creation time. */
+ /** Sets the creation time. Should be SystemClock.elapsedRealtime */
public GroupEntryBuilder setCreationTime(long creationTime) {
mCreationTime = creationTime;
return this;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
similarity index 66%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
index 3cec5a9..38b5994 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-val Kosmos.keyguardServiceShowLockscreenInteractor by
- Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
+val Kosmos.packageDemotionInteractor by Kosmos.Fixture { PackageDemotionInteractor() }
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 1148539..083d2aa 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -180,9 +180,11 @@
// AUTO-GENERATED-END
],
"ravenwood-postsubmit": [
- {
- "name": "SystemUiRavenTests",
- "host": true
- }
+ // We haven't maintained SystemUiRavenTests, and as a result, it's been demoted already.
+ // Disable it until we fix the issues: b/319647875
+ // {
+ // "name": "SystemUiRavenTests",
+ // "host": true
+ // }
]
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6c26c1f..703e37f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -531,7 +531,7 @@
AccessibilitySecurityPolicy securityPolicy,
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager a11yWindowManager,
- AccessibilityDisplayListener a11yDisplayListener,
+ AccessibilityDisplayListener.DisplayManagerWrapper displayManagerWrapper,
MagnificationController magnificationController,
@Nullable AccessibilityInputFilter inputFilter,
ProxyManager proxyManager,
@@ -550,7 +550,8 @@
mSecurityPolicy = securityPolicy;
mSystemActionPerformer = systemActionPerformer;
mA11yWindowManager = a11yWindowManager;
- mA11yDisplayListener = a11yDisplayListener;
+ mA11yDisplayListener = new AccessibilityDisplayListener(displayManagerWrapper,
+ new MainHandler(Looper.getMainLooper()));
mMagnificationController = magnificationController;
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
@@ -596,7 +597,8 @@
this, LocalServices.getService(PackageManagerInternal.class));
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
- mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+ mA11yDisplayListener = new AccessibilityDisplayListener(
+ new AccessibilityDisplayListener.DisplayManagerWrapper(mContext), mMainHandler);
mMagnificationController = new MagnificationController(
this,
mLock,
@@ -5457,11 +5459,11 @@
* A Utility class to handle display state.
*/
public class AccessibilityDisplayListener implements DisplayManager.DisplayListener {
- private final DisplayManager mDisplayManager;
+ private final DisplayManagerWrapper mDisplayManager;
private final ArrayList<Display> mDisplaysList = new ArrayList<>();
private int mSystemUiUid = 0;
- AccessibilityDisplayListener(Context context, Handler handler) {
+ AccessibilityDisplayListener(DisplayManagerWrapper displayManager, Handler handler) {
// Avoid concerns about one thread adding displays while another thread removes
// them by ensuring the looper is the main looper and the DisplayListener
// callbacks are always executed on the one main thread.
@@ -5474,7 +5476,7 @@
Slog.e(LOG_TAG, errorMessage);
}
- mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager = displayManager;
mDisplayManager.registerDisplayListener(this, handler);
initializeDisplayList();
@@ -5626,6 +5628,34 @@
}
return true;
}
+
+ /** Wrapper of DisplayManager for testing. */
+ @VisibleForTesting
+ static class DisplayManagerWrapper {
+ private final DisplayManager mDm;
+
+ DisplayManagerWrapper(Context context) {
+ mDm = context.getSystemService(DisplayManager.class);
+ }
+
+ /**
+ * @see DisplayManager#registerDisplayListener(DisplayManager.DisplayListener, Handler)
+ */
+ public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ @Nullable Handler handler) {
+ mDm.registerDisplayListener(listener, handler);
+ }
+
+ /** @see DisplayManager#getDisplays() */
+ public Display[] getDisplays() {
+ return mDm.getDisplays();
+ }
+
+ /** @see DisplayManager#getDisplay(int) */
+ public Display getDisplay(int displayId) {
+ return mDm.getDisplay(displayId);
+ }
+ }
}
/** Represents an {@link AccessibilityManager} */
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
index 805d7f8..94cef41 100644
--- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -165,6 +165,9 @@
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
if (com.android.server.accessibility.Flags.hearingInputChangeWhenCommDevice()) {
AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice();
+ if (commDevice == null) {
+ return;
+ }
mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice));
if (mHearingDevice != null) {
showNotificationIfNeeded();
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 23166a8..84158cf 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -24,6 +24,7 @@
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_REVERT_TO_LEFT_CLICK_DEFAULT;
import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
@@ -45,6 +46,7 @@
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
@@ -799,7 +801,7 @@
final long now = SystemClock.uptimeMillis();
- int actionButton;
+ int actionButton = BUTTON_PRIMARY;
if (mHoveredState) {
// Always triggers left-click when the cursor hovers over the autoclick type
// panel, to always allow users to change a different click type. Otherwise, if
@@ -807,15 +809,32 @@
// select other click types.
actionButton = BUTTON_PRIMARY;
} else {
- actionButton = mActiveClickType == AUTOCLICK_TYPE_RIGHT_CLICK
- ? BUTTON_SECONDARY
- : BUTTON_PRIMARY;
+ switch (mActiveClickType) {
+ case AUTOCLICK_TYPE_LEFT_CLICK:
+ actionButton = BUTTON_PRIMARY;
+ break;
+ case AUTOCLICK_TYPE_RIGHT_CLICK:
+ actionButton = BUTTON_SECONDARY;
+ break;
+ case AUTOCLICK_TYPE_DOUBLE_CLICK:
+ actionButton = BUTTON_PRIMARY;
+ long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+ sendMotionEvent(actionButton, now);
+ sendMotionEvent(actionButton, now + doubleTapMinimumTimeout);
+ return;
+ default:
+ break;
+ }
}
+ sendMotionEvent(actionButton, now);
+ }
+
+ private void sendMotionEvent(int actionButton, long eventTime) {
MotionEvent downEvent =
MotionEvent.obtain(
- /* downTime= */ now,
- /* eventTime= */ now,
+ /* downTime= */ eventTime,
+ /* eventTime= */ eventTime,
MotionEvent.ACTION_DOWN,
/* pointerCount= */ 1,
mTempPointerProperties,
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 88b7910..6e098d01 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -21,15 +21,18 @@
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.AttributionSource;
+import android.hardware.SensorAdditionalInfo;
import android.hardware.SensorDirectChannel;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -140,7 +143,7 @@
final IBinder sensorToken =
new Binder("android.hardware.sensor.VirtualSensor:" + config.getName());
VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(),
- virtualDevice, sensorToken);
+ config.getFlags(), virtualDevice, sensorToken);
synchronized (mLock) {
mSensorDescriptors.put(sensorToken, sensorDescriptor);
mVirtualSensors.put(handle, sensor);
@@ -164,6 +167,37 @@
}
}
+ boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+ @NonNull VirtualSensorAdditionalInfo info) {
+ Objects.requireNonNull(token);
+ Objects.requireNonNull(info);
+ synchronized (mLock) {
+ final SensorDescriptor sensorDescriptor = mSensorDescriptors.get(token);
+ long timestamp = SystemClock.elapsedRealtimeNanos();
+ if (sensorDescriptor == null) {
+ throw new IllegalArgumentException("Could not send sensor event for given token");
+ }
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_BEGIN,
+ /* serial= */ 0, timestamp++, /* values= */ null)) {
+ return false;
+ }
+ for (int i = 0; i < info.getValues().size(); ++i) {
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), info.getType(), /* serial= */ i,
+ timestamp++, info.getValues().get(i))) {
+ return false;
+ }
+ }
+ if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+ sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_END,
+ /* serial= */ 0, timestamp, /* values= */ null)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Nullable
VirtualSensor getSensorByHandle(int handle) {
synchronized (mLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 28efdfc..0023b6d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -55,6 +55,7 @@
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.companion.virtualdevice.flags.Flags;
import android.compat.annotation.ChangeId;
@@ -1294,6 +1295,18 @@
}
@Override // Binder call
+ public boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+ @NonNull VirtualSensorAdditionalInfo info) {
+ checkCallerIsDeviceOwner();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mSensorController.sendSensorAdditionalInfo(token, info);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
IntentFilter filter) {
checkCallerIsDeviceOwner();
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 7eb7072..a1d39fa 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -313,7 +313,8 @@
android.Manifest.permission.CREATE_USERS,
android.Manifest.permission.QUERY_USERS
})
- private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
+ private Intent getContextualSearchIntent(int entrypoint, int userId, String callingPackage,
+ CallbackToken mToken) {
final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
if (DEBUG) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
@@ -332,6 +333,9 @@
launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_AUDIO_PLAYING,
mAudioManager.isMusicActive());
}
+ if (Flags.selfInvocation()) {
+ launchIntent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
+ }
boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
final List<IBinder> activityTokens = new ArrayList<>(records.size());
@@ -500,6 +504,7 @@
}
private void startContextualSearchInternal(int entrypoint) {
+ final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid());
final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
mAssistDataRequester.cancel();
// Creates a new CallbackToken at mToken and an expiration handler.
@@ -508,7 +513,8 @@
// server has READ_FRAME_BUFFER permission to get the screenshot and because only
// the system server can invoke non-exported activities.
Binder.withCleanCallingIdentity(() -> {
- Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, mToken);
+ Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId,
+ callingPackage, mToken);
if (launchIntent != null) {
int result = invokeContextualSearchIntent(launchIntent, callingUserId);
if (DEBUG) {
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 4b6d6bc..fd2d835 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -61,7 +61,7 @@
per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
# Miscellaneous
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com
per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
# Activity Security
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index fb33cb1..72de8d5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1059,7 +1059,7 @@
if (Flags.enableAllSqliteAppopsAccesses()) {
mHistoricalRegistry = new HistoricalRegistrySql(context);
} else {
- mHistoricalRegistry = new HistoricalRegistry(this, context);
+ mHistoricalRegistry = new LegacyHistoricalRegistry(this, context);
}
}
@@ -7011,7 +7011,8 @@
mHistoricalRegistry = new HistoricalRegistrySql(
(HistoricalRegistrySql) mHistoricalRegistry);
} else {
- mHistoricalRegistry = new HistoricalRegistry((HistoricalRegistry) mHistoricalRegistry);
+ mHistoricalRegistry = new LegacyHistoricalRegistry(
+ (LegacyHistoricalRegistry) mHistoricalRegistry);
}
mHistoricalRegistry.systemReady(mContext.getContentResolver());
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index c53e4bd..3a8d583 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -24,6 +24,7 @@
import android.database.DefaultDatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteRawStatement;
import android.os.Environment;
@@ -174,11 +175,16 @@
if (DEBUG) {
Slog.i(LOG_TAG, "DB execSQL, sql: " + sql);
}
- SQLiteDatabase db = getWritableDatabase();
- if (bindArgs == null) {
- db.execSQL(sql);
- } else {
- db.execSQL(sql, bindArgs);
+ try {
+ SQLiteDatabase db = getWritableDatabase();
+ if (bindArgs == null) {
+ db.execSQL(sql);
+ } else {
+ db.execSQL(sql, bindArgs);
+ }
+ } catch (SQLiteFullException exception) {
+ Slog.e(LOG_TAG, "Couldn't exec sql command, disk is full. Discrete ops "
+ + "db file size (bytes) : " + getDatabaseFile().length(), exception);
}
}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
index 70b7016..3867cbe 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -82,9 +82,8 @@
* INITIALIZATION: We can initialize persistence only after the system is ready
* as we need to check the optional configuration override from the settings
* database which is not initialized at the time the app ops service is created. This class
- * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
- * outside calls are going through {@link HistoricalRegistry}.
- *
+ * relies on {@link LegacyHistoricalRegistry} for controlling that no calls are allowed until then.
+ * All outside calls are going through {@link LegacyHistoricalRegistry}.
*/
abstract class DiscreteOpsRegistry {
private static final String TAG = DiscreteOpsRegistry.class.getSimpleName();
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
index 20706b6..771df19 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
@@ -81,7 +81,7 @@
* THREADING AND LOCKING:
* For in-memory transactions this class relies on {@link DiscreteOpsXmlRegistry#mInMemoryLock}.
* It is assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
- * {@link HistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
+ * {@link LegacyHistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
* {@link DiscreteOpsRegistry#recordDiscreteAccess} must only be called while holding this lock.
* {@link DiscreteOpsXmlRegistry#mOnDiskLock} is used when disk transactions are performed.
* It is very important to release {@link DiscreteOpsXmlRegistry#mInMemoryLock} as soon as
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
similarity index 99%
rename from services/core/java/com/android/server/appop/HistoricalRegistry.java
rename to services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
index a8128dd..f4f5775 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
@@ -128,11 +128,11 @@
*/
// TODO (bug:122218838): Make sure we handle start of epoch time
// TODO (bug:122218838): Validate changed time is handled correctly
-final class HistoricalRegistry implements HistoricalRegistryInterface {
+final class LegacyHistoricalRegistry implements HistoricalRegistryInterface {
private static final boolean DEBUG = false;
private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE;
- private static final String LOG_TAG = HistoricalRegistry.class.getSimpleName();
+ private static final String LOG_TAG = LegacyHistoricalRegistry.class.getSimpleName();
private static final String PARAMETER_DELIMITER = ",";
private static final String PARAMETER_ASSIGNMENT = "=";
@@ -200,7 +200,7 @@
private final Context mContext;
- HistoricalRegistry(@NonNull Object lock, Context context) {
+ LegacyHistoricalRegistry(@NonNull Object lock, Context context) {
mInMemoryLock = lock;
mContext = context;
if (Flags.enableSqliteAppopsAccesses()) {
@@ -210,7 +210,7 @@
}
}
- HistoricalRegistry(@NonNull HistoricalRegistry other) {
+ LegacyHistoricalRegistry(@NonNull LegacyHistoricalRegistry other) {
this(other.mInMemoryLock, other.mContext);
mMode = other.mMode;
mBaseSnapshotInterval = other.mBaseSnapshotInterval;
@@ -313,9 +313,9 @@
final int mode = AppOpsManager.parseHistoricalMode(modeValue);
final long baseSnapshotInterval = Long.parseLong(baseSnapshotIntervalValue);
final int intervalCompressionMultiplier = Integer.parseInt(intervalMultiplierValue);
- setHistoryParameters(mode, baseSnapshotInterval,intervalCompressionMultiplier);
+ setHistoryParameters(mode, baseSnapshotInterval, intervalCompressionMultiplier);
return;
- } catch (NumberFormatException ignored) {}
+ } catch (NumberFormatException ignored) { }
}
Slog.w(LOG_TAG, "Bad value for" + Settings.Global.APPOP_HISTORY_PARAMETERS
+ "=" + setting + " resetting!");
@@ -805,7 +805,7 @@
private void schedulePersistHistoricalOpsMLocked(@NonNull HistoricalOps ops) {
final Message message = PooledLambda.obtainMessage(
- HistoricalRegistry::persistPendingHistory, HistoricalRegistry.this);
+ LegacyHistoricalRegistry::persistPendingHistory, LegacyHistoricalRegistry.this);
message.what = MSG_WRITE_PENDING_HISTORY;
IoThread.getHandler().sendMessage(message);
mPendingWrites.offerFirst(ops);
@@ -813,7 +813,7 @@
private static void makeRelativeToEpochStart(@NonNull HistoricalOps ops, long nowMillis) {
ops.setBeginAndEndTime(nowMillis - ops.getEndTimeMillis(),
- nowMillis- ops.getBeginTimeMillis());
+ nowMillis - ops.getBeginTimeMillis());
}
private void pruneFutureOps(@NonNull List<HistoricalOps> ops) {
@@ -979,7 +979,7 @@
final HistoricalOps readOp = readOps.get(i);
currentOps.merge(readOp);
}
- }
+ }
}
private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(int filterUid,
@@ -1125,7 +1125,7 @@
if (existingOpCount > 0) {
// Compute elapsed time
final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1)
- .getEndTimeMillis();
+ .getEndTimeMillis();
for (int i = 0; i < existingOpCount; i++) {
final HistoricalOps existingOp = existingOps.get(i);
existingOp.offsetBeginAndEndTime(elapsedTimeMillis);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 11a5c6d..7f03b27 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -15123,6 +15123,61 @@
/**
* @hide
+ * Returns the current audio output device delay value of key in milliseconds.
+ *
+ * In aidl implementation, the param of getParameters is "key" and reply delay of all supported
+ * devices which be composited as "key=device,address,delay|device,address,delay|...".
+ * e.g.
+ * param: additional_output_device_delay=
+ * reply: additional_output_device_delay=2,,0|4,,0|8,,0|128,,0|256,,0|512,,0|1024,,0|8192,,0|
+ * 16384,,0|262144,,0|262145,,0|524288,,0|67108864,,0|134217728,,0|536870912,,0|536870913,,0|
+ * 536870914,,0
+ *
+ * In hidl implementation, the param of getParameters is "key=device,address" and reply a
+ * specific device delay which be composited as "key=device,address,delay".
+ * e.g.
+ * param: additional_output_device_delay=2,
+ * reply: additional_output_device_delay=2,,0
+ *
+ * @param key
+ * @param deviceType
+ * @param address
+ * @return the delay value of key. This is a non-negative number.
+ * {@code 0} is returned if unsupported.
+ */
+ private long getDelayByKeyDevice(@NonNull String key, @NonNull AudioDeviceAttributes device) {
+ long delayMillis = 0;
+
+ try {
+ if (AudioHalVersionInfo.AUDIO_HAL_TYPE_AIDL == getHalVersion().getHalType()) {
+ final String reply = AudioSystem.getParameters(key);
+ final String keyDeviceAddressPrefix =
+ Integer.toUnsignedString(device.getInternalType()) + "," + device.getAddress() +
+ ",";
+ int start = reply.indexOf(keyDeviceAddressPrefix);
+ int end = -1;
+ if (start != -1) {
+ start += keyDeviceAddressPrefix.length();
+ end = reply.indexOf("|", start);
+ delayMillis = Long.parseLong(
+ end == -1 ? reply.substring(start) : reply.substring(start, end));
+ }
+ } else {
+ final String reply = AudioSystem.getParameters(
+ key + "=" + device.getInternalType() + "," + device.getAddress());
+ if (reply.contains(key)) {
+ delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+ }
+ }
+ } catch (NullPointerException e) {
+ Log.w(TAG, "NullPointerException when getting delay for device " + device, e);
+ }
+
+ return delayMillis;
+ }
+
+ /**
+ * @hide
* Returns the current additional audio output device delay in milliseconds.
*
* @param deviceType
@@ -15138,17 +15193,8 @@
device = retrieveBluetoothAddress(device);
final String key = "additional_output_device_delay";
- final String reply = AudioSystem.getParameters(
- key + "=" + device.getInternalType() + "," + device.getAddress());
- long delayMillis = 0;
- if (reply.contains(key)) {
- try {
- delayMillis = Long.parseLong(reply.substring(key.length() + 1));
- } catch (NullPointerException e) {
- delayMillis = 0;
- }
- }
- return delayMillis;
+
+ return getDelayByKeyDevice(key, device);
}
/**
@@ -15170,17 +15216,8 @@
device = retrieveBluetoothAddress(device);
final String key = "max_additional_output_device_delay";
- final String reply = AudioSystem.getParameters(
- key + "=" + device.getInternalType() + "," + device.getAddress());
- long delayMillis = 0;
- if (reply.contains(key)) {
- try {
- delayMillis = Long.parseLong(reply.substring(key.length() + 1));
- } catch (NullPointerException e) {
- delayMillis = 0;
- }
- }
- return delayMillis;
+
+ return getDelayByKeyDevice(key, device);
}
@android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ac0892b..aa98590 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1113,7 +1113,11 @@
}
// Remove always-on VPN if it's not supported.
if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
- setAlwaysOnPackage(null, false, null);
+ // Do not remove the always-on setting due to the restricted ability in safe mode.
+ // The always-on VPN can then start after the device reboots to normal mode.
+ if (!mContext.getPackageManager().isSafeMode()) {
+ setAlwaysOnPackage(null, false, null);
+ }
return false;
}
// Skip if the service is already established. This isn't bulletproof: it's not bound
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 3aaf4f6..7450dff 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -122,8 +122,8 @@
public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
/**
- * Flag: This flag identifies secondary displays that should show system decorations, such as
- * navigation bar, home activity or wallpaper.
+ * Flag: This flag identifies secondary displays that should always show system decorations,
+ * such as navigation bar, home activity or wallpaper.
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
* @hide
*/
@@ -191,6 +191,19 @@
public static final int FLAG_STEAL_TOP_FOCUS_DISABLED = 1 << 19;
/**
+ * Flag: Indicates that the display is allowed to switch the content mode between
+ * projected/extended and mirroring. This allows the display to dynamically add or remove the
+ * home and system decorations.
+ *
+ * Note that this flag should not be enabled with any of {@link #FLAG_PRIVATE},
+ * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}, or {@link #FLAG_OWN_CONTENT_ONLY} at the
+ * same time; otherwise it will be ignored.
+ *
+ * @hide
+ */
+ public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 20;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index f73b66c..ce8d8a6 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import android.util.IndentingPrintWriter;
+
import java.util.ArrayList;
import java.util.List;
@@ -97,4 +99,14 @@
}
return displayIds;
}
+
+ /** Dumps information about the DisplayGroup. */
+ void dumpLocked(IndentingPrintWriter ipw) {
+ final int numDisplays = mDisplays.size();
+ for (int i = 0; i < numDisplays; i++) {
+ LogicalDisplay logicalDisplay = mDisplays.get(i);
+ ipw.println("Display " + logicalDisplay.getDisplayIdLocked() + " "
+ + logicalDisplay.getPrimaryDisplayDeviceLocked());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7b714ad..2cad7ed 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -766,7 +766,7 @@
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
} else {
- if (!res.getBoolean(R.bool.config_localDisplaysMirrorContent)) {
+ if (shouldOwnContentOnly()) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
}
@@ -780,6 +780,15 @@
}
}
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ // Public display with FLAG_OWN_CONTENT_ONLY disabled is allowed to switch the
+ // content mode.
+ if (mIsFirstDisplay
+ || (!isDisplayPrivate(physicalAddress) && !shouldOwnContentOnly())) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
+ }
+
if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
}
@@ -822,6 +831,7 @@
R.string.display_manager_hdmi_display_name);
}
}
+
mInfo.frameRateOverrides = mFrameRateOverrides;
// The display is trusted since it is created by system.
@@ -1467,6 +1477,11 @@
return false;
}
+ private boolean shouldOwnContentOnly() {
+ final Resources res = getOverlayContext().getResources();
+ return !res.getBoolean(R.bool.config_localDisplaysMirrorContent);
+ }
+
private boolean isDisplayStealTopFocusDisabled(DisplayAddress.Physical physicalAddress) {
if (physicalAddress == null) {
return false;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b2b9ef1..0e6870f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -489,6 +489,11 @@
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_STEAL_TOP_FOCUS_DISABLED;
}
+ // Rear display should not be allowed to use the content mode switch.
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0
+ && mDevicePosition != Layout.Display.POSITION_REAR) {
+ mBaseDisplayInfo.flags |= Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
@@ -1155,6 +1160,7 @@
pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
+ pw.println("mDisplayGroupId=" + mDisplayGroupId);
pw.println("mDisplayGroupName=" + mDisplayGroupName);
pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
pw.println("mLeadDisplayId=" + mLeadDisplayId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index f4daf87..4a4c616 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -478,6 +478,21 @@
ipw.decreaseIndent();
ipw.println();
}
+
+ final int displayGroupCount = mDisplayGroups.size();
+ ipw.println();
+ ipw.println("Display Groups: size=" + displayGroupCount);
+ for (int i = 0; i < displayGroupCount; i++) {
+ int groupId = mDisplayGroups.keyAt(i);
+ DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
+ ipw.println("Group " + groupId + ":");
+ ipw.increaseIndent();
+ displayGroup.dumpLocked(ipw);
+ ipw.decreaseIndent();
+ ipw.println();
+ }
+
+
mDeviceStateToLayoutMap.dumpLocked(ipw);
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index b5a9b19..60b7fca 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -76,6 +76,7 @@
* <li><code>secure</code>: creates a secure display</li>
* <li><code>own_content_only</code>: only shows this display's own content</li>
* <li><code>should_show_system_decorations</code>: supports system decorations</li>
+ * <li><code>fixed_content_mode</code>: not allowed to switch content mode</li>
* <li><code>gravity_top_left</code>: display the overlay at the top left of the screen</li>
* <li><code>gravity_top_right</code>: display the overlay at the top right of the screen</li>
* <li><code>gravity_bottom_right</code>: display the overlay at the bottom right of the screen</li>
@@ -117,6 +118,18 @@
private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
"should_show_system_decorations";
+ /**
+ * When this flag is set, the overlay display is not allowed to switch content mode.
+ * Note that it is the opposite of {@link DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH},
+ * because we want overlay displays (such as those used for connected display simulation in
+ * development) to have {@link DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH} enabled by
+ * default without explicitly specifying it.
+ *
+ * @see DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH
+ */
+ private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE =
+ "fixed_content_mode";
+
// Gravity flags to decide where the overlay should be shown.
private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
@@ -384,6 +397,17 @@
if (mFlags.mShouldShowSystemDecorations) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ if (!mFlags.mFixedContentMode
+ && !mFlags.mOwnContentOnly
+ && !mFlags.mShouldShowSystemDecorations) {
+ // For overlay displays, if FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS and
+ // FLAG_OWN_CONTENT_ONLY are both disabled,
+ // then FLAG_ALLOWS_CONTENT_MODE_SWITCH should be enabled by default,
+ // unless OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE is set.
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
+ }
mInfo.type = Display.TYPE_OVERLAY;
mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
mInfo.state = mState;
@@ -628,16 +652,21 @@
/** See {@link #OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
final boolean mShouldShowSystemDecorations;
+ /** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */
+ final boolean mFixedContentMode;
+
final int mGravity;
OverlayFlags(
boolean secure,
boolean ownContentOnly,
boolean shouldShowSystemDecorations,
+ boolean fixedContentMode,
int gravity) {
mSecure = secure;
mOwnContentOnly = ownContentOnly;
mShouldShowSystemDecorations = shouldShowSystemDecorations;
+ mFixedContentMode = fixedContentMode;
mGravity = gravity;
}
@@ -647,12 +676,14 @@
false /* secure */,
false /* ownContentOnly */,
false /* shouldShowSystemDecorations */,
+ false /* fixedContentMode */,
Gravity.NO_GRAVITY);
}
boolean secure = false;
boolean ownContentOnly = false;
boolean shouldShowSystemDecorations = false;
+ boolean fixedContentMode = false;
int gravity = Gravity.NO_GRAVITY;
for (String flag: flagString.split(FLAG_SPLITTER)) {
if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
@@ -661,11 +692,14 @@
ownContentOnly = true;
} else if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
shouldShowSystemDecorations = true;
+ } else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) {
+ fixedContentMode = true;
} else {
gravity = parseOverlayGravity(flag);
}
}
- return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, gravity);
+ return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations,
+ fixedContentMode, gravity);
}
@Override
@@ -674,6 +708,7 @@
.append("secure=").append(mSecure)
.append(", ownContentOnly=").append(mOwnContentOnly)
.append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
+ .append(", fixedContentMode=").append(mFixedContentMode)
.append(", gravity").append(Gravity.toString(mGravity))
.append("}")
.toString();
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 902eefa..89679c7 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -666,6 +666,12 @@
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
// The display is trusted since it is created by system.
mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
+ if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+ // The wifi display is allowed to switch content mode since FLAG_PRIVATE,
+ // FLAG_OWN_CONTENT_ONLY, and FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS are not
+ // enabled in WifiDisplayDevice#getDisplayDeviceInfoLocked().
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ }
mInfo.displayShape =
DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false);
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 7cc178d..f5228df 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -280,6 +280,11 @@
Flags::committedStateSeparateEvent
);
+ private final FlagState mSeparateTimeouts = new FlagState(
+ Flags.FLAG_SEPARATE_TIMEOUTS,
+ Flags::separateTimeouts
+ );
+
private final FlagState mDelayImplicitRrRegistrationUntilRrAccessed = new FlagState(
Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED,
Flags::delayImplicitRrRegistrationUntilRrAccessed
@@ -608,6 +613,14 @@
}
/**
+ * @return {@code true} if the flag for having a separate timeouts for power groups
+ * is enabled
+ */
+ public boolean isSeparateTimeoutsEnabled() {
+ return mSeparateTimeouts.isEnabled();
+ }
+
+ /**
* @return {@code true} if the flag for only explicit subscription for RR changes is enabled
*/
public boolean isDelayImplicitRrRegistrationUntilRrAccessedEnabled() {
@@ -671,6 +684,7 @@
pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
pw.println(" " + mRefreshRateEventForForegroundApps);
pw.println(" " + mCommittedStateSeparateEvent);
+ pw.println(" " + mSeparateTimeouts);
pw.println(" " + mDelayImplicitRrRegistrationUntilRrAccessed);
}
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index a0064a9..007646f 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -509,6 +509,14 @@
}
}
+
+flag {
+ name: "separate_timeouts"
+ namespace: "lse_desktop_experience"
+ description: "Allow separate timeouts for different power groups"
+ bug: "402356291"
+}
+
flag {
name: "delay_implicit_rr_registration_until_rr_accessed"
namespace: "display_manager"
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2375775..fde9165 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5669,11 +5669,6 @@
LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal);
}
- // TODO(b/352228316): Remove it once IMMIProxy is removed.
- InputMethodManagerInternal getLocalService(){
- return mInputMethodManagerInternal;
- }
-
private final class LocalServiceImpl extends InputMethodManagerInternal {
@ImfLockFree
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 6a72cc7..7d9f2c2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -24,6 +24,7 @@
import android.hardware.location.GeofenceHardwareImpl;
import android.location.FusedBatchOptions;
import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementRequest;
@@ -35,6 +36,8 @@
import android.location.IGpsGeofenceHardware;
import android.location.Location;
import android.location.LocationManager;
+import android.location.flags.Flags;
+import android.location.provider.IGnssAssistanceCallback;
import android.location.util.identity.CallerIdentity;
import android.os.BatteryStats;
import android.os.Binder;
@@ -47,12 +50,13 @@
import com.android.server.FgThread;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.Injector;
+import com.android.server.location.provider.proxy.ProxyGnssAssistanceProvider;
import java.io.FileDescriptor;
import java.util.List;
/** Manages Gnss providers and related Gnss functions for LocationManagerService. */
-public class GnssManagerService {
+public class GnssManagerService implements GnssNative.GnssAssistanceCallbacks {
public static final String TAG = "GnssManager";
public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
@@ -75,6 +79,8 @@
private final GnssMetrics mGnssMetrics;
+ private @Nullable ProxyGnssAssistanceProvider mProxyGnssAssistanceProvider = null;
+
public GnssManagerService(Context context, Injector injector, GnssNative gnssNative) {
mContext = context.createAttributionContext(ATTRIBUTION_ID);
mGnssNative = gnssNative;
@@ -100,6 +106,16 @@
/** Called when system is ready. */
public void onSystemReady() {
mGnssLocationProvider.onSystemReady();
+
+ if (Flags.gnssAssistanceInterfaceJni()) {
+ mProxyGnssAssistanceProvider =
+ ProxyGnssAssistanceProvider.createAndRegister(mContext);
+ if (mProxyGnssAssistanceProvider == null) {
+ Log.e(TAG, "no gnss assistance provider found");
+ } else {
+ mGnssNative.setGnssAssistanceCallbacks(this);
+ }
+ }
}
/** Retrieve the GnssLocationProvider. */
@@ -323,6 +339,29 @@
}
}
+ @Override
+ public void onRequestGnssAssistanceInject() {
+ if (!Flags.gnssAssistanceInterfaceJni()) {
+ return;
+ }
+ if (mProxyGnssAssistanceProvider == null) {
+ Log.e(TAG, "ProxyGnssAssistanceProvider is null");
+ return;
+ }
+ mProxyGnssAssistanceProvider.request(new IGnssAssistanceCallback.Stub() {
+ @Override
+ public void onError() {
+ Log.e(TAG, "GnssAssistanceCallback.onError");
+ }
+
+ @Override
+ public void onResult(GnssAssistance gnssAssistance) {
+ Log.d(TAG, "GnssAssistanceCallback.onResult");
+ mGnssNative.injectGnssAssistance(gnssAssistance);
+ }
+ });
+ }
+
private class GnssCapabilitiesHalModule implements GnssNative.BaseCallbacks {
GnssCapabilitiesHalModule(GnssNative gnssNative) {
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index c79a21a..7b4c563 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementsEvent;
@@ -30,6 +31,7 @@
import android.location.GnssSignalType;
import android.location.GnssStatus;
import android.location.Location;
+import android.location.flags.Flags;
import android.os.Binder;
import android.os.Handler;
import android.os.SystemClock;
@@ -275,6 +277,12 @@
void onRequestPsdsDownload(int psdsType);
}
+ /** Callbacks for HAL requesting GNSS assistance. */
+ public interface GnssAssistanceCallbacks {
+ /** On request GnssAssistance injection. */
+ void onRequestGnssAssistanceInject();
+ }
+
/** Callbacks for AGPS functionality. */
public interface AGpsCallbacks {
@@ -400,6 +408,7 @@
private TimeCallbacks mTimeCallbacks;
private LocationRequestCallbacks mLocationRequestCallbacks;
private PsdsCallbacks mPsdsCallbacks;
+ private @Nullable GnssAssistanceCallbacks mGnssAssistanceCallbacks;
private AGpsCallbacks mAGpsCallbacks;
private NotificationCallbacks mNotificationCallbacks;
@@ -504,6 +513,16 @@
mNotificationCallbacks = Objects.requireNonNull(callbacks);
}
+ /** Sets GnssAssistanceCallbacks. */
+ public void setGnssAssistanceCallbacks(GnssAssistanceCallbacks callbacks) {
+ if (!Flags.gnssAssistanceInterfaceJni()) {
+ return;
+ }
+ Preconditions.checkState(!mRegistered);
+ Preconditions.checkState(mGnssAssistanceCallbacks == null);
+ mGnssAssistanceCallbacks = Objects.requireNonNull(callbacks);
+ }
+
/**
* Registers with the HAL and allows callbacks to begin. Once registered with the native HAL,
* no more callbacks can be added or set. Must only be called once.
@@ -1053,6 +1072,17 @@
mGnssHal.injectNiSuplMessageData(data, length, slotIndex);
}
+ /**
+ * Injects GNSS assistance data into the GNSS HAL.
+ */
+ public void injectGnssAssistance(GnssAssistance assistance) {
+ if (!Flags.gnssAssistanceInterfaceJni()) {
+ return;
+ }
+ Preconditions.checkState(mRegistered);
+ mGnssHal.injectGnssAssistance(assistance);
+ }
+
@NativeEntryPoint
void reportGnssServiceDied() {
// Not necessary to clear (and restore) binder identity since it runs on another thread.
@@ -1269,6 +1299,15 @@
}
@NativeEntryPoint
+ void gnssAssistanceInjectRequest() {
+ if (!Flags.gnssAssistanceInterfaceJni() || mGnssAssistanceCallbacks == null) {
+ return;
+ }
+ Binder.withCleanCallingIdentity(
+ () -> mGnssAssistanceCallbacks.onRequestGnssAssistanceInject());
+ }
+
+ @NativeEntryPoint
void reportGeofenceTransition(int geofenceId, Location location, int transition,
long transitionTimestamp) {
Binder.withCleanCallingIdentity(
@@ -1569,6 +1608,10 @@
protected void injectNiSuplMessageData(byte[] data, int length, int slotIndex) {
native_inject_ni_supl_message_data(data, length, slotIndex);
}
+
+ protected void injectGnssAssistance(GnssAssistance gnssAssistance) {
+ native_inject_gnss_assistance(gnssAssistance);
+ }
}
// basic APIs
@@ -1718,4 +1761,7 @@
private static native boolean native_supports_psds();
private static native void native_inject_psds_data(byte[] data, int length, int psdsType);
+
+ // GNSS Assistance APIs
+ private static native void native_inject_gnss_assistance(GnssAssistance gnssAssistance);
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index dd52cce..3f2c222 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -46,7 +46,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
public class ConditionProviders extends ManagedServices {
@@ -203,14 +202,7 @@
@Override
protected void loadDefaultsFromConfig() {
- for (String dndPackage : getDefaultDndAccessPackages(mContext)) {
- addDefaultComponentOrPackage(dndPackage);
- }
- }
-
- static List<String> getDefaultDndAccessPackages(Context context) {
- ArrayList<String> packages = new ArrayList<>();
- String defaultDndAccess = context.getResources().getString(
+ String defaultDndAccess = mContext.getResources().getString(
R.string.config_defaultDndAccessPackages);
if (defaultDndAccess != null) {
String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
@@ -218,10 +210,9 @@
if (TextUtils.isEmpty(dnds[i])) {
continue;
}
- packages.add(dnds[i]);
+ addDefaultComponentOrPackage(dnds[i]);
}
}
- return packages;
}
@Override
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index dc7129cd..ea7ee4a 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
"name": "CtsNotificationTestCases_notification"
},
{
- "name": "FrameworksUiServicesTests_notification"
+ "name": "FrameworksUiServicesNotificationTests"
+ },
+ {
+ "name": "FrameworksUiServicesZenTests"
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java b/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
deleted file mode 100644
index d65954d..0000000
--- a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.service.notification.SystemZenRules;
-import android.service.notification.ZenModeConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-class ZenConfigTrimmer {
-
- private static final String TAG = "ZenConfigTrimmer";
- private static final int MAXIMUM_PARCELED_SIZE = 150_000; // bytes
-
- private final HashSet<String> mTrustedPackages;
-
- ZenConfigTrimmer(Context context) {
- mTrustedPackages = new HashSet<>();
- mTrustedPackages.add(SystemZenRules.PACKAGE_ANDROID);
- mTrustedPackages.addAll(ConditionProviders.getDefaultDndAccessPackages(context));
- }
-
- void trimToMaximumSize(ZenModeConfig config) {
- Map<String, PackageRules> rulesPerPackage = new HashMap<>();
- for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
- PackageRules pkgRules = rulesPerPackage.computeIfAbsent(rule.pkg, PackageRules::new);
- pkgRules.mRules.add(rule);
- }
-
- int totalSize = 0;
- for (PackageRules pkgRules : rulesPerPackage.values()) {
- totalSize += pkgRules.dataSize();
- }
-
- if (totalSize > MAXIMUM_PARCELED_SIZE) {
- List<PackageRules> deletionCandidates = new ArrayList<>();
- for (PackageRules pkgRules : rulesPerPackage.values()) {
- if (!mTrustedPackages.contains(pkgRules.mPkg)) {
- deletionCandidates.add(pkgRules);
- }
- }
- deletionCandidates.sort(Comparator.comparingInt(PackageRules::dataSize).reversed());
-
- evictPackagesFromConfig(config, deletionCandidates, totalSize);
- }
- }
-
- private static void evictPackagesFromConfig(ZenModeConfig config,
- List<PackageRules> deletionCandidates, int currentSize) {
- while (currentSize > MAXIMUM_PARCELED_SIZE && !deletionCandidates.isEmpty()) {
- PackageRules rulesToDelete = deletionCandidates.removeFirst();
- Slog.w(TAG, String.format("Evicting %s zen rules from package '%s' (%s bytes)",
- rulesToDelete.mRules.size(), rulesToDelete.mPkg, rulesToDelete.dataSize()));
-
- for (ZenModeConfig.ZenRule rule : rulesToDelete.mRules) {
- config.automaticRules.remove(rule.id);
- }
-
- currentSize -= rulesToDelete.dataSize();
- }
- }
-
- private static class PackageRules {
- private final String mPkg;
- private final List<ZenModeConfig.ZenRule> mRules;
- private int mParceledSize = -1;
-
- PackageRules(String pkg) {
- mPkg = pkg;
- mRules = new ArrayList<>();
- }
-
- private int dataSize() {
- if (mParceledSize >= 0) {
- return mParceledSize;
- }
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeParcelableList(mRules, 0);
- mParceledSize = parcel.dataSize();
- return mParceledSize;
- } finally {
- parcel.recycle();
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8b09c2a..889df51 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -48,7 +48,6 @@
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.notification.Flags.preventZenDeviceEffectsWhileDriving;
-import static com.android.server.notification.Flags.limitZenConfigSize;
import static java.util.Objects.requireNonNull;
@@ -193,7 +192,6 @@
private final ConditionProviders.Config mServiceConfig;
private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
private final ZenModeEventLogger mZenModeEventLogger;
- private final ZenConfigTrimmer mConfigTrimmer;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
@@ -228,7 +226,6 @@
mClock = clock;
addCallback(mMetrics);
mAppOps = context.getSystemService(AppOpsManager.class);
- mConfigTrimmer = new ZenConfigTrimmer(mContext);
mDefaultConfig = Flags.modesUi()
? ZenModeConfig.getDefaultConfig()
@@ -2064,20 +2061,20 @@
Log.w(TAG, "Invalid config in setConfigLocked; " + config);
return false;
}
- if (limitZenConfigSize() && (origin == ORIGIN_APP || origin == ORIGIN_USER_IN_APP)) {
- mConfigTrimmer.trimToMaximumSize(config);
- }
-
if (config.user != mUser) {
// simply store away for background users
- mConfigs.put(config.user, config);
+ synchronized (mConfigLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- mConfigs.put(config.user, config);
+ synchronized (mConfigLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(origin, reason, triggeringComponent, mConfig, config, callingUid);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 346d65a..76cd5c8 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -212,16 +212,6 @@
}
flag {
- name: "limit_zen_config_size"
- namespace: "systemui"
- description: "Enforce a maximum (serialized) size for the Zen configuration"
- bug: "387498139"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "managed_services_concurrent_multiuser"
namespace: "systemui"
description: "Enables ManagedServices to support Concurrent multi user environment"
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 1fda478..92e8eb9 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -133,6 +133,38 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "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"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "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"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"file_patterns": [
"core/java/.*Install.*",
@@ -288,6 +320,38 @@
]
},
{
+ "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+ "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"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+ "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"
+ }
+ ]
+ },
+ {
"name": "CtsPackageInstallerCUJMultiUsersTestCases",
"file_patterns": [
"core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 092ec8e..233b577 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1078,7 +1078,7 @@
mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
mUserDataPreparer = userDataPreparer;
mUserTypes = UserTypeFactory.getUserTypes();
- invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
+ invalidateOwnerNameIfNecessary(getContextResources(), true /* forceUpdate */);
synchronized (mPackagesLock) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
@@ -1184,6 +1184,15 @@
&& android.multiuser.Flags.enablePrivateSpaceFeatures();
}
+ private Resources getSystemResources() {
+ return android.multiuser.Flags.useUnifiedResources()
+ ? getContextResources() : Resources.getSystem();
+ }
+
+ private Resources getContextResources() {
+ return mContext.getResources();
+ }
+
/**
* This method retrieves the {@link UserManagerInternal} only for the purpose of
* PackageManagerService construction.
@@ -1223,7 +1232,7 @@
// Avoid marking pre-created users for removal.
return;
}
- if (ui.lastLoggedInTime == 0 && ui.isGuest() && Resources.getSystem().getBoolean(
+ if (ui.lastLoggedInTime == 0 && ui.isGuest() && getSystemResources().getBoolean(
com.android.internal.R.bool.config_guestUserAutoCreated)) {
// Avoid marking auto-created but not-yet-logged-in guest user for removal. Because a
// new one will be created anyway, and this one doesn't have any personal data in it yet
@@ -1402,7 +1411,7 @@
}
if (isHeadlessSystemUserMode()) {
- final int bootStrategy = mContext.getResources()
+ final int bootStrategy = getContextResources()
.getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
switch (bootStrategy) {
case BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER:
@@ -2983,7 +2992,7 @@
boolean isUserSwitcherEnabled(@UserIdInt int userId) {
boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.USER_SWITCHER_ENABLED,
- Resources.getSystem().getBoolean(com.android.internal
+ getSystemResources().getBoolean(com.android.internal
.R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;
return UserManager.supportsMultipleUsers()
@@ -4672,7 +4681,7 @@
UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
if ("Primary".equals(userData.info.name)) {
userData.info.name =
- mContext.getResources().getString(com.android.internal.R.string.owner_name);
+ getContextResources().getString(com.android.internal.R.string.owner_name);
userIdsToWrite.add(userData.info.id);
}
userVersion = 1;
@@ -5002,7 +5011,7 @@
final Bundle restrictions = new Bundle();
try {
- final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
+ final String[] defaultFirstUserRestrictions = getContextResources().getStringArray(
com.android.internal.R.array.config_defaultFirstUserRestrictions);
for (String userRestriction : defaultFirstUserRestrictions) {
if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
@@ -6178,7 +6187,7 @@
// If the user switch hasn't been explicitly toggled on or off by the user, turn it on.
if (android.provider.Settings.Global.getString(mContext.getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) {
- if (Resources.getSystem().getBoolean(
+ if (getSystemResources().getBoolean(
com.android.internal.R.bool.config_enableUserSwitcherUponUserCreation)) {
android.provider.Settings.Global.putInt(mContext.getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
@@ -7490,7 +7499,6 @@
final long now = System.currentTimeMillis();
final long nowRealtime = SystemClock.elapsedRealtime();
final StringBuilder sb = new StringBuilder();
- final Resources resources = Resources.getSystem();
if (args != null && args.length > 0) {
switch (args[0]) {
@@ -7573,13 +7581,14 @@
pw.println();
int effectiveMaxSupportedUsers = UserManager.getMaxSupportedUsers();
pw.print(" Max users: " + effectiveMaxSupportedUsers);
- int defaultMaxSupportedUsers = resources.getInteger(R.integer.config_multiuserMaximumUsers);
+ int defaultMaxSupportedUsers = getSystemResources()
+ .getInteger(R.integer.config_multiuserMaximumUsers);
if (effectiveMaxSupportedUsers != defaultMaxSupportedUsers) {
pw.print(" (built-in value: " + defaultMaxSupportedUsers + ")");
}
pw.println(" (limit reached: " + isUserLimitReached() + ")");
pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
- pw.println(" All guests ephemeral: " + resources.getBoolean(
+ pw.println(" All guests ephemeral: " + getSystemResources().getBoolean(
com.android.internal.R.bool.config_guestUserEphemeral));
pw.println(" Force ephemeral users: " + mForceEphemeralUsers);
final boolean isHeadlessSystemUserMode = isHeadlessSystemUserMode();
@@ -7594,7 +7603,7 @@
}
}
if (isHeadlessSystemUserMode) {
- pw.println(" Can switch to headless system user: " + resources
+ pw.println(" Can switch to headless system user: " + getSystemResources()
.getBoolean(com.android.internal.R.bool.config_canSwitchToHeadlessSystemUser));
}
pw.println(" User version: " + mUserVersion);
@@ -8536,8 +8545,7 @@
* or downgraded to non-admin status.
*/
public boolean isMainUserPermanentAdmin() {
- return Resources.getSystem()
- .getBoolean(R.bool.config_isMainUserPermanentAdmin);
+ return getSystemResources().getBoolean(R.bool.config_isMainUserPermanentAdmin);
}
/**
@@ -8546,8 +8554,7 @@
* it is not a full user.
*/
public boolean canSwitchToHeadlessSystemUser() {
- return Resources.getSystem()
- .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
+ return getSystemResources().getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
}
/**
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 102dc07..417b5c7 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -16,6 +16,7 @@
package com.android.server.power;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
@@ -36,8 +37,8 @@
import android.os.BatteryStatsInternal;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -67,6 +68,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
@@ -147,7 +149,8 @@
@Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
private final TrustManager mTrustManager;
private final Vibrator mVibrator;
- private final WakeLockLog mWakeLockLog;
+ @NonNull private final WakeLockLog mPartialWakeLockLog;
+ @NonNull private final WakeLockLog mFullWakeLockLog;
private final DisplayManagerInternal mDisplayManagerInternal;
private final NotifierHandler mHandler;
@@ -250,7 +253,9 @@
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
- mWakeLockLog = mInjector.getWakeLockLog(context);
+ mFullWakeLockLog = mInjector.getWakeLockLog(context);
+ mPartialWakeLockLog = mInjector.getWakeLockLog(context);
+
// Initialize interactive state for battery stats.
try {
mBatteryStats.noteInteractive(true);
@@ -324,7 +329,8 @@
// Ignore
}
}
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
+ getWakeLockLog(flags).onWakeLockAcquired(tag,
+ getUidForWakeLockLog(ownerUid, workSource), flags, /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockAcquired(flags);
}
@@ -473,7 +479,8 @@
// Ignore
}
}
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
+ getWakeLockLog(flags).onWakeLockReleased(tag,
+ getUidForWakeLockLog(ownerUid, workSource), /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
}
@@ -960,11 +967,18 @@
* @param pw The stream to print to.
*/
public void dump(PrintWriter pw) {
- if (mWakeLockLog != null) {
- mWakeLockLog.dump(pw);
- }
+ pw.println("Notifier:");
- mWakefulnessSessionObserver.dump(pw);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("Partial Wakelock Log:");
+ mPartialWakeLockLog.dump(ipw);
+
+ ipw.println("");
+ ipw.println("Full Wakelock Log:");
+ mFullWakeLockLog.dump(ipw);
+
+ ipw.println("");
+ mWakefulnessSessionObserver.dump(ipw);
}
private void updatePendingBroadcastLocked() {
@@ -1232,7 +1246,9 @@
// Do Nothing
}
}
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+
+ getWakeLockLog(flags).onWakeLockAcquired(tag, getUidForWakeLockLog(ownerUid, workSource),
+ flags, currentTime);
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1253,7 +1269,8 @@
// Ignore
}
}
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
+ getWakeLockLog(flags).onWakeLockReleased(tag, getUidForWakeLockLog(ownerUid, workSource),
+ currentTime);
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1419,6 +1436,15 @@
}
}
+ private @NonNull WakeLockLog getWakeLockLog(int flags) {
+ return PowerManagerService.isScreenLock(flags) ? mFullWakeLockLog : mPartialWakeLockLog;
+ }
+
+ private int getUidForWakeLockLog(int ownerUid, WorkSource workSource) {
+ int attributionUid = workSource != null ? workSource.getAttributionUid() : -1;
+ return attributionUid != -1 ? attributionUid : ownerUid;
+ }
+
private final class NotifierHandler extends Handler {
public NotifierHandler(Looper looper) {
@@ -1501,7 +1527,7 @@
/**
* Gets the WakeLockLog object
*/
- WakeLockLog getWakeLockLog(Context context);
+ @NonNull WakeLockLog getWakeLockLog(Context context);
/**
* Gets the AppOpsManager system service
@@ -1522,7 +1548,7 @@
}
@Override
- public WakeLockLog getWakeLockLog(Context context) {
+ public @NonNull WakeLockLog getWakeLockLog(Context context) {
return new WakeLockLog(context);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dd454cd..3eac4b5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1723,9 +1723,16 @@
}
}
- @SuppressWarnings("deprecation")
private static boolean isScreenLock(final WakeLock wakeLock) {
- switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ return isScreenLock(wakeLock.mFlags);
+ }
+
+ /**
+ * Returns if a wakelock flag corresponds to a screen wake lock.
+ */
+ @SuppressWarnings("deprecation")
+ public static boolean isScreenLock(int flags) {
+ switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.FULL_WAKE_LOCK:
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
case PowerManager.SCREEN_DIM_WAKE_LOCK:
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index eda222e..7f152d6 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -81,11 +81,12 @@
private static final int TYPE_ACQUIRE = 0x1;
private static final int TYPE_RELEASE = 0x2;
private static final int MAX_LOG_ENTRY_BYTE_SIZE = 9;
- private static final int LOG_SIZE = 1024 * 10;
+ private static final int LOG_SIZE = 1024 * 3;
private static final int LOG_SIZE_MIN = MAX_LOG_ENTRY_BYTE_SIZE + 1;
- private static final int TAG_DATABASE_SIZE = 128;
+ private static final int TAG_DATABASE_SIZE = 64;
private static final int TAG_DATABASE_SIZE_MAX = 128;
+ private static final int TAG_DATABASE_STARTING_SIZE = 16;
private static final int LEVEL_SCREEN_TIMEOUT_OVERRIDE_WAKE_LOCK = 0;
private static final int LEVEL_PARTIAL_WAKE_LOCK = 1;
@@ -182,7 +183,7 @@
* @param pw The {@code PrintWriter} to write to.
*/
public void dump(PrintWriter pw) {
- dump(pw, false);
+ dump(pw, /* includeTagDb= */ true);
}
@VisibleForTesting
@@ -1161,15 +1162,16 @@
*/
static class TagDatabase {
private final int mInvalidIndex;
- private final TagData[] mArray;
+ private final int mMaxArraySize;
+ private TagData[] mArray;
private Callback mCallback;
TagDatabase(Injector injector) {
- int size = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX);
-
- // Largest possible index used as "INVALID", hence the (size - 1) sizing.
- mArray = new TagData[size - 1];
- mInvalidIndex = size - 1;
+ // Largest possible index used as "INVALID", hence the (size - 1) sizing
+ mMaxArraySize = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX - 1);
+ int startingSize = Math.min(mMaxArraySize, injector.getTagDatabaseStartingSize());
+ mArray = new TagData[startingSize];
+ mInvalidIndex = mMaxArraySize;
}
@Override
@@ -1195,8 +1197,10 @@
sb.append(", entries: ").append(entries);
sb.append(", Bytes used: ").append(byteEstimate);
if (DEBUG) {
- sb.append(", Avg tag size: ").append(tagSize / tags);
- sb.append("\n ").append(Arrays.toString(mArray));
+ sb.append(", Avg tag size: ").append(tags == 0 ? 0 : (tagSize / tags));
+ for (int i = 0; i < mArray.length; i++) {
+ sb.append("\n [").append(i).append("] ").append(mArray[i]);
+ }
}
return sb.toString();
}
@@ -1284,6 +1288,18 @@
return null;
}
+ // We don't have a spot available, see if we can still increase the array size
+ if (firstAvailable == -1) {
+ if (mArray.length < mMaxArraySize) {
+ int oldSize = mArray.length;
+ int newSize = Math.min(oldSize * 2, mMaxArraySize);
+ TagData[] newArray = new TagData[newSize];
+ System.arraycopy(mArray, 0, newArray, 0, oldSize);
+ mArray = newArray;
+ firstAvailable = oldSize;
+ }
+ }
+
// If we need to remove an index, report to listeners that we are removing an index.
boolean useOldest = firstAvailable == -1;
if (useOldest && mCallback != null) {
@@ -1402,6 +1418,10 @@
return TAG_DATABASE_SIZE;
}
+ public int getTagDatabaseStartingSize() {
+ return TAG_DATABASE_STARTING_SIZE;
+ }
+
public int getLogSize() {
return LOG_SIZE;
}
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index ebc50fd..52d4555 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -67,6 +67,10 @@
new FlagState(Flags.FLAG_WAKELOCK_ATTRIBUTION_VIA_WORKCHAIN,
Flags::wakelockAttributionViaWorkchain);
+ private final FlagState mDisableFrozenProcessWakelocks =
+ new FlagState(Flags.FLAG_DISABLE_FROZEN_PROCESS_WAKELOCKS,
+ Flags::disableFrozenProcessWakelocks);
+
/** Returns whether early-screen-timeout-detector is enabled on not. */
public boolean isEarlyScreenTimeoutDetectorEnabled() {
return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -121,6 +125,13 @@
}
/**
+ * @return Whether the feature to disable the frozen process wakelocks is enabled
+ */
+ public boolean isDisableFrozenProcessWakelocksEnabled() {
+ return mDisableFrozenProcessWakelocks.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -132,6 +143,7 @@
pw.println(" " + mFrameworkWakelockInfo);
pw.println(" " + mMoveWscLoggingToNotifier);
pw.println(" " + mWakelockAttributionViaWorkchain);
+ pw.println(" " + mDisableFrozenProcessWakelocks);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index fefe195..ad8ec03 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -70,3 +70,10 @@
description: "Feature flag to move logging of WakelockStateChanged atoms from BatteryStatsImpl to Notifier."
bug: "352602149"
}
+
+flag {
+ name: "disable_frozen_process_wakelocks"
+ namespace: "power"
+ description: "Feature flag to disable/enable wakelocks of a process when it is frozen/unfrozen"
+ bug: "291115867"
+}
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 93fd276..6872ca9 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -23,7 +23,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.SharedPreferences;
import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -32,14 +34,21 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.UserHandle;
import android.provider.Settings;
import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.security.advancedprotection.AdvancedProtectionManager;
+import android.security.advancedprotection.AdvancedProtectionManager.FeatureId;
+import android.security.advancedprotection.AdvancedProtectionManager.SupportDialogType;
import android.security.advancedprotection.IAdvancedProtectionCallback;
import android.security.advancedprotection.IAdvancedProtectionService;
+import android.security.advancedprotection.AdvancedProtectionProtoEnums;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -51,7 +60,9 @@
import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook;
import com.android.server.security.advancedprotection.features.UsbDataAdvancedProtectionHook;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -61,6 +72,15 @@
private static final int MODE_CHANGED = 0;
private static final int CALLBACK_ADDED = 1;
+ // Shared preferences keys
+ private static final String PREFERENCE = "advanced_protection_preference";
+ private static final String ENABLED_CHANGE_TIME = "enabled_change_time";
+ private static final String LAST_DIALOG_FEATURE_ID = "last_dialog_feature_id";
+ private static final String LAST_DIALOG_TYPE = "last_dialog_type";
+ private static final String LAST_DIALOG_HOURS_SINCE_ENABLED = "last_dialog_hours_since_enabled";
+ private static final String LAST_DIALOG_LEARN_MORE_CLICKED = "last_dialog_learn_more_clicked";
+ private static final long MILLIS_PER_HOUR = 60 * 60 * 1000;
+
private final Context mContext;
private final Handler mHandler;
private final AdvancedProtectionStore mStore;
@@ -72,6 +92,10 @@
// For tracking only - not called on state change
private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>();
+ // Used to store logging data
+ private SharedPreferences mSharedPreferences;
+ private boolean mEmitLogs = true;
+
private AdvancedProtectionService(@NonNull Context context) {
super(PermissionEnforcer.fromContext(context));
mContext = context;
@@ -126,6 +150,8 @@
if (provider != null) {
mProviders.add(provider);
}
+
+ mEmitLogs = false;
}
@Override
@@ -178,7 +204,7 @@
if (enabled != isAdvancedProtectionEnabledInternal()) {
mStore.store(enabled);
sendModeChanged(enabled);
- Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled"));
+ logAdvancedProtectionEnabled(enabled);
}
}
} finally {
@@ -188,6 +214,91 @@
@Override
@EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+ public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+ boolean learnMoreClicked) {
+ logDialogShown_enforcePermission();
+
+ if (!mEmitLogs) {
+ return;
+ }
+
+ int hoursSinceEnabled = hoursSinceLastChange();
+ FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_SUPPORT_DIALOG_DISPLAYED,
+ /*feature_id*/ featureIdToLogEnum(featureId),
+ /*dialogue_type*/ dialogueTypeToLogEnum(type),
+ /*learn_more_clicked*/ learnMoreClicked,
+ /*hours_since_last_change*/ hoursSinceEnabled);
+
+ getSharedPreferences().edit()
+ .putInt(LAST_DIALOG_FEATURE_ID, featureId)
+ .putInt(LAST_DIALOG_TYPE, type)
+ .putBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, learnMoreClicked)
+ .putInt(LAST_DIALOG_HOURS_SINCE_ENABLED, hoursSinceEnabled)
+ .apply();
+ }
+
+ private int featureIdToLogEnum(@FeatureId int featureId) {
+ switch (featureId) {
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_CELLULAR_2G;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_USB:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_USB;
+ case AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_WEP;
+ case AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_ENABLE_MTE;
+ default:
+ return AdvancedProtectionProtoEnums.FEATURE_ID_UNKNOWN;
+ }
+ }
+
+ private int dialogueTypeToLogEnum(@SupportDialogType int type) {
+ switch (type) {
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_BLOCKED_INTERACTION;
+ case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_DISABLED_SETTING;
+ default:
+ return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+ }
+ }
+
+ private void logAdvancedProtectionEnabled(boolean enabled) {
+ if (!mEmitLogs) {
+ return;
+ }
+
+ Slog.i(TAG, "Advanced protection has been " + (enabled ? "enabled" : "disabled"));
+ SharedPreferences prefs = getSharedPreferences();
+ FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_STATE_CHANGED,
+ /*enabled*/ enabled,
+ /*hours_since_enabled*/ hoursSinceLastChange(),
+ /*last_dialog_feature_id*/ featureIdToLogEnum(
+ prefs.getInt(LAST_DIALOG_FEATURE_ID, -1)),
+ /*_type*/ dialogueTypeToLogEnum(prefs.getInt(LAST_DIALOG_TYPE, -1)),
+ /*_learn_more_clicked*/ prefs.getBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, false),
+ /*_hours_since_enabled*/ prefs.getInt(LAST_DIALOG_HOURS_SINCE_ENABLED, -1));
+ prefs.edit()
+ .putLong(ENABLED_CHANGE_TIME, System.currentTimeMillis())
+ .apply();
+ }
+
+ private int hoursSinceLastChange() {
+ int hoursSinceEnabled = -1;
+ long lastChangeTimeMillis = getSharedPreferences().getLong(ENABLED_CHANGE_TIME, -1);
+ if (lastChangeTimeMillis != -1) {
+ hoursSinceEnabled = (int)
+ ((System.currentTimeMillis() - lastChangeTimeMillis) / MILLIS_PER_HOUR);
+ }
+ return hoursSinceEnabled;
+ }
+
+ @Override
+ @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
getAdvancedProtectionFeatures_enforcePermission();
List<AdvancedProtectionFeature> features = new ArrayList<>();
@@ -213,6 +324,30 @@
.exec(this, in, out, err, args, callback, resultReceiver);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+ writer.println("AdvancedProtectionService");
+ writer.println(" isAdvancedProtectionEnabled: " + isAdvancedProtectionEnabledInternal());
+ writer.println(" mHooks.size(): " + mHooks.size());
+ writer.println(" mCallbacks.size(): " + mCallbacks.size());
+ writer.println(" mProviders.size(): " + mProviders.size());
+
+ writer.println("Hooks: ");
+ mHooks.stream().forEach(hook -> {
+ writer.println(" " + hook.getClass().getSimpleName() +
+ " available: " + hook.isAvailable());
+ });
+ writer.println(" Providers: ");
+ mProviders.stream().forEach(provider -> {
+ writer.println(" " + provider.getClass().getSimpleName());
+ provider.getFeatures().stream().forEach(feature -> {
+ writer.println(" " + feature.getClass().getSimpleName());
+ });
+ });
+ writer.println(" mSharedPreferences: " + getSharedPreferences().getAll());
+ }
+
void sendModeChanged(boolean enabled) {
Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1)
.sendToTarget();
@@ -224,6 +359,22 @@
.sendToTarget();
}
+ private SharedPreferences getSharedPreferences() {
+ if (mSharedPreferences == null) {
+ initSharedPreferences();
+ }
+ return mSharedPreferences;
+ }
+
+ private synchronized void initSharedPreferences() {
+ if (mSharedPreferences == null) {
+ Context deviceContext = mContext.createDeviceProtectedStorageContext();
+ File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREFERENCE);
+ mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs,
+ Context.MODE_PRIVATE);
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private final AdvancedProtectionService mService;
diff --git a/services/core/java/com/android/server/sensors/OWNERS b/services/core/java/com/android/server/sensors/OWNERS
new file mode 100644
index 0000000..6b22473
--- /dev/null
+++ b/services/core/java/com/android/server/sensors/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/native:/services/sensorservice/OWNERS
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index 7ff4ade..9636cc6 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -17,6 +17,7 @@
package com.android.server.sensors;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.SensorDirectChannel;
import android.os.ParcelFileDescriptor;
@@ -71,7 +72,7 @@
/**
* Sends an event for the runtime sensor with the given handle to the framework.
*
- * Only relevant for sending runtime sensor events. @see #createRuntimeSensor.
+ * <p>Only relevant for sending runtime sensor events. @see #createRuntimeSensor.</p>
*
* @param handle The sensor handle.
* @param type The type of the sensor.
@@ -83,6 +84,21 @@
@NonNull float[] values);
/**
+ * Sends an additional info event for the runtime sensor with the given handle to the framework.
+ *
+ * <p>Only relevant for runtime sensors. @see #createRuntimeSensor.</p>
+ *
+ * @param handle The sensor handle.
+ * @param type The type of payload data.
+ * @param serial The sequence number of this frame for this type.
+ * @param timestampNanos Timestamp of the event.
+ * @param values The payload data represented in float values.
+ * @return Whether the event injection was successful.
+ */
+ public abstract boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+ long timestampNanos, @Nullable float[] values);
+
+ /**
* Listener for proximity sensor state changes.
*/
public interface ProximityActiveListener {
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index 3de1910..0d31b22 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -19,6 +19,7 @@
import static com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.ArrayMap;
@@ -62,6 +63,9 @@
private static native void unregisterRuntimeSensorNative(long ptr, int handle);
private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
long timestampNanos, float[] values);
+ private static native boolean sendRuntimeSensorAdditionalInfoNative(long ptr, int handle,
+ int type, int serial, long timestampNanos, float[] values);
+
public SensorService(Context ctx) {
super(ctx);
@@ -129,6 +133,18 @@
}
@Override
+ public boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+ long timestampNanos, @Nullable float[] values) {
+ synchronized (mLock) {
+ if (!mRuntimeSensorHandles.contains(handle)) {
+ return false;
+ }
+ return sendRuntimeSensorAdditionalInfoNative(mPtr, handle, type, serial,
+ timestampNanos, values);
+ }
+ }
+
+ @Override
public void addProximityActiveListener(@NonNull Executor executor,
@NonNull ProximityActiveListener listener) {
Objects.requireNonNull(executor, "executor must not be null");
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 6e640d8..424439d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -856,10 +856,14 @@
BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath(), options);
wallpaperImageSize.set(options.outWidth, options.outHeight);
}
+ boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+ == View.LAYOUT_DIRECTION_RTL;
+ Rect croppedImageBound = getCrop(displaySize, mDefaultDisplayInfo, wallpaperImageSize,
+ getRelativeCropHints(wallpaperData), isRtl);
- double maxDisplayToImageRatio = Math.max((double) displaySize.x / wallpaperImageSize.x,
- (double) displaySize.y / wallpaperImageSize.y);
- if (maxDisplayToImageRatio > 1.5) {
+ double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(),
+ (double) displaySize.y / croppedImageBound.height());
+ if (maxDisplayToImageRatio > 1.3) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index ce3ad88..d935432 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -73,9 +73,12 @@
final Rect stableBounds = new Rect();
task.getDisplayArea().getStableRect(stableBounds);
- // If the options bounds size is flexible, update size with calculated desired size.
+ final boolean hasFullscreenOverride = activity != null
+ && activity.mAppCompatController.getAspectRatioOverrides().hasFullscreenOverride();
+ // If the options bounds size is flexible and no fullscreen override has been applied,
+ // update size with calculated desired size.
final boolean updateOptionBoundsSize = options != null
- && options.getFlexibleLaunchSize();
+ && options.getFlexibleLaunchSize() && !hasFullscreenOverride;
// If cascading is also enabled, the position of the options bounds must be respected
// during the size update.
final boolean shouldRespectOptionPosition =
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 76a39d9..59a0429 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -41,6 +41,7 @@
import static android.util.TypedValue.COMPLEX_UNIT_MASK;
import static android.util.TypedValue.COMPLEX_UNIT_SHIFT;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -2161,7 +2162,7 @@
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
void finishAsyncRotationIfPossible() {
final AsyncRotationController controller = mAsyncRotationController;
- if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
+ if (controller != null) {
controller.completeAll();
mAsyncRotationController = null;
}
@@ -2238,11 +2239,7 @@
*/
private void applyRotation(final int oldRotation, final int rotation) {
mDisplayRotation.applyCurrentRotation(rotation);
- final boolean shellTransitions = mTransitionController.getTransitionPlayer() != null;
- final boolean rotateSeamlessly =
- mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
- final Transaction transaction =
- shellTransitions ? getSyncTransaction() : getPendingTransaction();
+
// We need to update our screen size information to match the new rotation. If the rotation
// has actually changed then this method will return true and, according to the comment at
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -2250,25 +2247,13 @@
// #computeScreenConfiguration() later.
updateDisplayAndOrientation(null /* outConfig */);
- if (!shellTransitions) {
- forAllWindows(w -> {
- w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
- }, true /* traverseTopToBottom */);
- mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
- if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
- // Make sure DisplayRotation#isRotatingSeamlessly() will return false.
- mDisplayRotation.cancelSeamlessRotation();
- }
- }
+ // Before setDisplayProjection is applied by the start transaction of transition,
+ // set the transform hint to avoid using surface in old rotation.
+ setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
+ // The sync transaction should already contains setDisplayProjection, so unset the
+ // hint to restore the natural state when the transaction is applied.
+ getSyncTransaction().unsetFixedTransformHint(mSurfaceControl);
- if (shellTransitions) {
- // Before setDisplayProjection is applied by the start transaction of transition,
- // set the transform hint to avoid using surface in old rotation.
- setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
- // The sync transaction should already contains setDisplayProjection, so unset the
- // hint to restore the natural state when the transaction is applied.
- transaction.unsetFixedTransformHint(mSurfaceControl);
- }
scheduleAnimation();
mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
@@ -2870,8 +2855,7 @@
// If the transition finished callback cannot match the token for some reason, make sure the
// rotated state is cleared if it is already invisible.
if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested()
- && !mFixedRotationLaunchingApp.isVisible()
- && !mDisplayRotation.isRotatingSeamlessly()) {
+ && !mFixedRotationLaunchingApp.isVisible()) {
clearFixedRotationLaunchingApp();
}
// If there won't be a transition to notify the launch is done, then it should be ready to
@@ -3283,26 +3267,30 @@
/* inTopology= */ shouldShowContent);
}
- /**
- * Whether the display is allowed to switch the content mode between extended and mirroring.
- * If the content mode is extended, the display will start home activity and show system
- * decorations, such as wallpapaer, status bar and navigation bar.
- * If the content mode is mirroring, the display will not show home activity or system
- * decorations.
- * The content mode is switched when {@link Display#canHostTasks()} changes.
- *
- * Note that we only allow displays that are able to show system decorations to use the content
- * mode switch; however, not all displays that are able to show system decorations are allowed
- * to use the content mode switch.
- */
- boolean allowContentModeSwitch() {
+ /**
+ * Whether the display is allowed to switch the content mode between extended and mirroring.
+ * If the content mode is extended, the display will start home activity and show system
+ * decorations, such as wallpapaer, status bar and navigation bar.
+ * If the content mode is mirroring, the display will not show home activity or system
+ * decorations.
+ * The content mode is switched when {@link Display#canHostTasks()} changes.
+ *
+ * Note that we only allow displays that are able to show system decorations to use the content
+ * mode switch; however, not all displays that are able to show system decorations are allowed
+ * to use the content mode switch.
+ */
+ boolean allowContentModeSwitch() {
+ if ((mDisplay.getFlags() & FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0) {
+ return false;
+ }
+
// The default display should always show system decorations.
if (isDefaultDisplay) {
return false;
}
- // Private display should never show system decorations.
- if (isPrivate()) {
+ // Private or untrusted display should never show system decorations.
+ if (isPrivate() || !isTrusted()) {
return false;
}
@@ -3310,14 +3298,9 @@
return false;
}
- // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
- if ((mDisplay.getFlags() & Display.FLAG_REAR) != 0) {
- return false;
- }
-
- // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
- // Virtual displays cannot add or remove system decorations during their lifecycle.
- if (mDisplay.getType() == Display.TYPE_VIRTUAL) {
+ // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should always show system
+ // decorations, and should not switch the content mode.
+ if ((mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
return false;
}
@@ -5072,7 +5055,7 @@
}
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
- if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
+ if (!inTransition()) {
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 9cf792d..9edbb70 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -168,19 +168,6 @@
private int mDeferredRotationPauseCount;
/**
- * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
- * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
- * so we need to track when this hits zero so we can apply deferred orientation updates.
- */
- private int mSeamlessRotationCount;
-
- /**
- * True in the interval from starting seamless rotation until the last rotated window draws in
- * the new orientation.
- */
- private boolean mRotatingSeamlessly;
-
- /**
* Behavior of rotation suggestions.
*
* @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
@@ -630,15 +617,6 @@
return true;
}
- if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
- // The screen rotation animation uses a screenshot to freeze the screen while windows
- // resize underneath. When we are rotating seamlessly, we allow the elements to
- // transition to their rotated state independently and without a freeze required.
- prepareSeamlessRotation();
- } else {
- cancelSeamlessRotation();
- }
-
// Give a remote handler (system ui) some time to reposition things.
startRemoteRotation(oldRotation, mRotation);
@@ -677,42 +655,6 @@
}
}
- /**
- * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
- * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
- * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
- * and it doesn't choose seamless rotation.
- */
- void cancelSeamlessRotation() {
- if (!mRotatingSeamlessly) {
- return;
- }
- mDisplayContent.forAllWindows(w -> {
- if (w.mSeamlesslyRotated) {
- w.cancelSeamlessRotation();
- w.mSeamlesslyRotated = false;
- }
- }, true /* traverseTopToBottom */);
- mSeamlessRotationCount = 0;
- mRotatingSeamlessly = false;
- mDisplayContent.finishAsyncRotationIfPossible();
- }
-
- private void prepareSeamlessRotation() {
- // We are careful to reset this in case a window was removed before it finished
- // seamless rotation.
- mSeamlessRotationCount = 0;
- mRotatingSeamlessly = true;
- }
-
- boolean isRotatingSeamlessly() {
- return mRotatingSeamlessly;
- }
-
- boolean hasSeamlessRotatingWindow() {
- return mSeamlessRotationCount > 0;
- }
-
@VisibleForTesting
boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
// Display doesn't need to be frozen because application has been started in correct
@@ -750,13 +692,6 @@
return false;
}
- // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
- // complete (that is, waiting for windows to redraw). It's tempting to check
- // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
- if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
- return false;
- }
-
return true;
}
@@ -774,28 +709,6 @@
return oldRotation != Surface.ROTATION_180 && newRotation != Surface.ROTATION_180;
}
- void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
- if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
- return;
- }
-
- w.mSeamlesslyRotated = seamlesslyRotated;
- if (seamlesslyRotated) {
- mSeamlessRotationCount++;
- } else {
- mSeamlessRotationCount--;
- }
- if (mSeamlessRotationCount == 0) {
- ProtoLog.i(WM_DEBUG_ORIENTATION,
- "Performing post-rotate rotation after seamless rotation");
- // Finish seamless rotation.
- mRotatingSeamlessly = false;
- mDisplayContent.finishAsyncRotationIfPossible();
-
- updateRotationAndSendNewConfigIfChanged();
- }
- }
-
void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
mFixedToUserRotation = fixedToUserRotation;
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 040bbe4..234fbd2 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -82,6 +82,13 @@
*/
private boolean mGivenInsetsReady = false;
+ /**
+ * The last state of the windowContainer. This is used to reset server visibility, in case of
+ * the IME (temporarily) redrawing (e.g. during a rotation), to dispatch the control with
+ * leash again after it has finished drawing.
+ */
+ private boolean mLastDrawn = false;
+
ImeInsetsSourceProvider(@NonNull InsetsSource source,
@NonNull InsetsStateController stateController,
@NonNull DisplayContent displayContent) {
@@ -97,6 +104,7 @@
final WindowState ws =
mWindowContainer != null ? mWindowContainer.asWindowState() : null;
final boolean givenInsetsPending = ws != null && ws.mGivenInsetsPending;
+ mLastDrawn = ws != null && ws.isDrawn();
// isLeashReadyForDispatching (used to dispatch the leash of the control) is
// depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here
@@ -158,6 +166,35 @@
}
}
+ /**
+ * This is used to determine the desired serverVisibility state. For the IME, just having a
+ * window state that would be visible by policy is not enough.
+ */
+ @Override
+ protected boolean isSurfaceVisible() {
+ final boolean isSurfaceVisible = super.isSurfaceVisible();
+ if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ final WindowState windowState = mWindowContainer.asWindowState();
+ if (mControl != null && windowState != null) {
+ final boolean isDrawn = windowState.isDrawn();
+ if (!isServerVisible() && isSurfaceVisible) {
+ // In case the IME becomes visible, we need to check if it is already drawn and
+ // does not have given insets pending. If it's not yet drawn, we do not set
+ // server visibility
+ return isDrawn && !windowState.mGivenInsetsPending;
+ } else if (mLastDrawn && !isDrawn) {
+ // If the IME was drawn before, but is not drawn anymore, we need to reset
+ // server visibility, which will also reset {@link
+ // ImeInsetsSourceProvider#mGivenInsetsReady}. Otherwise, the new control
+ // with leash won't be dispatched after the surface has redrawn.
+ return false;
+ }
+ }
+ }
+ return isSurfaceVisible;
+ }
+
+
@Nullable
@Override
InsetsSourceControl getControl(InsetsControlTarget target) {
@@ -779,6 +816,8 @@
pw.print(prefix);
pw.print("mImeShowing=");
pw.print(mImeShowing);
+ pw.print(" mLastDrawn=");
+ pw.print(mLastDrawn);
if (mImeRequester != null) {
pw.print(prefix);
pw.print("showImePostLayout pending for mImeRequester=");
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1b693fc..dcbb781 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -176,6 +176,16 @@
}
/**
+ * @return Whether the current window container has a visible surface.
+ */
+ protected boolean isSurfaceVisible() {
+ final WindowState windowState = mWindowContainer.asWindowState();
+ return windowState != null
+ ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
+ : mWindowContainer.isVisibleRequested();
+ }
+
+ /**
* Updates the window container that currently backs this source.
*
* @param windowContainer The window container that links to this source.
@@ -368,20 +378,9 @@
if (mWindowContainer == null) {
return;
}
- WindowState windowState = mWindowContainer.asWindowState();
- boolean isServerVisible = windowState != null
- ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
- : mWindowContainer.isVisibleRequested();
+ final WindowState windowState = mWindowContainer.asWindowState();
+ final boolean isServerVisible = isSurfaceVisible();
- if (android.view.inputmethod.Flags.refactorInsetsController()) {
- if (mControl != null && mControl.getType() == WindowInsets.Type.ime() && !mServerVisible
- && isServerVisible && windowState != null) {
- // in case the IME becomes visible, we need to check if it is already drawn and
- // does not have given insets pending. If it's not yet drawn, we do not set
- // server visibility
- isServerVisible = windowState.isDrawn() && !windowState.mGivenInsetsPending;
- }
- }
final boolean serverVisibleChanged = mServerVisible != isServerVisible;
setServerVisible(isServerVisible);
if (mControl != null && mControlTarget != null) {
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 6dd7d35..6e59828 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -21,20 +21,13 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.app.PictureInPictureParams;
import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
-import android.util.RotationUtils;
import android.util.Slog;
import android.view.IPinnedTaskListener;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.window.PictureInPictureSurfaceTransaction;
import java.io.PrintWriter;
@@ -71,11 +64,7 @@
* based on the new rotation.
*/
private Rect mDestRotatedBounds;
- /**
- * Non-null if the entering PiP task from recents animation will cause display rotation to
- * change. The transaction is based on the old rotation.
- */
- private PictureInPictureSurfaceTransaction mPipTransaction;
+
/** Whether to skip task configuration change once. */
private boolean mFreezingTaskConfig;
/** Defer display orientation change if the PiP task is animating across orientations. */
@@ -212,14 +201,12 @@
}
/**
- * Sets the transaction for {@link #startSeamlessRotationIfNeeded} if the orientation of display
- * will be changed. This is only called when finishing recents animation with pending
- * orientation change that will be handled by
- * {@link DisplayContent.FixedRotationTransitionListener#onFinishRecentsAnimation}.
+ * Sets a hint if the orientation of display will be changed. This is only called when
+ * finishing recents animation with pending orientation change that will be handled by
+ * {@link DisplayContent.FixedRotationTransitionListener}.
*/
- void setEnterPipTransaction(PictureInPictureSurfaceTransaction tx) {
+ void setEnterPipWithRotatedTransientLaunch() {
mFreezingTaskConfig = true;
- mPipTransaction = tx;
}
/** Called when the activity in PiP task has PiP windowing mode (at the end of animation). */
@@ -233,81 +220,6 @@
}
/**
- * Resets rotation and applies scale and position to PiP task surface to match the current
- * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
- * receives the callback of fixed rotation completion.
- */
- void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t,
- int oldRotation, int newRotation) {
- final Rect bounds = mDestRotatedBounds;
- final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
- final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
- if (bounds == null && emptyPipPositionTx) {
- return;
- }
- final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
- final Task pinnedTask = taskArea.getRootPinnedTask();
- if (pinnedTask == null) {
- return;
- }
-
- mDestRotatedBounds = null;
- mPipTransaction = null;
- final Rect areaBounds = taskArea.getBounds();
- if (!emptyPipPositionTx) {
- // The transaction from recents animation is in old rotation. So the position needs to
- // be rotated.
- float dx = pipTx.mPosition.x;
- float dy = pipTx.mPosition.y;
- final Matrix matrix = pipTx.getMatrix();
- if (pipTx.mRotation == 90) {
- dx = pipTx.mPosition.y;
- dy = areaBounds.right - pipTx.mPosition.x;
- matrix.postRotate(-90);
- } else if (pipTx.mRotation == -90) {
- dx = areaBounds.bottom - pipTx.mPosition.y;
- dy = pipTx.mPosition.x;
- matrix.postRotate(90);
- }
- matrix.postTranslate(dx, dy);
- final SurfaceControl leash = pinnedTask.getSurfaceControl();
- t.setMatrix(leash, matrix, new float[9]);
- if (pipTx.hasCornerRadiusSet()) {
- t.setCornerRadius(leash, pipTx.mCornerRadius);
- }
- Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
- return;
- }
-
- final PictureInPictureParams params = pinnedTask.getPictureInPictureParams();
- final Rect sourceHintRect = params != null && params.hasSourceBoundsHint()
- ? params.getSourceRectHint()
- : null;
- Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
- final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation);
- // Adjust for display cutout if applicable.
- if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) {
- if (pinnedTask.getDisplayCutoutInsets() != null) {
- final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation);
- final Rect displayCutoutInsets = RotationUtils.rotateInsets(
- Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect();
- sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top);
- }
- }
- final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
- ? sourceHintRect : areaBounds;
- final int w = contentBounds.width();
- final int h = contentBounds.height();
- final float scale = w <= h ? (float) bounds.width() / w : (float) bounds.height() / h;
- final int insetLeft = (int) ((contentBounds.left - areaBounds.left) * scale + .5f);
- final int insetTop = (int) ((contentBounds.top - areaBounds.top) * scale + .5f);
- final Matrix matrix = new Matrix();
- matrix.setScale(scale, scale);
- matrix.postTranslate(bounds.left - insetLeft, bounds.top - insetTop);
- t.setMatrix(pinnedTask.getSurfaceControl(), matrix, new float[9]);
- }
-
- /**
* Returns {@code true} to skip {@link Task#onConfigurationChanged} because it is expected that
* there will be a orientation change and a PiP configuration change.
*/
@@ -321,7 +233,6 @@
mFreezingTaskConfig = false;
mDeferOrientationChanging = false;
mDestRotatedBounds = null;
- mPipTransaction = null;
}
/**
@@ -381,9 +292,6 @@
if (mDestRotatedBounds != null) {
pw.println(prefix + " mPendingBounds=" + mDestRotatedBounds);
}
- if (mPipTransaction != null) {
- pw.println(prefix + " mPipTransaction=" + mPipTransaction);
- }
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
pw.println(prefix + " mImeHeight=" + mImeHeight);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 609302c..39d1062 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -79,8 +79,6 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
import static java.lang.Integer.MAX_VALUE;
@@ -655,9 +653,6 @@
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
- if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
- }
if (pendingChanges != 0) {
hasChanges = true;
}
@@ -858,7 +853,8 @@
// Post these on a handler such that we don't call into power manager service while
// holding the window manager lock to avoid lock contention with power manager lock.
- mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides)
+ // Send a copy of the brightness overrides as they may be cleared before being sent out.
+ mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides.clone())
.sendToTarget();
mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
@@ -1024,18 +1020,6 @@
return changed;
}
- boolean copyAnimToLayoutParams() {
- boolean doRequest = false;
-
- final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
- if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
- mUpdateRotation = true;
- doRequest = true;
- }
-
- return doRequest;
- }
-
private final class MyHandler extends Handler {
public MyHandler(Looper looper) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 803c21c..30313fc 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1249,7 +1249,7 @@
// Skip dispatching the change for PiP task to avoid its activity drawing for the
// intermediate state which will cause flickering. The final PiP bounds in new
// rotation will be applied by PipTransition.
- ar.mDisplayContent.mPinnedTaskController.setEnterPipTransaction(null);
+ ar.mDisplayContent.mPinnedTaskController.setEnterPipWithRotatedTransientLaunch();
}
return inPip;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 80137a2..3f2b40c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -65,9 +65,6 @@
/** Time of current animation step. Reset on each iteration */
long mCurrentTime;
- int mBulkUpdateParams = 0;
- Object mLastWindowFreezeSource;
-
private boolean mInitialized = false;
private Choreographer mChoreographer;
@@ -145,7 +142,6 @@
final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
boolean rootAnimating = false;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = 0;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
@@ -202,8 +198,7 @@
}
final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
- final boolean doRequest = mBulkUpdateParams != 0 && root.copyAnimToLayoutParams();
- if (hasPendingLayoutChanges || doRequest) {
+ if (hasPendingLayoutChanges) {
mService.mWindowPlacerLocked.requestTraversal();
}
@@ -245,7 +240,6 @@
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit"
- + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
@@ -265,17 +259,6 @@
mRunningExpensiveAnimations = runningExpensiveAnimations;
}
- private static String bulkUpdateParamsToString(int bulkUpdateParams) {
- StringBuilder builder = new StringBuilder(128);
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
- builder.append(" UPDATE_ROTATION");
- }
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
- builder.append(" SET_WALLPAPER_ACTION_PENDING");
- }
- return builder.toString();
- }
-
public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
final String subPrefix = " " + prefix;
@@ -292,11 +275,6 @@
pw.print(prefix); pw.print("mCurrentTime=");
pw.println(TimeUtils.formatUptime(mCurrentTime));
}
- if (mBulkUpdateParams != 0) {
- pw.print(prefix); pw.print("mBulkUpdateParams=0x");
- pw.print(Integer.toHexString(mBulkUpdateParams));
- pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
- }
}
void scheduleAnimation() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c078d67b..d7626f0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -198,6 +198,8 @@
import android.graphics.Region;
import android.hardware.configstore.V1_0.OptionalBool;
import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputSettings;
@@ -1032,6 +1034,21 @@
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
+ private DeviceStateManager mDeviceStateManager;
+ private DeviceStateCallback mDeviceStateCallback;
+ private class DeviceStateCallback implements DeviceStateManager.DeviceStateCallback {
+ private DeviceState mCurrentDeviceState;
+ @Override
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
+ mCurrentDeviceState = state;
+ }
+
+ boolean isInRearDisplayOuterDefaultState() {
+ return mCurrentDeviceState != null && mCurrentDeviceState
+ .hasProperties(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
+ }
+ }
+
private float mWindowAnimationScaleSetting = 1.0f;
private float mTransitionAnimationScaleSetting = 1.0f;
private float mAnimatorDurationScaleSetting = 1.0f;
@@ -1317,6 +1334,10 @@
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+ mDeviceStateCallback = new DeviceStateCallback();
+ mDeviceStateManager.registerCallback(new HandlerExecutor(mH), mDeviceStateCallback);
+
if (mPowerManagerInternal != null) {
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@@ -2132,7 +2153,6 @@
}
final DisplayContent dc = win.getDisplayContent();
- dc.getDisplayRotation().markForSeamlessRotation(win, false /* seamlesslyRotated */);
win.resetAppOpsState();
@@ -7594,6 +7614,26 @@
}
@Override
+ public boolean isEligibleForDesktopMode(int displayId) {
+ if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "isEligibleForDesktopMode()")) {
+ throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+ }
+
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ ProtoLog.e(WM_ERROR, "Attempted to check isEligibleForDesktopMode() "
+ + "for a display that does not exist: %d", displayId);
+ return false;
+ }
+ if (!displayContent.isSystemDecorationsSupported()) {
+ return false;
+ }
+ return displayContent.isDefaultDisplay || displayContent.allowContentModeSwitch();
+ }
+ }
+
+ @Override
public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) {
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
@@ -8950,6 +8990,17 @@
}
}
+ if (mDeviceStateCallback.isInRearDisplayOuterDefaultState()) {
+ final Display[] rearDisplays = mDisplayManager
+ .getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
+ if (rearDisplays.length > 0 && rearDisplays[0].getDisplayId() == t.getDisplayId()) {
+ // Do not change display focus to the inner display if we're in this mode. Note that
+ // in this mode, the inner display is configured as a rear display.
+ Slog.w(TAG, "Ignoring focus change because device is in RDM.");
+ return;
+ }
+ }
+
clearPointerDownOutsideFocusRunnable();
final InputTarget focusedInputTarget = mFocusedInputTarget;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 6c3d516..c19fa8c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -473,35 +473,6 @@
transition.setAllReady();
}
- // TODO(b/365884835): remove this method and callers.
- @Override
- public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
- @NonNull IWindowContainerTransactionCallback callback,
- @NonNull WindowContainerTransaction t) {
- enforceTaskPermission("startLegacyTransition()");
- final CallerInfo caller = new CallerInfo();
- final long ident = Binder.clearCallingIdentity();
- int syncId;
- try {
- synchronized (mGlobalLock) {
- if (type < 0) {
- throw new IllegalArgumentException("Can't create transition with no type");
- }
- if (mTransitionController.getTransitionPlayer() != null) {
- throw new IllegalArgumentException("Can't use legacy transitions in"
- + " when shell transitions are enabled.");
- }
- syncId = startSyncWithOrganizer(callback);
- applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"),
- caller);
- setSyncReady(syncId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return syncId;
- }
-
@Override
public void finishTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4a93904..a270af5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -169,7 +169,6 @@
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
-import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -231,7 +230,6 @@
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.Surface;
-import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewDebug;
@@ -399,7 +397,6 @@
* rotation.
*/
final boolean mForceSeamlesslyRotate;
- SeamlessRotator mPendingSeamlessRotate;
private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -593,13 +590,6 @@
/** The time when the window was last requested to redraw for orientation change. */
private long mOrientationChangeRedrawRequestTime;
- /**
- * The orientation during the last visible call to relayout. If our
- * current orientation is different, the window can't be ready
- * to be shown.
- */
- int mLastVisibleLayoutRotation = -1;
-
/** Is this window now (or just being) removed? */
boolean mRemoved;
@@ -655,15 +645,6 @@
boolean mIsSurfacePositionPaused;
/**
- * During seamless rotation we have two phases, first the old window contents
- * are rotated to look as if they didn't move in the new coordinate system. Then we
- * have to freeze updates to this layer (to preserve the transformation) until
- * the resize actually occurs. This is true from when the transformation is set
- * and false until the transaction to resize is sent.
- */
- boolean mSeamlesslyRotated = false;
-
- /**
* Whether the IME insets have been consumed. If {@code true}, this window won't be able to
* receive visible IME insets; {@code false}, otherwise.
*/
@@ -778,11 +759,6 @@
*/
private boolean mInsetsAnimationRunning;
- private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
- finishSeamlessRotation(t);
- updateSurfacePosition(t);
- };
-
private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
// Only apply the position to the surface when there's no leash created.
if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
@@ -899,69 +875,6 @@
return visible && mFrozenInsetsState == null;
}
- void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
- @Rotation int rotation, boolean requested) {
- // Invisible windows and the wallpaper do not participate in the seamless rotation animation
- if (!isVisibleNow() || mIsWallpaper) {
- return;
- }
-
- if (mToken.hasFixedRotationTransform()) {
- // The transform of its surface is handled by fixed rotation.
- return;
- }
- final Task task = getTask();
- if (task != null && task.inPinnedWindowingMode()) {
- // It is handled by PinnedTaskController. Note that the windowing mode of activity
- // and windows may still be fullscreen.
- return;
- }
-
- if (mPendingSeamlessRotate != null) {
- oldRotation = mPendingSeamlessRotate.getOldRotation();
- }
-
- // Skip performing seamless rotation when the controlled insets is IME with visible state.
- if (mControllableInsetProvider != null
- && mControllableInsetProvider.getSource().getType() == WindowInsets.Type.ime()) {
- return;
- }
-
- if (mForceSeamlesslyRotate || requested) {
- if (mControllableInsetProvider != null) {
- mControllableInsetProvider.startSeamlessRotation();
- }
- mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo(),
- false /* applyFixedTransformationHint */);
- // The surface position is going to be unrotated according to the last position.
- // Make sure the source position is up-to-date.
- mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
- mPendingSeamlessRotate.unrotate(transaction, this);
- getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
- true /* seamlesslyRotated */);
- applyWithNextDraw(mSeamlessRotationFinishedConsumer);
- }
- }
-
- void cancelSeamlessRotation() {
- finishSeamlessRotation(getPendingTransaction());
- }
-
- void finishSeamlessRotation(SurfaceControl.Transaction t) {
- if (mPendingSeamlessRotate == null) {
- return;
- }
-
- mPendingSeamlessRotate.finish(t, this);
- mPendingSeamlessRotate = null;
-
- getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
- false /* seamlesslyRotated */);
- if (mControllableInsetProvider != null) {
- mControllableInsetProvider.finishSeamlessRotation();
- }
- }
-
List<Rect> getSystemGestureExclusion() {
return mExclusionRects;
}
@@ -2176,8 +2089,7 @@
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
&& !isDragResizing()
&& hasMovementAnimation
- && !mWinAnimator.mLastHidden
- && !mSeamlesslyRotated;
+ && !mWinAnimator.mLastHidden;
}
/**
@@ -3998,7 +3910,6 @@
proto.write(REMOVED, mRemoved);
proto.write(IS_ON_SCREEN, isOnScreen());
proto.write(IS_VISIBLE, isVisible);
- proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
proto.write(HAS_COMPAT_SCALE, hasCompatScale());
proto.write(GLOBAL_SCALE, mGlobalScale);
@@ -4144,14 +4055,6 @@
+ " mDestroying=" + mDestroying
+ " mRemoved=" + mRemoved);
}
- pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate
- + " seamlesslyRotate: pending=");
- if (mPendingSeamlessRotate != null) {
- mPendingSeamlessRotate.dump(pw);
- } else {
- pw.print("null");
- }
- pw.println();
if (mXOffset != 0 || mYOffset != 0) {
pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset);
@@ -4883,8 +4786,6 @@
mWinAnimator.mEnterAnimationPending = true;
}
- mLastVisibleLayoutRotation = getDisplayContent().getRotation();
-
mWinAnimator.mEnteringAnimation = true;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
@@ -5282,9 +5183,8 @@
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
- if ((asyncRotationController != null
- && asyncRotationController.hasSeamlessOperation(mToken))
- || mPendingSeamlessRotate != null) {
+ if (asyncRotationController != null
+ && asyncRotationController.hasSeamlessOperation(mToken)) {
// Freeze position while un-rotating the window, so its surface remains at the position
// corresponding to the original rotation.
return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index a34b511..4fb74ef 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -42,9 +42,6 @@
/** Only do a maximum of 6 repeated layouts. After that quit */
private int mLayoutRepeatCount;
- static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 1;
-
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
/** The number of layout requests when deferring. */
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 66d04df..adfabe1 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -169,7 +169,7 @@
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
"android.hardware.common.fmq-V1-ndk",
- "android.hardware.gnss-V3-cpp",
+ "android.hardware.gnss-V5-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
@@ -204,6 +204,7 @@
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend-V1-ndk",
+ "android_location_flags_c_lib",
"server_configurable_flags",
"service.incremental",
],
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e32ce52..ec8794f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -671,9 +671,13 @@
return;
}
- // TODO(b/383092013): Add topology validation
const DisplayTopologyGraph displayTopology =
android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
+ if (input_flags::enable_display_topology_validation() && !displayTopology.isValid()) {
+ LOG(ERROR) << "Ignoring Invalid DisplayTopology";
+ return;
+ }
+
mInputManager->getDispatcher().setDisplayTopology(displayTopology);
mInputManager->getChoreographer().setDisplayTopology(displayTopology);
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 9c033e2..93f6e95 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -36,6 +36,7 @@
#include <android/hardware/gnss/BnGnssMeasurementCallback.h>
#include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
#include <android/hardware/gnss/BnGnssPsdsCallback.h>
+#include <android_location_flags.h>
#include <binder/IServiceManager.h>
#include <nativehelper/JNIHelp.h>
#include <pthread.h>
@@ -53,6 +54,8 @@
#include "gnss/Gnss.h"
#include "gnss/GnssAntennaInfo.h"
#include "gnss/GnssAntennaInfoCallback.h"
+#include "gnss/GnssAssistance.h"
+#include "gnss/GnssAssistanceCallback.h"
#include "gnss/GnssBatching.h"
#include "gnss/GnssConfiguration.h"
#include "gnss/GnssDebug.h"
@@ -114,6 +117,7 @@
using android::hardware::gnss::GnssPowerStats;
using android::hardware::gnss::IGnssPowerIndication;
using android::hardware::gnss::IGnssPowerIndicationCallback;
+using android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
using IGnssAidl = android::hardware::gnss::IGnss;
using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
@@ -140,6 +144,9 @@
std::unique_ptr<android::gnss::GnssVisibilityControlInterface> gnssVisibilityControlIface = nullptr;
std::unique_ptr<android::gnss::MeasurementCorrectionsInterface> gnssMeasurementCorrectionsIface =
nullptr;
+std::unique_ptr<android::gnss::GnssAssistanceInterface> gnssAssistanceIface = nullptr;
+
+namespace location_flags = android::location::flags;
namespace android {
@@ -229,6 +236,9 @@
gnss::GnssVisibilityControl_class_init_once(env, clazz);
gnss::MeasurementCorrections_class_init_once(env, clazz);
gnss::MeasurementCorrectionsCallback_class_init_once(env, clazz);
+ if (location_flags::gnss_assistance_interface_jni()) {
+ gnss::GnssAssistance_class_init_once(env, clazz);
+ }
gnss::Utils_class_init_once(env);
}
@@ -266,7 +276,9 @@
gnssBatchingIface = gnssHal->getGnssBatchingInterface();
gnssVisibilityControlIface = gnssHal->getGnssVisibilityControlInterface();
gnssPowerIndicationIface = gnssHal->getGnssPowerIndicationInterface();
-
+ if (location_flags::gnss_assistance_interface_jni()) {
+ gnssAssistanceIface = gnssHal->getGnssAssistanceInterface();
+ }
if (mCallbacksObj) {
ALOGE("Callbacks already initialized");
} else {
@@ -355,13 +367,22 @@
// Set IGnssPowerIndication.hal callback.
if (gnssPowerIndicationIface != nullptr) {
sp<IGnssPowerIndicationCallback> gnssPowerIndicationCallback =
- new GnssPowerIndicationCallback();
+ sp<GnssPowerIndicationCallback>::make();
auto status = gnssPowerIndicationIface->setCallback(gnssPowerIndicationCallback);
if (!checkAidlStatus(status, "IGnssPowerIndication setCallback() failed.")) {
gnssPowerIndicationIface = nullptr;
}
}
+ // Set IGnssAssistance callback.
+ if (gnssAssistanceIface != nullptr) {
+ sp<IGnssAssistanceCallback> gnssAssistanceCallback =
+ sp<gnss::GnssAssistanceCallback>::make();
+ if (!gnssAssistanceIface->setCallback(gnssAssistanceCallback)) {
+ ALOGI("IGnssAssistanceInterface setCallback() failed");
+ }
+ }
+
return JNI_TRUE;
}
@@ -493,6 +514,15 @@
gnssPsdsIface->injectPsdsData(data, length, psdsType);
}
+static void android_location_gnss_hal_GnssNative_inject_gnss_assistance(JNIEnv* env, jclass,
+ jobject gnssAssistanceObj) {
+ if (gnssAssistanceIface == nullptr) {
+ ALOGE("%s: IGnssAssistance interface not available.", __func__);
+ return;
+ }
+ gnssAssistanceIface->injectGnssAssistance(env, gnssAssistanceObj);
+}
+
static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
JNIEnv* env, jobject /* obj */, jlong networkHandle, jstring apn, jint apnIpType) {
if (apn == nullptr) {
@@ -937,6 +967,8 @@
{"native_stop_nmea_message_collection", "()Z",
reinterpret_cast<void*>(
android_location_gnss_hal_GnssNative_stop_nmea_message_collection)},
+ {"native_inject_gnss_assistance", "(Landroid/location/GnssAssistance;)V",
+ reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_gnss_assistance)},
};
static const JNINativeMethod sBatchingMethods[] = {
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index eb729de..0bee718 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -60,6 +60,8 @@
void unregisterRuntimeSensor(jint handle);
jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp,
jfloatArray values);
+ jboolean sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type, jint serial,
+ jlong timestamp, jfloatArray values);
private:
sp<SensorService> mService;
@@ -172,9 +174,9 @@
sensors_event_t event{
.version = sizeof(sensors_event_t),
- .timestamp = timestamp,
.sensor = handle,
.type = type,
+ .timestamp = timestamp,
};
int valuesLength = env->GetArrayLength(values);
@@ -234,6 +236,42 @@
return err == OK;
}
+jboolean NativeSensorService::sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type,
+ jint serial, jlong timestamp,
+ jfloatArray values) {
+ if (mService == nullptr) {
+ ALOGD("Dropping sendRuntimeSensorAdditionalInfo, sensor service not available.");
+ return false;
+ }
+
+ sensors_event_t event{
+ .version = sizeof(sensors_event_t),
+ .sensor = handle,
+ .type = SENSOR_TYPE_ADDITIONAL_INFO,
+ .timestamp = timestamp,
+ .additional_info =
+ (additional_info_event_t){
+ .type = type,
+ .serial = serial,
+ },
+ };
+
+ if (values != nullptr) {
+ int valuesLength = env->GetArrayLength(values);
+ if (valuesLength > 14) {
+ ALOGD("Dropping sendRuntimeSensorAdditionalInfo, number of values exceeds maximum.");
+ return false;
+ }
+ if (valuesLength > 0) {
+ jfloat* sensorValues = env->GetFloatArrayElements(values, nullptr);
+ memcpy(event.additional_info.data_float, sensorValues, valuesLength * sizeof(float));
+ }
+ }
+
+ status_t err = mService->sendRuntimeSensorEvent(event);
+ return err == OK;
+}
+
NativeSensorService::ProximityActiveListenerDelegate::ProximityActiveListenerDelegate(
JNIEnv* env, jobject listener)
: mListener(env->NewGlobalRef(listener)) {}
@@ -326,6 +364,13 @@
return service->sendRuntimeSensorEvent(env, handle, type, timestamp, values);
}
+static jboolean sendRuntimeSensorAdditionalInfoNative(JNIEnv* env, jclass, jlong ptr, jint handle,
+ jint type, jint serial, jlong timestamp,
+ jfloatArray values) {
+ auto* service = reinterpret_cast<NativeSensorService*>(ptr);
+ return service->sendRuntimeSensorAdditionalInfo(env, handle, type, serial, timestamp, values);
+}
+
static const JNINativeMethod methods[] = {
{"startSensorServiceNative", "(L" PROXIMITY_ACTIVE_CLASS ";)J",
reinterpret_cast<void*>(startSensorServiceNative)},
@@ -340,6 +385,8 @@
reinterpret_cast<void*>(unregisterRuntimeSensorNative)},
{"sendRuntimeSensorEventNative", "(JIIJ[F)Z",
reinterpret_cast<void*>(sendRuntimeSensorEventNative)},
+ {"sendRuntimeSensorAdditionalInfoNative", "(JIIIJ[F)Z",
+ reinterpret_cast<void*>(sendRuntimeSensorAdditionalInfoNative)},
};
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index e72259f..562e82f 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -17,7 +17,6 @@
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
-
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
],
@@ -41,6 +40,8 @@
"GnssMeasurementCallback.cpp",
"GnssNavigationMessage.cpp",
"GnssNavigationMessageCallback.cpp",
+ "GnssAssistance.cpp",
+ "GnssAssistanceCallback.cpp",
"GnssPsds.cpp",
"GnssPsdsCallback.cpp",
"GnssVisibilityControl.cpp",
@@ -61,7 +62,7 @@
"libnativehelper",
"libhardware_legacy",
"libutils",
- "android.hardware.gnss-V3-cpp",
+ "android.hardware.gnss-V5-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/gnss/Gnss.cpp b/services/core/jni/gnss/Gnss.cpp
index da8928b5..a3fd9aa 100644
--- a/services/core/jni/gnss/Gnss.cpp
+++ b/services/core/jni/gnss/Gnss.cpp
@@ -765,4 +765,15 @@
return nullptr;
}
+std::unique_ptr<GnssAssistanceInterface> GnssHal::getGnssAssistanceInterface() {
+ if (gnssHalAidl != nullptr) {
+ sp<hardware::gnss::gnss_assistance::IGnssAssistanceInterface> gnssAssistance;
+ auto status = gnssHalAidl->getExtensionGnssAssistanceInterface(&gnssAssistance);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssAssistance")) {
+ return std::make_unique<GnssAssistanceInterface>(gnssAssistance);
+ }
+ }
+ return nullptr;
+}
+
} // namespace android::gnss
diff --git a/services/core/jni/gnss/Gnss.h b/services/core/jni/gnss/Gnss.h
index 458da8a..2b6b751 100644
--- a/services/core/jni/gnss/Gnss.h
+++ b/services/core/jni/gnss/Gnss.h
@@ -34,6 +34,7 @@
#include "AGnss.h"
#include "AGnssRil.h"
#include "GnssAntennaInfo.h"
+#include "GnssAssistance.h"
#include "GnssBatching.h"
#include "GnssCallback.h"
#include "GnssConfiguration.h"
@@ -115,6 +116,7 @@
std::unique_ptr<GnssVisibilityControlInterface> getGnssVisibilityControlInterface();
std::unique_ptr<GnssAntennaInfoInterface> getGnssAntennaInfoInterface();
std::unique_ptr<GnssPsdsInterface> getGnssPsdsInterface();
+ std::unique_ptr<GnssAssistanceInterface> getGnssAssistanceInterface();
sp<hardware::gnss::IGnssPowerIndication> getGnssPowerIndicationInterface();
sp<hardware::gnss::V1_0::IGnssNi> getGnssNiInterface();
diff --git a/services/core/jni/gnss/GnssAssistance.cpp b/services/core/jni/gnss/GnssAssistance.cpp
new file mode 100644
index 0000000..fff396e
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.cpp
@@ -0,0 +1,2047 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+
+#define LOG_TAG "GnssAssistanceJni"
+
+#include "GnssAssistance.h"
+
+#include <utils/String16.h>
+
+#include "GnssAssistanceCallback.h"
+#include "Utils.h"
+
+namespace android::gnss {
+
+using GnssConstellationType = android::hardware::gnss::GnssConstellationType;
+using GnssCorrectionComponent = android::hardware::gnss::gnss_assistance::GnssCorrectionComponent;
+using GnssInterval =
+ android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::GnssInterval;
+using GnssSatelliteAlmanac =
+ android::hardware::gnss::gnss_assistance::GnssAlmanac::GnssSatelliteAlmanac;
+using IonosphericCorrection = android::hardware::gnss::gnss_assistance::IonosphericCorrection;
+using PseudorangeCorrection =
+ android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::PseudorangeCorrection;
+using GalileoSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+ GalileoSatelliteEphemeris::GalileoSatelliteClockModel;
+using GalileoSvHealth =
+ android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris::GalileoSvHealth;
+using GlonassSatelliteAlmanac =
+ android::hardware::gnss::gnss_assistance::GlonassAlmanac::GlonassSatelliteAlmanac;
+using GlonassSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+ GlonassSatelliteEphemeris::GlonassSatelliteClockModel;
+using GlonassSatelliteOrbitModel = android::hardware::gnss::gnss_assistance::
+ GlonassSatelliteEphemeris::GlonassSatelliteOrbitModel;
+using GnssSignalType = hardware::gnss::GnssSignalType;
+using GnssConstellationType = hardware::gnss::GnssConstellationType;
+using BeidouB1CSatelliteOrbitType =
+ android::hardware::gnss::gnss_assistance::AuxiliaryInformation::BeidouB1CSatelliteOrbitType;
+using QzssSatelliteEphemeris = android::hardware::gnss::gnss_assistance::QzssSatelliteEphemeris;
+
+// Implementation of GnssAssistance (AIDL HAL)
+
+namespace {
+jmethodID method_gnssAssistanceGetGpsAssistance;
+jmethodID method_gnssAssistanceGetGlonassAssistance;
+jmethodID method_gnssAssistanceGetGalileoAssistance;
+jmethodID method_gnssAssistanceGetBeidouAssistance;
+jmethodID method_gnssAssistanceGetQzssAssistance;
+
+jmethodID method_listSize;
+jmethodID method_listGet;
+
+jmethodID method_gnssAlmanacGetIssueDateMillis;
+jmethodID method_gnssAlmanacGetIoda;
+jmethodID method_gnssAlmanacGetWeekNumber;
+jmethodID method_gnssAlmanacGetToaSeconds;
+jmethodID method_gnssAlmanacGetSatelliteAlmanacs;
+jmethodID method_gnssAlmanacIsCompleteAlmanacProvided;
+jmethodID method_satelliteAlmanacGetSvid;
+jmethodID method_satelliteAlmanacGetSvHealth;
+jmethodID method_satelliteAlmanacGetAf0;
+jmethodID method_satelliteAlmanacGetAf1;
+jmethodID method_satelliteAlmanacGetEccentricity;
+jmethodID method_satelliteAlmanacGetInclination;
+jmethodID method_satelliteAlmanacGetM0;
+jmethodID method_satelliteAlmanacGetOmega;
+jmethodID method_satelliteAlmanacGetOmega0;
+jmethodID method_satelliteAlmanacGetOmegaDot;
+jmethodID method_satelliteAlmanacGetRootA;
+
+jmethodID method_satelliteEphemerisTimeGetIode;
+jmethodID method_satelliteEphemerisTimeGetToeSeconds;
+jmethodID method_satelliteEphemerisTimeGetWeekNumber;
+
+jmethodID method_keplerianOrbitModelGetDeltaN;
+jmethodID method_keplerianOrbitModelGetEccentricity;
+jmethodID method_keplerianOrbitModelGetI0;
+jmethodID method_keplerianOrbitModelGetIDot;
+jmethodID method_keplerianOrbitModelGetM0;
+jmethodID method_keplerianOrbitModelGetOmega;
+jmethodID method_keplerianOrbitModelGetOmega0;
+jmethodID method_keplerianOrbitModelGetOmegaDot;
+jmethodID method_keplerianOrbitModelGetRootA;
+jmethodID method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation;
+jmethodID method_secondOrderHarmonicPerturbationGetCic;
+jmethodID method_secondOrderHarmonicPerturbationGetCis;
+jmethodID method_secondOrderHarmonicPerturbationGetCrc;
+jmethodID method_secondOrderHarmonicPerturbationGetCrs;
+jmethodID method_secondOrderHarmonicPerturbationGetCuc;
+jmethodID method_secondOrderHarmonicPerturbationGetCus;
+
+jmethodID method_klobucharIonosphericModelGetAlpha0;
+jmethodID method_klobucharIonosphericModelGetAlpha1;
+jmethodID method_klobucharIonosphericModelGetAlpha2;
+jmethodID method_klobucharIonosphericModelGetAlpha3;
+jmethodID method_klobucharIonosphericModelGetBeta0;
+jmethodID method_klobucharIonosphericModelGetBeta1;
+jmethodID method_klobucharIonosphericModelGetBeta2;
+jmethodID method_klobucharIonosphericModelGetBeta3;
+
+jmethodID method_utcModelGetA0;
+jmethodID method_utcModelGetA1;
+jmethodID method_utcModelGetTimeOfWeek;
+jmethodID method_utcModelGetWeekNumber;
+
+jmethodID method_leapSecondsModelGetDayNumberLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetLeapSeconds;
+jmethodID method_leapSecondsModelGetLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetWeekNumberLeapSecondsFuture;
+
+jmethodID method_timeModelsGetTimeOfWeek;
+jmethodID method_timeModelsGetToGnss;
+jmethodID method_timeModelsGetWeekNumber;
+jmethodID method_timeModelsGetA0;
+jmethodID method_timeModelsGetA1;
+
+jmethodID method_realTimeIntegrityModelGetBadSvid;
+jmethodID method_realTimeIntegrityModelGetBadSignalTypes;
+jmethodID method_realTimeIntegrityModelGetStartDateSeconds;
+jmethodID method_realTimeIntegrityModelGetEndDateSeconds;
+jmethodID method_realTimeIntegrityModelGetPublishDateSeconds;
+jmethodID method_realTimeIntegrityModelGetAdvisoryNumber;
+jmethodID method_realTimeIntegrityModelGetAdvisoryType;
+
+jmethodID method_gnssSignalTypeGetConstellationType;
+jmethodID method_gnssSignalTypeGetCarrierFrequencyHz;
+jmethodID method_gnssSignalTypeGetCodeType;
+
+jmethodID method_auxiliaryInformationGetSvid;
+jmethodID method_auxiliaryInformationGetAvailableSignalTypes;
+jmethodID method_auxiliaryInformationGetFrequencyChannelNumber;
+jmethodID method_auxiliaryInformationGetSatType;
+
+jmethodID method_satelliteCorrectionGetSvid;
+jmethodID method_satelliteCorrectionGetIonosphericCorrections;
+jmethodID method_ionosphericCorrectionGetCarrierFrequencyHz;
+jmethodID method_ionosphericCorrectionGetIonosphericCorrection;
+jmethodID method_gnssCorrectionComponentGetPseudorangeCorrection;
+jmethodID method_gnssCorrectionComponentGetSourceKey;
+jmethodID method_gnssCorrectionComponentGetValidityInterval;
+jmethodID method_pseudorangeCorrectionGetCorrectionMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionUncertaintyMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond;
+jmethodID method_gnssIntervalGetStartMillisSinceGpsEpoch;
+jmethodID method_gnssIntervalGetEndMillisSinceGpsEpoch;
+
+jmethodID method_gpsAssistanceGetAlmanac;
+jmethodID method_gpsAssistanceGetIonosphericModel;
+jmethodID method_gpsAssistanceGetUtcModel;
+jmethodID method_gpsAssistanceGetLeapSecondsModel;
+jmethodID method_gpsAssistanceGetTimeModels;
+jmethodID method_gpsAssistanceGetSatelliteEphemeris;
+jmethodID method_gpsAssistanceGetRealTimeIntegrityModels;
+jmethodID method_gpsAssistanceGetSatelliteCorrections;
+jmethodID method_gpsSatelliteEphemerisGetSvid;
+jmethodID method_gpsSatelliteEphemerisGetGpsL2Params;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_gpsL2ParamsGetL2Code;
+jmethodID method_gpsL2ParamsGetL2Flag;
+jmethodID method_gpsSatelliteClockModelGetAf0;
+jmethodID method_gpsSatelliteClockModelGetAf1;
+jmethodID method_gpsSatelliteClockModelGetAf2;
+jmethodID method_gpsSatelliteClockModelGetTgd;
+jmethodID method_gpsSatelliteClockModelGetIodc;
+jmethodID method_gpsSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_gpsSatelliteHealthGetFitInt;
+jmethodID method_gpsSatelliteHealthGetSvAccur;
+jmethodID method_gpsSatelliteHealthGetSvHealth;
+
+jmethodID method_beidouAssistanceGetAlmanac;
+jmethodID method_beidouAssistanceGetIonosphericModel;
+jmethodID method_beidouAssistanceGetUtcModel;
+jmethodID method_beidouAssistanceGetLeapSecondsModel;
+jmethodID method_beidouAssistanceGetTimeModels;
+jmethodID method_beidouAssistanceGetSatelliteEphemeris;
+jmethodID method_beidouAssistanceGetSatelliteCorrections;
+jmethodID method_beidouAssistanceGetRealTimeIntegrityModels;
+jmethodID method_beidouSatelliteEphemerisGetSvid;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_beidouSatelliteClockModelGetAf0;
+jmethodID method_beidouSatelliteClockModelGetAf1;
+jmethodID method_beidouSatelliteClockModelGetAf2;
+jmethodID method_beidouSatelliteClockModelGetAodc;
+jmethodID method_beidouSatelliteClockModelGetTgd1;
+jmethodID method_beidouSatelliteClockModelGetTgd2;
+jmethodID method_beidouSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_beidouSatelliteHealthGetSatH1;
+jmethodID method_beidouSatelliteHealthGetSvAccur;
+jmethodID method_beidouSatelliteEphemerisTimeGetIode;
+jmethodID method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber;
+jmethodID method_beidouSatelliteEphemerisTimeGetToeSeconds;
+
+jmethodID method_galileoAssistanceGetAlmanac;
+jmethodID method_galileoAssistanceGetIonosphericModel;
+jmethodID method_galileoAssistanceGetUtcModel;
+jmethodID method_galileoAssistanceGetLeapSecondsModel;
+jmethodID method_galileoAssistanceGetTimeModels;
+jmethodID method_galileoAssistanceGetSatelliteEphemeris;
+jmethodID method_galileoAssistanceGetSatelliteCorrections;
+jmethodID method_galileoAssistanceGetRealTimeIntegrityModels;
+jmethodID method_galileoSatelliteEphemerisGetSvid;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteClockModels;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_galileoSatelliteClockModelGetAf0;
+jmethodID method_galileoSatelliteClockModelGetAf1;
+jmethodID method_galileoSatelliteClockModelGetAf2;
+jmethodID method_galileoSatelliteClockModelGetBgdSeconds;
+jmethodID method_galileoSatelliteClockModelGetSatelliteClockType;
+jmethodID method_galileoSatelliteClockModelGetSisaMeters;
+jmethodID method_galileoSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_galileoSvHealthGetDataValidityStatusE1b;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5a;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE1b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5a;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5b;
+jmethodID method_galileoIonosphericModelGetAi0;
+jmethodID method_galileoIonosphericModelGetAi1;
+jmethodID method_galileoIonosphericModelGetAi2;
+
+jmethodID method_glonassAssistanceGetAlmanac;
+jmethodID method_glonassAssistanceGetUtcModel;
+jmethodID method_glonassAssistanceGetTimeModels;
+jmethodID method_glonassAssistanceGetSatelliteEphemeris;
+jmethodID method_glonassAssistanceGetSatelliteCorrections;
+jmethodID method_glonassAlmanacGetIssueDateMillis;
+jmethodID method_glonassAlmanacGetSatelliteAlmanacs;
+jmethodID method_glonassSatelliteAlmanacGetDeltaI;
+jmethodID method_glonassSatelliteAlmanacGetDeltaT;
+jmethodID method_glonassSatelliteAlmanacGetDeltaTDot;
+jmethodID method_glonassSatelliteAlmanacGetEccentricity;
+jmethodID method_glonassSatelliteAlmanacGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteAlmanacGetLambda;
+jmethodID method_glonassSatelliteAlmanacGetOmega;
+jmethodID method_glonassSatelliteAlmanacGetSlotNumber;
+jmethodID method_glonassSatelliteAlmanacGetHealthState;
+jmethodID method_glonassSatelliteAlmanacGetTLambda;
+jmethodID method_glonassSatelliteAlmanacGetTau;
+jmethodID method_glonassSatelliteAlmanacGetIsGlonassM;
+jmethodID method_glonassSatelliteAlmanacGetCalendarDayNumber;
+jmethodID method_glonassSatelliteEphemerisGetAgeInDays;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_glonassSatelliteEphemerisGetHealthState;
+jmethodID method_glonassSatelliteEphemerisGetSlotNumber;
+jmethodID method_glonassSatelliteEphemerisGetFrameTimeSeconds;
+jmethodID method_glonassSatelliteEphemerisGetUpdateIntervalMinutes;
+jmethodID method_glonassSatelliteEphemerisGetIsGlonassM;
+jmethodID method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd;
+
+jmethodID method_glonassSatelliteOrbitModelGetX;
+jmethodID method_glonassSatelliteOrbitModelGetY;
+jmethodID method_glonassSatelliteOrbitModelGetZ;
+jmethodID method_glonassSatelliteOrbitModelGetXAccel;
+jmethodID method_glonassSatelliteOrbitModelGetYAccel;
+jmethodID method_glonassSatelliteOrbitModelGetZAccel;
+jmethodID method_glonassSatelliteOrbitModelGetXDot;
+jmethodID method_glonassSatelliteOrbitModelGetYDot;
+jmethodID method_glonassSatelliteOrbitModelGetZDot;
+jmethodID method_glonassSatelliteClockModelGetClockBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteClockModelGetTimeOfClockSeconds;
+
+jmethodID method_qzssAssistanceGetAlmanac;
+jmethodID method_qzssAssistanceGetIonosphericModel;
+jmethodID method_qzssAssistanceGetUtcModel;
+jmethodID method_qzssAssistanceGetLeapSecondsModel;
+jmethodID method_qzssAssistanceGetTimeModels;
+jmethodID method_qzssAssistanceGetSatelliteEphemeris;
+jmethodID method_qzssAssistanceGetSatelliteCorrections;
+jmethodID method_qzssAssistanceGetRealTimeIntegrityModels;
+jmethodID method_qzssSatelliteEphemerisGetSvid;
+jmethodID method_qzssSatelliteEphemerisGetGpsL2Params;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_qzssSatelliteClockModelGetAf0;
+jmethodID method_qzssSatelliteClockModelGetAf1;
+jmethodID method_qzssSatelliteClockModelGetAf2;
+jmethodID method_qzssSatelliteClockModelGetAodc;
+jmethodID method_qzssSatelliteClockModelGetTgd1;
+jmethodID method_qzssSatelliteClockModelGetTgd2;
+jmethodID method_qzssSatelliteClockModelGetTimeOfClockSeconds;
+} // namespace
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz) {
+ // Get the methods of GnssAssistance class.
+ jclass gnssAssistanceClass = env->FindClass("android/location/GnssAssistance");
+
+ method_gnssAssistanceGetGpsAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getGpsAssistance",
+ "()Landroid/location/GpsAssistance;");
+ method_gnssAssistanceGetGlonassAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getGlonassAssistance",
+ "()Landroid/location/GlonassAssistance;");
+ method_gnssAssistanceGetGalileoAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getGalileoAssistance",
+ "()Landroid/location/GalileoAssistance;");
+ method_gnssAssistanceGetBeidouAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getBeidouAssistance",
+ "()Landroid/location/BeidouAssistance;");
+ method_gnssAssistanceGetQzssAssistance =
+ env->GetMethodID(gnssAssistanceClass, "getQzssAssistance",
+ "()Landroid/location/QzssAssistance;");
+
+ // Get the methods of List class.
+ jclass listClass = env->FindClass("java/util/List");
+
+ method_listSize = env->GetMethodID(listClass, "size", "()I");
+ method_listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
+
+ // Get the methods of GnssAlmanac class.
+ jclass gnssAlmanacClass = env->FindClass("android/location/GnssAlmanac");
+
+ method_gnssAlmanacGetIssueDateMillis =
+ env->GetMethodID(gnssAlmanacClass, "getIssueDateMillis", "()J");
+ method_gnssAlmanacGetIoda = env->GetMethodID(gnssAlmanacClass, "getIoda", "()I");
+ method_gnssAlmanacGetWeekNumber = env->GetMethodID(gnssAlmanacClass, "getWeekNumber", "()I");
+ method_gnssAlmanacGetToaSeconds = env->GetMethodID(gnssAlmanacClass, "getToaSeconds", "()I");
+ method_gnssAlmanacGetSatelliteAlmanacs =
+ env->GetMethodID(gnssAlmanacClass, "getGnssSatelliteAlmanacs", "()Ljava/util/List;");
+ method_gnssAlmanacIsCompleteAlmanacProvided =
+ env->GetMethodID(gnssAlmanacClass, "isCompleteAlmanacProvided", "()Z");
+
+ // Get the methods of SatelliteAlmanac class.
+ jclass satelliteAlmanacClass =
+ env->FindClass("android/location/GnssAlmanac$GnssSatelliteAlmanac");
+
+ method_satelliteAlmanacGetSvid = env->GetMethodID(satelliteAlmanacClass, "getSvid", "()I");
+ method_satelliteAlmanacGetSvHealth =
+ env->GetMethodID(satelliteAlmanacClass, "getSvHealth", "()I");
+ method_satelliteAlmanacGetAf0 = env->GetMethodID(satelliteAlmanacClass, "getAf0", "()D");
+ method_satelliteAlmanacGetAf1 = env->GetMethodID(satelliteAlmanacClass, "getAf1", "()D");
+ method_satelliteAlmanacGetEccentricity =
+ env->GetMethodID(satelliteAlmanacClass, "getEccentricity", "()D");
+ method_satelliteAlmanacGetInclination =
+ env->GetMethodID(satelliteAlmanacClass, "getInclination", "()D");
+ method_satelliteAlmanacGetM0 = env->GetMethodID(satelliteAlmanacClass, "getM0", "()D");
+ method_satelliteAlmanacGetOmega = env->GetMethodID(satelliteAlmanacClass, "getOmega", "()D");
+ method_satelliteAlmanacGetOmega0 = env->GetMethodID(satelliteAlmanacClass, "getOmega0", "()D");
+ method_satelliteAlmanacGetOmegaDot =
+ env->GetMethodID(satelliteAlmanacClass, "getOmegaDot", "()D");
+ method_satelliteAlmanacGetRootA = env->GetMethodID(satelliteAlmanacClass, "getRootA", "()D");
+
+ // Get the mothods of SatelliteEphemerisTime class.
+ jclass satelliteEphemerisTimeClass = env->FindClass("android/location/SatelliteEphemerisTime");
+
+ method_satelliteEphemerisTimeGetIode =
+ env->GetMethodID(satelliteEphemerisTimeClass, "getIode", "()I");
+ method_satelliteEphemerisTimeGetToeSeconds =
+ env->GetMethodID(satelliteEphemerisTimeClass, "getToeSeconds", "()I");
+ method_satelliteEphemerisTimeGetWeekNumber =
+ env->GetMethodID(satelliteEphemerisTimeClass, "getWeekNumber", "()I");
+
+ // Get the mothods of KeplerianOrbitModel class.
+ jclass keplerianOrbitModelClass = env->FindClass("android/location/KeplerianOrbitModel");
+
+ method_keplerianOrbitModelGetDeltaN =
+ env->GetMethodID(keplerianOrbitModelClass, "getDeltaN", "()D");
+ method_keplerianOrbitModelGetEccentricity =
+ env->GetMethodID(keplerianOrbitModelClass, "getEccentricity", "()D");
+ method_keplerianOrbitModelGetI0 = env->GetMethodID(keplerianOrbitModelClass, "getI0", "()D");
+ method_keplerianOrbitModelGetIDot =
+ env->GetMethodID(keplerianOrbitModelClass, "getIDot", "()D");
+ method_keplerianOrbitModelGetM0 = env->GetMethodID(keplerianOrbitModelClass, "getM0", "()D");
+ method_keplerianOrbitModelGetOmega =
+ env->GetMethodID(keplerianOrbitModelClass, "getOmega", "()D");
+ method_keplerianOrbitModelGetOmega0 =
+ env->GetMethodID(keplerianOrbitModelClass, "getOmega0", "()D");
+ method_keplerianOrbitModelGetOmegaDot =
+ env->GetMethodID(keplerianOrbitModelClass, "getOmegaDot", "()D");
+ method_keplerianOrbitModelGetRootA =
+ env->GetMethodID(keplerianOrbitModelClass, "getRootA", "()D");
+ method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation =
+ env->GetMethodID(keplerianOrbitModelClass, "getSecondOrderHarmonicPerturbation",
+ "()Landroid/location/"
+ "KeplerianOrbitModel$SecondOrderHarmonicPerturbation;");
+
+ // Get the methods of SecondOrderHarmonicPerturbation class.
+ jclass secondOrderHarmonicPerturbationClass =
+ env->FindClass("android/location/KeplerianOrbitModel$SecondOrderHarmonicPerturbation");
+
+ method_secondOrderHarmonicPerturbationGetCic =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCic", "()D");
+ method_secondOrderHarmonicPerturbationGetCis =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCis", "()D");
+ method_secondOrderHarmonicPerturbationGetCrc =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrc", "()D");
+ method_secondOrderHarmonicPerturbationGetCrs =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrs", "()D");
+ method_secondOrderHarmonicPerturbationGetCuc =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCuc", "()D");
+ method_secondOrderHarmonicPerturbationGetCus =
+ env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCus", "()D");
+
+ // Get the methods of KlobucharIonosphericModel class.
+ jclass klobucharIonosphericModelClass =
+ env->FindClass("android/location/KlobucharIonosphericModel");
+
+ method_klobucharIonosphericModelGetAlpha0 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha0", "()D");
+ method_klobucharIonosphericModelGetAlpha1 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha1", "()D");
+ method_klobucharIonosphericModelGetAlpha2 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha2", "()D");
+ method_klobucharIonosphericModelGetAlpha3 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getAlpha3", "()D");
+ method_klobucharIonosphericModelGetBeta0 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta0", "()D");
+ method_klobucharIonosphericModelGetBeta1 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta1", "()D");
+ method_klobucharIonosphericModelGetBeta2 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta2", "()D");
+ method_klobucharIonosphericModelGetBeta3 =
+ env->GetMethodID(klobucharIonosphericModelClass, "getBeta3", "()D");
+
+ // Get the methods of UtcModel class.
+ jclass utcModelClass = env->FindClass("android/location/UtcModel");
+
+ method_utcModelGetA0 = env->GetMethodID(utcModelClass, "getA0", "()D");
+ method_utcModelGetA1 = env->GetMethodID(utcModelClass, "getA1", "()D");
+ method_utcModelGetTimeOfWeek = env->GetMethodID(utcModelClass, "getTimeOfWeek", "()I");
+ method_utcModelGetWeekNumber = env->GetMethodID(utcModelClass, "getWeekNumber", "()I");
+
+ // Get the methods of LeapSecondsModel class.
+ jclass leapSecondsModelClass = env->FindClass("android/location/LeapSecondsModel");
+
+ method_leapSecondsModelGetDayNumberLeapSecondsFuture =
+ env->GetMethodID(leapSecondsModelClass, "getDayNumberLeapSecondsFuture", "()I");
+ method_leapSecondsModelGetLeapSeconds =
+ env->GetMethodID(leapSecondsModelClass, "getLeapSeconds", "()I");
+ method_leapSecondsModelGetLeapSecondsFuture =
+ env->GetMethodID(leapSecondsModelClass, "getLeapSecondsFuture", "()I");
+ method_leapSecondsModelGetWeekNumberLeapSecondsFuture =
+ env->GetMethodID(leapSecondsModelClass, "getWeekNumberLeapSecondsFuture", "()I");
+
+ // Get the methods of TimeModel class.
+ jclass timeModelsClass = env->FindClass("android/location/TimeModel");
+
+ method_timeModelsGetTimeOfWeek = env->GetMethodID(timeModelsClass, "getTimeOfWeek", "()I");
+ method_timeModelsGetToGnss = env->GetMethodID(timeModelsClass, "getToGnss", "()I");
+ method_timeModelsGetWeekNumber = env->GetMethodID(timeModelsClass, "getWeekNumber", "()I");
+ method_timeModelsGetA0 = env->GetMethodID(timeModelsClass, "getA0", "()D");
+ method_timeModelsGetA1 = env->GetMethodID(timeModelsClass, "getA1", "()D");
+
+ // Get the methods of AuxiliaryInformation class.
+ jclass auxiliaryInformationClass = env->FindClass("android/location/AuxiliaryInformation");
+
+ method_auxiliaryInformationGetSvid =
+ env->GetMethodID(auxiliaryInformationClass, "getSvid", "()I");
+ method_auxiliaryInformationGetAvailableSignalTypes =
+ env->GetMethodID(auxiliaryInformationClass, "getAvailableSignalTypes",
+ "()Ljava/util/List;");
+ method_auxiliaryInformationGetFrequencyChannelNumber =
+ env->GetMethodID(auxiliaryInformationClass, "getFrequencyChannelNumber", "()I");
+ method_auxiliaryInformationGetSatType =
+ env->GetMethodID(auxiliaryInformationClass, "getSatType", "()I");
+
+ // Get the methods of RealTimeIntegrityModel
+ jclass realTimeIntegrityModelClass = env->FindClass("android/location/RealTimeIntegrityModel");
+
+ method_realTimeIntegrityModelGetBadSvid =
+ env->GetMethodID(realTimeIntegrityModelClass, "getBadSvid", "()I");
+ method_realTimeIntegrityModelGetBadSignalTypes =
+ env->GetMethodID(realTimeIntegrityModelClass, "getBadSignalTypes",
+ "()Ljava/util/List;");
+ method_realTimeIntegrityModelGetStartDateSeconds =
+ env->GetMethodID(realTimeIntegrityModelClass, "getStartDateSeconds", "()J");
+ method_realTimeIntegrityModelGetEndDateSeconds =
+ env->GetMethodID(realTimeIntegrityModelClass, "getEndDateSeconds", "()J");
+ method_realTimeIntegrityModelGetPublishDateSeconds =
+ env->GetMethodID(realTimeIntegrityModelClass, "getPublishDateSeconds", "()J");
+ method_realTimeIntegrityModelGetAdvisoryNumber =
+ env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryNumber",
+ "()Ljava/lang/String;");
+ method_realTimeIntegrityModelGetAdvisoryType =
+ env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryType",
+ "()Ljava/lang/String;");
+
+ // Get the methods of GnssSignalType class.
+ jclass gnssSignalTypeClass = env->FindClass("android/location/GnssSignalType");
+
+ method_gnssSignalTypeGetConstellationType =
+ env->GetMethodID(gnssSignalTypeClass, "getConstellationType", "()I");
+ method_gnssSignalTypeGetCarrierFrequencyHz =
+ env->GetMethodID(gnssSignalTypeClass, "getCarrierFrequencyHz", "()D");
+ method_gnssSignalTypeGetCodeType =
+ env->GetMethodID(gnssSignalTypeClass, "getCodeType", "()Ljava/lang/String;");
+
+ // Get the methods of SatelliteCorrection class.
+ jclass satelliteCorrectionClass =
+ env->FindClass("android/location/GnssAssistance$GnssSatelliteCorrections");
+
+ method_satelliteCorrectionGetSvid =
+ env->GetMethodID(satelliteCorrectionClass, "getSvid", "()I");
+ method_satelliteCorrectionGetIonosphericCorrections =
+ env->GetMethodID(satelliteCorrectionClass, "getIonosphericCorrections",
+ "()Ljava/util/List;");
+
+ // Get the methods of IonosphericCorrection class.
+ jclass ionosphericCorrectionClass = env->FindClass("android/location/IonosphericCorrection");
+
+ method_ionosphericCorrectionGetCarrierFrequencyHz =
+ env->GetMethodID(ionosphericCorrectionClass, "getCarrierFrequencyHz", "()J");
+ method_ionosphericCorrectionGetIonosphericCorrection =
+ env->GetMethodID(ionosphericCorrectionClass, "getIonosphericCorrection",
+ "()Landroid/location/GnssCorrectionComponent;");
+
+ // Get the methods of GnssCorrectionComponent class.
+ jclass gnssCorrectionComponentClass =
+ env->FindClass("android/location/GnssCorrectionComponent");
+
+ method_gnssCorrectionComponentGetPseudorangeCorrection =
+ env->GetMethodID(gnssCorrectionComponentClass, "getPseudorangeCorrection",
+ "()Landroid/location/GnssCorrectionComponent$PseudorangeCorrection;");
+ method_gnssCorrectionComponentGetSourceKey =
+ env->GetMethodID(gnssCorrectionComponentClass, "getSourceKey", "()Ljava/lang/String;");
+ method_gnssCorrectionComponentGetValidityInterval =
+ env->GetMethodID(gnssCorrectionComponentClass, "getValidityInterval",
+ "()Landroid/location/GnssCorrectionComponent$GnssInterval;");
+
+ // Get the methods of PseudorangeCorrection class.
+ jclass pseudorangeCorrectionClass =
+ env->FindClass("android/location/GnssCorrectionComponent$PseudorangeCorrection");
+
+ method_pseudorangeCorrectionGetCorrectionMeters =
+ env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionMeters", "()D");
+ method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond =
+ env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionRateMetersPerSecond", "()D");
+ method_pseudorangeCorrectionGetCorrectionUncertaintyMeters =
+ env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionUncertaintyMeters", "()D");
+
+ // Get the methods of GnssInterval class.
+ jclass gnssIntervalClass =
+ env->FindClass("android/location/GnssCorrectionComponent$GnssInterval");
+
+ method_gnssIntervalGetStartMillisSinceGpsEpoch =
+ env->GetMethodID(gnssIntervalClass, "getStartMillisSinceGpsEpoch", "()J");
+ method_gnssIntervalGetEndMillisSinceGpsEpoch =
+ env->GetMethodID(gnssIntervalClass, "getEndMillisSinceGpsEpoch", "()J");
+
+ // Get the methods of GpsAssistance class.
+ jclass gpsAssistanceClass = env->FindClass("android/location/GpsAssistance");
+
+ method_gpsAssistanceGetAlmanac =
+ env->GetMethodID(gpsAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+ method_gpsAssistanceGetIonosphericModel =
+ env->GetMethodID(gpsAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_gpsAssistanceGetUtcModel =
+ env->GetMethodID(gpsAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+ method_gpsAssistanceGetLeapSecondsModel =
+ env->GetMethodID(gpsAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_gpsAssistanceGetTimeModels =
+ env->GetMethodID(gpsAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_gpsAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(gpsAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_gpsAssistanceGetRealTimeIntegrityModels =
+ env->GetMethodID(gpsAssistanceClass, "getRealTimeIntegrityModels",
+ "()Ljava/util/List;");
+ method_gpsAssistanceGetSatelliteCorrections =
+ env->GetMethodID(gpsAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+ // Get the methods of GpsSatelliteEphemeris class.
+ jclass gpsSatelliteEphemerisClass = env->FindClass("android/location/GpsSatelliteEphemeris");
+
+ method_gpsSatelliteEphemerisGetSvid =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSvid", "()I");
+ method_gpsSatelliteEphemerisGetGpsL2Params =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getGpsL2Params",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+ method_gpsSatelliteEphemerisGetSatelliteClockModel =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteClockModel",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteClockModel;");
+ method_gpsSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+ method_gpsSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+ method_gpsSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/SatelliteEphemerisTime;");
+
+ // Get the methods of GpsL2Params class.
+ jclass gpsL2ParamsClass = env->FindClass("android/location/GpsSatelliteEphemeris$GpsL2Params");
+ method_gpsL2ParamsGetL2Code = env->GetMethodID(gpsL2ParamsClass, "getL2Code", "()I");
+ method_gpsL2ParamsGetL2Flag = env->GetMethodID(gpsL2ParamsClass, "getL2Flag", "()I");
+
+ // Get the methods of GpsSatelliteClockModel class.
+ jclass gpsSatelliteClockModelClass =
+ env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteClockModel");
+ method_gpsSatelliteClockModelGetAf0 =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getAf0", "()D");
+ method_gpsSatelliteClockModelGetAf1 =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getAf1", "()D");
+ method_gpsSatelliteClockModelGetAf2 =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getAf2", "()D");
+ method_gpsSatelliteClockModelGetTgd =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getTgd", "()D");
+ method_gpsSatelliteClockModelGetIodc =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getIodc", "()I");
+ method_gpsSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(gpsSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of GpsSatelliteHealth class.
+ jclass gpsSatelliteHealthClass =
+ env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteHealth");
+ method_gpsSatelliteHealthGetFitInt =
+ env->GetMethodID(gpsSatelliteHealthClass, "getFitInt", "()D");
+ method_gpsSatelliteHealthGetSvAccur =
+ env->GetMethodID(gpsSatelliteHealthClass, "getSvAccur", "()D");
+ method_gpsSatelliteHealthGetSvHealth =
+ env->GetMethodID(gpsSatelliteHealthClass, "getSvHealth", "()I");
+
+ // Get the methods of BeidouAssistance class.
+ jclass beidouAssistanceClass = env->FindClass("android/location/BeidouAssistance");
+ method_beidouAssistanceGetAlmanac = env->GetMethodID(beidouAssistanceClass, "getAlmanac",
+ "()Landroid/location/GnssAlmanac;");
+ method_beidouAssistanceGetIonosphericModel =
+ env->GetMethodID(beidouAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_beidouAssistanceGetUtcModel =
+ env->GetMethodID(beidouAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+ method_beidouAssistanceGetLeapSecondsModel =
+ env->GetMethodID(beidouAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_beidouAssistanceGetTimeModels =
+ env->GetMethodID(beidouAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_beidouAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(beidouAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_beidouAssistanceGetSatelliteCorrections =
+ env->GetMethodID(beidouAssistanceClass, "getSatelliteCorrections",
+ "()Ljava/util/List;");
+ method_beidouAssistanceGetRealTimeIntegrityModels =
+ env->GetMethodID(beidouAssistanceClass, "getRealTimeIntegrityModels",
+ "()Ljava/util/List;");
+
+ // Get the methods of BeidouSatelliteEphemeris class.
+ jclass beidouSatelliteEphemerisClass =
+ env->FindClass("android/location/BeidouSatelliteEphemeris");
+ method_beidouSatelliteEphemerisGetSvid =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSvid", "()I");
+ method_beidouSatelliteEphemerisGetSatelliteClockModel =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteClockModel",
+ "()Landroid/location/"
+ "BeidouSatelliteEphemeris$BeidouSatelliteClockModel;");
+ method_beidouSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+ method_beidouSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth;");
+ method_beidouSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/"
+ "BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime;");
+
+ // Get the methods of BeidouSatelliteClockModel
+ jclass beidouSatelliteClockModelClass =
+ env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteClockModel");
+ method_beidouSatelliteClockModelGetAf0 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAf0", "()D");
+ method_beidouSatelliteClockModelGetAf1 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAf1", "()D");
+ method_beidouSatelliteClockModelGetAf2 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAf2", "()D");
+ method_beidouSatelliteClockModelGetAodc =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getAodc", "()I");
+ method_beidouSatelliteClockModelGetTgd1 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getTgd1", "()D");
+ method_beidouSatelliteClockModelGetTgd2 =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getTgd2", "()D");
+ method_beidouSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(beidouSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of BeidouSatelliteHealth
+ jclass beidouSatelliteHealthClass =
+ env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth");
+ method_beidouSatelliteHealthGetSatH1 =
+ env->GetMethodID(beidouSatelliteHealthClass, "getSatH1", "()I");
+ method_beidouSatelliteHealthGetSvAccur =
+ env->GetMethodID(beidouSatelliteHealthClass, "getSvAccur", "()D");
+
+ // Get the methods of BeidouSatelliteEphemerisTime
+ jclass beidouSatelliteEphemerisTimeClass = env->FindClass(
+ "android/location/BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime");
+ method_beidouSatelliteEphemerisTimeGetIode =
+ env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getIode", "()I");
+ method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber =
+ env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getBeidouWeekNumber", "()I");
+ method_beidouSatelliteEphemerisTimeGetToeSeconds =
+ env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getToeSeconds", "()I");
+
+ // Get the methods of GalileoAssistance class.
+ jclass galileoAssistanceClass = env->FindClass("android/location/GalileoAssistance");
+ method_galileoAssistanceGetAlmanac = env->GetMethodID(galileoAssistanceClass, "getAlmanac",
+ "()Landroid/location/GnssAlmanac;");
+ method_galileoAssistanceGetIonosphericModel =
+ env->GetMethodID(galileoAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_galileoAssistanceGetUtcModel = env->GetMethodID(galileoAssistanceClass, "getUtcModel",
+ "()Landroid/location/UtcModel;");
+ method_galileoAssistanceGetLeapSecondsModel =
+ env->GetMethodID(galileoAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_galileoAssistanceGetTimeModels =
+ env->GetMethodID(galileoAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_galileoAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(galileoAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_galileoAssistanceGetSatelliteCorrections =
+ env->GetMethodID(galileoAssistanceClass, "getSatelliteCorrections",
+ "()Ljava/util/List;");
+ method_galileoAssistanceGetRealTimeIntegrityModels =
+ env->GetMethodID(galileoAssistanceClass, "getRealTimeIntegrityModels",
+ "()Ljava/util/List;");
+
+ // Get the methods of GalileoSatelliteEphemeris class
+ jclass galileoSatelliteEphemerisClass =
+ env->FindClass("android/location/GalileoSatelliteEphemeris");
+ method_galileoSatelliteEphemerisGetSatelliteClockModels =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteClockModels",
+ "()Ljava/util/List;");
+ method_galileoSatelliteEphemerisGetSvid =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSvid", "()I");
+ method_galileoSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/SatelliteEphemerisTime;");
+ method_galileoSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/GalileoSatelliteEphemeris$GalileoSvHealth;");
+ method_galileoSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+
+ // Get the methods of GalileoSatelliteClockModel class.
+ jclass galileoSatelliteClockModelClass =
+ env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSatelliteClockModel");
+ method_galileoSatelliteClockModelGetAf0 =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getAf0", "()D");
+ method_galileoSatelliteClockModelGetAf1 =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getAf1", "()D");
+ method_galileoSatelliteClockModelGetAf2 =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getAf2", "()D");
+ method_galileoSatelliteClockModelGetBgdSeconds =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getBgdSeconds", "()D");
+ method_galileoSatelliteClockModelGetSatelliteClockType =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getSatelliteClockType", "()I");
+ method_galileoSatelliteClockModelGetSisaMeters =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getSisaMeters", "()D");
+ method_galileoSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(galileoSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of GalileoSvHealth class.
+ jclass galileoSvHealthClass =
+ env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSvHealth");
+ method_galileoSvHealthGetDataValidityStatusE1b =
+ env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE1b", "()I");
+ method_galileoSvHealthGetDataValidityStatusE5a =
+ env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5a", "()I");
+ method_galileoSvHealthGetDataValidityStatusE5b =
+ env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5b", "()I");
+ method_galileoSvHealthGetSignalHealthStatusE1b =
+ env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE1b", "()I");
+ method_galileoSvHealthGetSignalHealthStatusE5a =
+ env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5a", "()I");
+ method_galileoSvHealthGetSignalHealthStatusE5b =
+ env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5b", "()I");
+
+ // Get the methods of GalileoIonosphericModel class.
+ jclass galileoIonosphericModelClass =
+ env->FindClass("android/location/GalileoIonosphericModel");
+ method_galileoIonosphericModelGetAi0 =
+ env->GetMethodID(galileoIonosphericModelClass, "getAi0", "()D");
+ method_galileoIonosphericModelGetAi1 =
+ env->GetMethodID(galileoIonosphericModelClass, "getAi1", "()D");
+ method_galileoIonosphericModelGetAi2 =
+ env->GetMethodID(galileoIonosphericModelClass, "getAi2", "()D");
+
+ // Get the methods of GlonassAssistance class.
+ jclass glonassAssistanceClass = env->FindClass("android/location/GlonassAssistance");
+ method_glonassAssistanceGetAlmanac = env->GetMethodID(glonassAssistanceClass, "getAlmanac",
+ "()Landroid/location/GlonassAlmanac;");
+ method_glonassAssistanceGetUtcModel = env->GetMethodID(glonassAssistanceClass, "getUtcModel",
+ "()Landroid/location/UtcModel;");
+ method_glonassAssistanceGetTimeModels =
+ env->GetMethodID(glonassAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_glonassAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(glonassAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_glonassAssistanceGetSatelliteCorrections =
+ env->GetMethodID(glonassAssistanceClass, "getSatelliteCorrections",
+ "()Ljava/util/List;");
+
+ // Get the methods of GlonassAlmanac class.
+ jclass glonassAlmanacClass = env->FindClass("android/location/GlonassAlmanac");
+ method_glonassAlmanacGetIssueDateMillis =
+ env->GetMethodID(glonassAlmanacClass, "getIssueDateMillis", "()J");
+ method_glonassAlmanacGetSatelliteAlmanacs =
+ env->GetMethodID(glonassAlmanacClass, "getSatelliteAlmanacs", "()Ljava/util/List;");
+
+ // Get the methods of GlonassSatelliteAlmanac class
+ jclass glonassSatelliteAlmanacClass =
+ env->FindClass("android/location/GlonassAlmanac$GlonassSatelliteAlmanac");
+ method_glonassSatelliteAlmanacGetDeltaI =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaI", "()D");
+ method_glonassSatelliteAlmanacGetDeltaT =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaT", "()D");
+ method_glonassSatelliteAlmanacGetDeltaTDot =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaTDot", "()D");
+ method_glonassSatelliteAlmanacGetEccentricity =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getEccentricity", "()D");
+ method_glonassSatelliteAlmanacGetFrequencyChannelNumber =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getFrequencyChannelNumber", "()I");
+ method_glonassSatelliteAlmanacGetLambda =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getLambda", "()D");
+ method_glonassSatelliteAlmanacGetOmega =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getOmega", "()D");
+ method_glonassSatelliteAlmanacGetSlotNumber =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getSlotNumber", "()I");
+ method_glonassSatelliteAlmanacGetHealthState =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getHealthState", "()I");
+ method_glonassSatelliteAlmanacGetTLambda =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getTLambda", "()D");
+ method_glonassSatelliteAlmanacGetTau =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getTau", "()D");
+ method_glonassSatelliteAlmanacGetCalendarDayNumber =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "getCalendarDayNumber", "()I");
+ method_glonassSatelliteAlmanacGetIsGlonassM =
+ env->GetMethodID(glonassSatelliteAlmanacClass, "isGlonassM", "()Z");
+
+ // Get the methods of GlonassSatelliteEphemeris
+ jclass glonassSatelliteEphemerisClass =
+ env->FindClass("android/location/GlonassSatelliteEphemeris");
+ method_glonassSatelliteEphemerisGetAgeInDays =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getAgeInDays", "()I");
+ method_glonassSatelliteEphemerisGetFrameTimeSeconds =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getFrameTimeSeconds", "()D");
+ method_glonassSatelliteEphemerisGetHealthState =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getHealthState", "()I");
+ method_glonassSatelliteEphemerisGetSlotNumber =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getSlotNumber", "()I");
+ method_glonassSatelliteEphemerisGetSatelliteClockModel =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteClockModel",
+ "()Landroid/location/"
+ "GlonassSatelliteEphemeris$GlonassSatelliteClockModel;");
+ method_glonassSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/"
+ "GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel;");
+ method_glonassSatelliteEphemerisGetUpdateIntervalMinutes =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "getUpdateIntervalMinutes", "()I");
+ method_glonassSatelliteEphemerisGetIsGlonassM =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "isGlonassM", "()Z");
+ method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd =
+ env->GetMethodID(glonassSatelliteEphemerisClass, "isUpdateIntervalOdd", "()Z");
+
+ // Get the methods of GlonassSatelliteOrbitModel
+ jclass glonassSatelliteOrbitModelClass =
+ env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel");
+ method_glonassSatelliteOrbitModelGetX =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getX", "()D");
+ method_glonassSatelliteOrbitModelGetXAccel =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getXAccel", "()D");
+ method_glonassSatelliteOrbitModelGetXDot =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getXDot", "()D");
+ method_glonassSatelliteOrbitModelGetY =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getY", "()D");
+ method_glonassSatelliteOrbitModelGetYAccel =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getYAccel", "()D");
+ method_glonassSatelliteOrbitModelGetYDot =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getYDot", "()D");
+ method_glonassSatelliteOrbitModelGetZ =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getZ", "()D");
+ method_glonassSatelliteOrbitModelGetZAccel =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getZAccel", "()D");
+ method_glonassSatelliteOrbitModelGetZDot =
+ env->GetMethodID(glonassSatelliteOrbitModelClass, "getZDot", "()D");
+
+ // Get the methods of GlonassSatelliteClockModel
+ jclass glonassSatelliteClockModelClass =
+ env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteClockModel");
+ method_glonassSatelliteClockModelGetClockBias =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getClockBias", "()D");
+ method_glonassSatelliteClockModelGetFrequencyBias =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyBias", "()D");
+ method_glonassSatelliteClockModelGetFrequencyChannelNumber =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyChannelNumber", "()I");
+ method_glonassSatelliteClockModelGetTimeOfClockSeconds =
+ env->GetMethodID(glonassSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+ // Get the methods of QzssAssistance class.
+ jclass qzssAssistanceClass = env->FindClass("android/location/QzssAssistance");
+ method_qzssAssistanceGetAlmanac =
+ env->GetMethodID(qzssAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+ method_qzssAssistanceGetIonosphericModel =
+ env->GetMethodID(qzssAssistanceClass, "getIonosphericModel",
+ "()Landroid/location/KlobucharIonosphericModel;");
+ method_qzssAssistanceGetUtcModel =
+ env->GetMethodID(qzssAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+ method_qzssAssistanceGetLeapSecondsModel =
+ env->GetMethodID(qzssAssistanceClass, "getLeapSecondsModel",
+ "()Landroid/location/LeapSecondsModel;");
+ method_qzssAssistanceGetTimeModels =
+ env->GetMethodID(qzssAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+ method_qzssAssistanceGetSatelliteEphemeris =
+ env->GetMethodID(qzssAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+ method_qzssAssistanceGetSatelliteCorrections =
+ env->GetMethodID(qzssAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+ // Get the methods of QzssSatelliteEphemeris class.
+ jclass qzssSatelliteEphemerisClass = env->FindClass("android/location/QzssSatelliteEphemeris");
+ method_qzssSatelliteEphemerisGetSvid =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSvid", "()I");
+ method_qzssSatelliteEphemerisGetGpsL2Params =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getGpsL2Params",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+ method_qzssSatelliteEphemerisGetSatelliteEphemerisTime =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+ "()Landroid/location/SatelliteEphemerisTime;");
+ method_qzssSatelliteEphemerisGetSatelliteHealth =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteHealth",
+ "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+ method_qzssSatelliteEphemerisGetSatelliteOrbitModel =
+ env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteOrbitModel",
+ "()Landroid/location/KeplerianOrbitModel;");
+}
+
+GnssAssistanceInterface::GnssAssistanceInterface(
+ const sp<IGnssAssistanceInterface>& iGnssAssistance)
+ : mGnssAssistanceInterface(iGnssAssistance) {
+ assert(mGnssAssistanceInterface != nullptr);
+}
+
+jboolean GnssAssistanceInterface::injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj) {
+ GnssAssistance gnssAssistance;
+ GnssAssistanceUtil::setGnssAssistance(env, gnssAssistanceObj, gnssAssistance);
+ auto status = mGnssAssistanceInterface->injectGnssAssistance(gnssAssistance);
+ return checkAidlStatus(status, "IGnssAssistanceInterface injectGnssAssistance() failed.");
+}
+
+jboolean GnssAssistanceInterface::setCallback(const sp<IGnssAssistanceCallback>& callback) {
+ auto status = mGnssAssistanceInterface->setCallback(callback);
+ return checkAidlStatus(status, "IGnssAssistanceInterface setCallback() failed.");
+}
+
+void GnssAssistanceUtil::setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+ GnssAssistance& gnssAssistance) {
+ jobject gpsAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGpsAssistance);
+ jobject glonassAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGlonassAssistance);
+ jobject qzssAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetQzssAssistance);
+ jobject galileoAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGalileoAssistance);
+ jobject beidouAssistanceObj =
+ env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetBeidouAssistance);
+ GnssAssistanceUtil::setGpsAssistance(env, gpsAssistanceObj, gnssAssistance.gpsAssistance);
+ GnssAssistanceUtil::setGlonassAssistance(env, glonassAssistanceObj,
+ gnssAssistance.glonassAssistance);
+ GnssAssistanceUtil::setQzssAssistance(env, qzssAssistanceObj, gnssAssistance.qzssAssistance);
+ GnssAssistanceUtil::setGalileoAssistance(env, galileoAssistanceObj,
+ gnssAssistance.galileoAssistance);
+ GnssAssistanceUtil::setBeidouAssistance(env, beidouAssistanceObj,
+ gnssAssistance.beidouAssistance);
+ env->DeleteLocalRef(gpsAssistanceObj);
+ env->DeleteLocalRef(glonassAssistanceObj);
+ env->DeleteLocalRef(qzssAssistanceObj);
+ env->DeleteLocalRef(galileoAssistanceObj);
+ env->DeleteLocalRef(beidouAssistanceObj);
+}
+
+void GnssAssistanceUtil::setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+ QzssAssistance& qzssAssistance) {
+ jobject qzssAlmanacObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetAlmanac);
+ jobject qzssIonosphericModelObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetIonosphericModel);
+ jobject qzssUtcModelObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetUtcModel);
+ jobject qzssLeapSecondsModelObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetLeapSecondsModel);
+ jobject qzssTimeModelsObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetTimeModels);
+ jobject qzssSatelliteEphemerisObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteEphemeris);
+ jobject qzssSatelliteCorrectionsObj =
+ env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteCorrections);
+ setGnssAlmanac(env, qzssAlmanacObj, qzssAssistance.almanac);
+ setKlobucharIonosphericModel(env, qzssIonosphericModelObj, qzssAssistance.ionosphericModel);
+ setUtcModel(env, qzssUtcModelObj, qzssAssistance.utcModel);
+ setLeapSecondsModel(env, qzssLeapSecondsModelObj, qzssAssistance.leapSecondsModel);
+ setTimeModels(env, qzssTimeModelsObj, qzssAssistance.timeModels);
+ setGpsOrQzssSatelliteEphemeris<QzssSatelliteEphemeris>(env, qzssSatelliteEphemerisObj,
+ qzssAssistance.satelliteEphemeris);
+ setSatelliteCorrections(env, qzssSatelliteCorrectionsObj, qzssAssistance.satelliteCorrections);
+ env->DeleteLocalRef(qzssAlmanacObj);
+ env->DeleteLocalRef(qzssIonosphericModelObj);
+ env->DeleteLocalRef(qzssUtcModelObj);
+ env->DeleteLocalRef(qzssLeapSecondsModelObj);
+ env->DeleteLocalRef(qzssTimeModelsObj);
+ env->DeleteLocalRef(qzssSatelliteEphemerisObj);
+ env->DeleteLocalRef(qzssSatelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+ GlonassAssistance& galileoAssistance) {
+ jobject glonassAlmanacObj =
+ env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetAlmanac);
+ jobject utcModelObj =
+ env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetUtcModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(glonassAssistanceObj,
+ method_glonassAssistanceGetSatelliteEphemeris);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(glonassAssistanceObj,
+ method_glonassAssistanceGetSatelliteCorrections);
+ setGlonassAlmanac(env, glonassAlmanacObj, galileoAssistance.almanac);
+ setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+ setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+ setGlonassSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+ env->DeleteLocalRef(glonassAlmanacObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+ GlonassAlmanac& glonassAlmanac) {
+ if (glonassAlmanacObj == nullptr) {
+ glonassAlmanac.issueDateMs = -1;
+ return;
+ }
+ jlong issueDateMillis =
+ env->CallLongMethod(glonassAlmanacObj, method_glonassAlmanacGetIssueDateMillis);
+ glonassAlmanac.issueDateMs = issueDateMillis;
+ jobject satelliteAlmanacsObj =
+ env->CallObjectMethod(glonassAlmanacObj, method_glonassAlmanacGetSatelliteAlmanacs);
+ if (satelliteAlmanacsObj == nullptr) return;
+ auto len = env->CallIntMethod(satelliteAlmanacsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject glonassSatelliteAlmanacObj =
+ env->CallObjectMethod(satelliteAlmanacsObj, method_listGet, i);
+ if (glonassSatelliteAlmanacObj == nullptr) continue;
+ GlonassSatelliteAlmanac glonassSatelliteAlmanac;
+ jdouble deltaI = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetDeltaI);
+ glonassSatelliteAlmanac.deltaI = deltaI;
+ jdouble deltaT = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetDeltaT);
+ glonassSatelliteAlmanac.deltaT = deltaT;
+ jdouble deltaTDot = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetDeltaTDot);
+ glonassSatelliteAlmanac.deltaTDot = deltaTDot;
+ jdouble eccentricity = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetEccentricity);
+ glonassSatelliteAlmanac.eccentricity = eccentricity;
+ jint frequencyChannelNumber =
+ env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetFrequencyChannelNumber);
+ glonassSatelliteAlmanac.frequencyChannelNumber =
+ static_cast<int32_t>(frequencyChannelNumber);
+ jdouble lambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetLambda);
+ glonassSatelliteAlmanac.lambda = lambda;
+ jdouble omega = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetOmega);
+ glonassSatelliteAlmanac.omega = omega;
+ jint slotNumber = env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetSlotNumber);
+ glonassSatelliteAlmanac.slotNumber = static_cast<int32_t>(slotNumber);
+ jint healthState = env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetHealthState);
+ glonassSatelliteAlmanac.svHealth = static_cast<int32_t>(healthState);
+ jdouble tLambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetTLambda);
+ glonassSatelliteAlmanac.tLambda = tLambda;
+ jdouble tau = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetTau);
+ glonassSatelliteAlmanac.tau = tau;
+ jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetIsGlonassM);
+ glonassSatelliteAlmanac.isGlonassM = isGlonassM;
+ jint calendarDayNumber =
+ env->CallIntMethod(glonassSatelliteAlmanacObj,
+ method_glonassSatelliteAlmanacGetCalendarDayNumber);
+ glonassSatelliteAlmanac.calendarDayNumber = static_cast<int32_t>(calendarDayNumber);
+ glonassAlmanac.satelliteAlmanacs.push_back(glonassSatelliteAlmanac);
+ env->DeleteLocalRef(glonassSatelliteAlmanacObj);
+ }
+ env->DeleteLocalRef(satelliteAlmanacsObj);
+}
+
+void GnssAssistanceUtil::setGlonassSatelliteEphemeris(
+ JNIEnv* env, jobject glonassSatelliteEphemerisListObj,
+ std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList) {
+ if (glonassSatelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(glonassSatelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject glonassSatelliteEphemerisObj =
+ env->CallObjectMethod(glonassSatelliteEphemerisListObj, method_listGet, i);
+ if (glonassSatelliteEphemerisObj == nullptr) continue;
+ GlonassSatelliteEphemeris glonassSatelliteEphemeris;
+ jdouble ageInDays = env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetAgeInDays);
+ glonassSatelliteEphemeris.ageInDays = ageInDays;
+
+ // Set the GlonassSatelliteClockModel.
+ jobject glonassSatelliteClockModelObj =
+ env->CallObjectMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetSatelliteClockModel);
+ GlonassSatelliteClockModel glonassSatelliteClockModel;
+ jdouble clockBias = env->CallDoubleMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetClockBias);
+ glonassSatelliteClockModel.clockBias = clockBias;
+ jdouble frequencyBias =
+ env->CallDoubleMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetFrequencyBias);
+ glonassSatelliteClockModel.frequencyBias = frequencyBias;
+ jint frequencyChannelNumber =
+ env->CallIntMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetFrequencyChannelNumber);
+ glonassSatelliteClockModel.frequencyChannelNumber =
+ static_cast<int32_t>(frequencyChannelNumber);
+ jdouble timeOfClockSeconds =
+ env->CallDoubleMethod(glonassSatelliteClockModelObj,
+ method_glonassSatelliteClockModelGetTimeOfClockSeconds);
+ glonassSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ glonassSatelliteEphemeris.satelliteClockModel = glonassSatelliteClockModel;
+ env->DeleteLocalRef(glonassSatelliteClockModelObj);
+
+ // Set the GlonassSatelliteOrbitModel.
+ jobject glonassSatelliteOrbitModelObj =
+ env->CallObjectMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetSatelliteOrbitModel);
+ GlonassSatelliteOrbitModel glonassSatelliteOrbitModel;
+ jdouble x = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetX);
+ glonassSatelliteOrbitModel.x = x;
+ jdouble y = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetY);
+ glonassSatelliteOrbitModel.y = y;
+ jdouble z = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetZ);
+ glonassSatelliteOrbitModel.z = z;
+ jdouble xAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetXAccel);
+ glonassSatelliteOrbitModel.xAccel = xAccel;
+ jdouble yAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetYAccel);
+ glonassSatelliteOrbitModel.yAccel = yAccel;
+ jdouble zAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetZAccel);
+ glonassSatelliteOrbitModel.zAccel = zAccel;
+ jdouble xDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetXDot);
+ glonassSatelliteOrbitModel.xDot = xDot;
+ jdouble yDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetYDot);
+ glonassSatelliteOrbitModel.yDot = yDot;
+ jdouble zDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+ method_glonassSatelliteOrbitModelGetZDot);
+ glonassSatelliteOrbitModel.zDot = zDot;
+ glonassSatelliteEphemeris.satelliteOrbitModel = glonassSatelliteOrbitModel;
+ env->DeleteLocalRef(glonassSatelliteOrbitModelObj);
+
+ jint healthState = env->CallIntMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetHealthState);
+ glonassSatelliteEphemeris.svHealth = static_cast<int32_t>(healthState);
+ jint slotNumber = env->CallIntMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetSlotNumber);
+ glonassSatelliteEphemeris.slotNumber = static_cast<int32_t>(slotNumber);
+ jdouble frameTimeSeconds =
+ env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetFrameTimeSeconds);
+ glonassSatelliteEphemeris.frameTimeSeconds = frameTimeSeconds;
+ jint updateIntervalMinutes =
+ env->CallIntMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetUpdateIntervalMinutes);
+ glonassSatelliteEphemeris.updateIntervalMinutes =
+ static_cast<int32_t>(updateIntervalMinutes);
+ jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetIsGlonassM);
+ glonassSatelliteEphemeris.isGlonassM = isGlonassM;
+ jboolean isUpdateIntervalOdd =
+ env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+ method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd);
+ glonassSatelliteEphemeris.isOddUpdateInterval = isUpdateIntervalOdd;
+ glonassSatelliteEphemerisList.push_back(glonassSatelliteEphemeris);
+ env->DeleteLocalRef(glonassSatelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+ GalileoAssistance& galileoAssistance) {
+ jobject galileoAlmanacObj =
+ env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetAlmanac);
+ jobject ionosphericModelObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetIonosphericModel);
+ jobject utcModelObj =
+ env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetUtcModel);
+ jobject leapSecondsModelObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetLeapSecondsModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetSatelliteEphemeris);
+ jobject realTimeIntegrityModelsObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetRealTimeIntegrityModels);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(galileoAssistanceObj,
+ method_galileoAssistanceGetSatelliteCorrections);
+ setGnssAlmanac(env, galileoAlmanacObj, galileoAssistance.almanac);
+ setGaliloKlobucharIonosphericModel(env, ionosphericModelObj,
+ galileoAssistance.ionosphericModel);
+ setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+ setLeapSecondsModel(env, leapSecondsModelObj, galileoAssistance.leapSecondsModel);
+ setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+ setGalileoSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+ setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+ galileoAssistance.realTimeIntegrityModels);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+ env->DeleteLocalRef(galileoAlmanacObj);
+ env->DeleteLocalRef(ionosphericModelObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(leapSecondsModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(realTimeIntegrityModelsObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGaliloKlobucharIonosphericModel(
+ JNIEnv* env, jobject galileoIonosphericModelObj,
+ GalileoIonosphericModel& ionosphericModel) {
+ if (galileoIonosphericModelObj == nullptr) return;
+ jdouble ai0 =
+ env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi0);
+ ionosphericModel.ai0 = ai0;
+ jdouble ai1 =
+ env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi1);
+ ionosphericModel.ai1 = ai1;
+ jdouble ai2 =
+ env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi2);
+ ionosphericModel.ai2 = ai2;
+}
+
+void GnssAssistanceUtil::setGalileoSatelliteEphemeris(
+ JNIEnv* env, jobject galileoSatelliteEphemerisListObj,
+ std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList) {
+ if (galileoSatelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(galileoSatelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject galileoSatelliteEphemerisObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisListObj, method_listGet, i);
+ GalileoSatelliteEphemeris galileoSatelliteEphemeris;
+ GalileoSvHealth galileoSvHealth;
+ // Set the svid of the satellite.
+ jint svid = env->CallLongMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSvid);
+ galileoSatelliteEphemeris.svid = svid;
+
+ // Set the satellite clock models.
+ jobject galileoSatelliteClockModelListObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteClockModels);
+ auto size = env->CallIntMethod(galileoSatelliteClockModelListObj, method_listSize);
+ for (uint16_t j = 0; j < size; ++j) {
+ jobject galileoSatelliteClockModelObj =
+ env->CallObjectMethod(galileoSatelliteClockModelListObj, method_listGet, j);
+ if (galileoSatelliteClockModelObj == nullptr) continue;
+ GalileoSatelliteClockModel galileoSatelliteClockModel;
+ jdouble af0 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetAf0);
+ galileoSatelliteClockModel.af0 = af0;
+ jdouble af1 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetAf1);
+ galileoSatelliteClockModel.af1 = af1;
+ jdouble af2 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetAf2);
+ galileoSatelliteClockModel.af2 = af2;
+ jdouble bgdSeconds =
+ env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetBgdSeconds);
+ galileoSatelliteClockModel.bgdSeconds = bgdSeconds;
+ jint satelliteClockType =
+ env->CallIntMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetSatelliteClockType);
+ galileoSatelliteClockModel.satelliteClockType =
+ static_cast<GalileoSatelliteClockModel::SatelliteClockType>(satelliteClockType);
+ jdouble sisaMeters =
+ env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetSisaMeters);
+ galileoSatelliteClockModel.sisaMeters = sisaMeters;
+ jdouble timeOfClockSeconds =
+ env->CallDoubleMethod(galileoSatelliteClockModelObj,
+ method_galileoSatelliteClockModelGetTimeOfClockSeconds);
+ galileoSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ galileoSatelliteEphemeris.satelliteClockModel.push_back(galileoSatelliteClockModel);
+ env->DeleteLocalRef(galileoSatelliteClockModelObj);
+ }
+ env->DeleteLocalRef(galileoSatelliteClockModelListObj);
+
+ // Set the satelliteOrbitModel of the satellite.
+ jobject satelliteOrbitModelObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteOrbitModel);
+ GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+ galileoSatelliteEphemeris.satelliteOrbitModel);
+ env->DeleteLocalRef(satelliteOrbitModelObj);
+
+ // Set the satellite health of the satellite clock model.
+ jobject galileoSvHealthObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteHealth);
+ jint dataValidityStatusE1b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetDataValidityStatusE1b);
+ galileoSvHealth.dataValidityStatusE1b =
+ static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE1b);
+ jint dataValidityStatusE5a =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetDataValidityStatusE5a);
+ galileoSvHealth.dataValidityStatusE5a =
+ static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5a);
+ jint dataValidityStatusE5b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetDataValidityStatusE5b);
+ galileoSvHealth.dataValidityStatusE5b =
+ static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5b);
+ jint signalHealthStatusE1b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetSignalHealthStatusE1b);
+ galileoSvHealth.signalHealthStatusE1b =
+ static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE1b);
+ jint signalHealthStatusE5a =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetSignalHealthStatusE5a);
+ galileoSvHealth.signalHealthStatusE5a =
+ static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5a);
+ jint signalHealthStatusE5b =
+ env->CallIntMethod(galileoSvHealthObj,
+ method_galileoSvHealthGetSignalHealthStatusE5b);
+ galileoSvHealth.signalHealthStatusE5b =
+ static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5b);
+ galileoSatelliteEphemeris.svHealth = galileoSvHealth;
+ env->DeleteLocalRef(galileoSvHealthObj);
+
+ // Set the satelliteEphemerisTime of the satellite.
+ jobject satelliteEphemerisTimeObj =
+ env->CallObjectMethod(galileoSatelliteEphemerisObj,
+ method_galileoSatelliteEphemerisGetSatelliteEphemerisTime);
+ GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+ galileoSatelliteEphemeris
+ .satelliteEphemerisTime);
+ env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+ galileoSatelliteEphemerisList.push_back(galileoSatelliteEphemeris);
+ env->DeleteLocalRef(galileoSatelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+ BeidouAssistance& beidouAssistance) {
+ jobject beidouAlmanacObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetAlmanac);
+ jobject ionosphericModelObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetIonosphericModel);
+ jobject utcModelObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetUtcModel);
+ jobject leapSecondsModelObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetLeapSecondsModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(beidouAssistanceObj,
+ method_beidouAssistanceGetSatelliteEphemeris);
+ jobject realTimeIntegrityModelsObj =
+ env->CallObjectMethod(beidouAssistanceObj,
+ method_beidouAssistanceGetRealTimeIntegrityModels);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(beidouAssistanceObj,
+ method_beidouAssistanceGetSatelliteCorrections);
+ setGnssAlmanac(env, beidouAlmanacObj, beidouAssistance.almanac);
+ setKlobucharIonosphericModel(env, ionosphericModelObj, beidouAssistance.ionosphericModel);
+ setUtcModel(env, utcModelObj, beidouAssistance.utcModel);
+ setLeapSecondsModel(env, leapSecondsModelObj, beidouAssistance.leapSecondsModel);
+ setTimeModels(env, timeModelsObj, beidouAssistance.timeModels);
+ setBeidouSatelliteEphemeris(env, satelliteEphemerisObj, beidouAssistance.satelliteEphemeris);
+ setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+ beidouAssistance.realTimeIntegrityModels);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, beidouAssistance.satelliteCorrections);
+ env->DeleteLocalRef(beidouAlmanacObj);
+ env->DeleteLocalRef(ionosphericModelObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(leapSecondsModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(realTimeIntegrityModelsObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setBeidouSatelliteEphemeris(
+ JNIEnv* env, jobject beidouSatelliteEphemerisListObj,
+ std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList) {
+ if (beidouSatelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(beidouSatelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject beidouSatelliteEphemerisObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisListObj, method_listGet, i);
+ if (beidouSatelliteEphemerisObj == nullptr) continue;
+ BeidouSatelliteEphemeris beidouSatelliteEphemeris;
+
+ // Set the svid of the satellite.
+ jint svid = env->CallIntMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSvid);
+ beidouSatelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+ // Set the satelliteClockModel of the satellite.
+ jobject satelliteClockModelObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteClockModel);
+ jdouble af0 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAf0);
+ jdouble af1 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAf1);
+ jdouble af2 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAf2);
+ jdouble tgd1 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetTgd1);
+ jdouble tgd2 = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetTgd2);
+ jdouble aodc = env->CallDoubleMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetAodc);
+ jlong timeOfClockSeconds =
+ env->CallLongMethod(satelliteClockModelObj,
+ method_beidouSatelliteClockModelGetTimeOfClockSeconds);
+ beidouSatelliteEphemeris.satelliteClockModel.af0 = af0;
+ beidouSatelliteEphemeris.satelliteClockModel.af1 = af1;
+ beidouSatelliteEphemeris.satelliteClockModel.af2 = af2;
+ beidouSatelliteEphemeris.satelliteClockModel.tgd1 = tgd1;
+ beidouSatelliteEphemeris.satelliteClockModel.tgd2 = tgd2;
+ beidouSatelliteEphemeris.satelliteClockModel.aodc = aodc;
+ beidouSatelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ env->DeleteLocalRef(satelliteClockModelObj);
+
+ // Set the satelliteOrbitModel of the satellite.
+ jobject satelliteOrbitModelObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteOrbitModel);
+ GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+ beidouSatelliteEphemeris.satelliteOrbitModel);
+ env->DeleteLocalRef(satelliteOrbitModelObj);
+
+ // Set the satelliteHealth of the satellite.
+ jobject satelliteHealthObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteHealth);
+ jint satH1 = env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSatH1);
+ jint svAccur =
+ env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSvAccur);
+ beidouSatelliteEphemeris.satelliteHealth.satH1 = static_cast<int32_t>(satH1);
+ beidouSatelliteEphemeris.satelliteHealth.svAccur = static_cast<int32_t>(svAccur);
+ env->DeleteLocalRef(satelliteHealthObj);
+
+ // Set the satelliteEphemerisTime of the satellite.
+ jobject satelliteEphemerisTimeObj =
+ env->CallObjectMethod(beidouSatelliteEphemerisObj,
+ method_beidouSatelliteEphemerisGetSatelliteEphemerisTime);
+ jint iode = env->CallIntMethod(satelliteEphemerisTimeObj,
+ method_beidouSatelliteEphemerisTimeGetIode);
+ jint beidouWeekNumber =
+ env->CallIntMethod(satelliteEphemerisTimeObj,
+ method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber);
+ jint toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+ method_beidouSatelliteEphemerisTimeGetToeSeconds);
+ beidouSatelliteEphemeris.satelliteEphemerisTime.aode = static_cast<int32_t>(iode);
+ beidouSatelliteEphemeris.satelliteEphemerisTime.weekNumber =
+ static_cast<int32_t>(beidouWeekNumber);
+ beidouSatelliteEphemeris.satelliteEphemerisTime.toeSeconds =
+ static_cast<int32_t>(toeSeconds);
+ env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+ beidouSatelliteEphemerisList.push_back(beidouSatelliteEphemeris);
+ env->DeleteLocalRef(beidouSatelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+ GpsAssistance& gpsAssistance) {
+ jobject gnssAlmanacObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetAlmanac);
+ jobject ionosphericModelObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetIonosphericModel);
+ jobject utcModelObj = env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetUtcModel);
+ jobject leapSecondsModelObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetLeapSecondsModel);
+ jobject timeModelsObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetTimeModels);
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteEphemeris);
+ jobject realTimeIntegrityModelsObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetRealTimeIntegrityModels);
+ jobject satelliteCorrectionsObj =
+ env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteCorrections);
+
+ setGnssAlmanac(env, gnssAlmanacObj, gpsAssistance.almanac);
+ setKlobucharIonosphericModel(env, ionosphericModelObj, gpsAssistance.ionosphericModel);
+ setUtcModel(env, utcModelObj, gpsAssistance.utcModel);
+ setLeapSecondsModel(env, leapSecondsModelObj, gpsAssistance.leapSecondsModel);
+ setTimeModels(env, timeModelsObj, gpsAssistance.timeModels);
+ setGpsOrQzssSatelliteEphemeris<GpsSatelliteEphemeris>(env, satelliteEphemerisObj,
+ gpsAssistance.satelliteEphemeris);
+ setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+ gpsAssistance.realTimeIntegrityModels);
+ setSatelliteCorrections(env, satelliteCorrectionsObj, gpsAssistance.satelliteCorrections);
+ env->DeleteLocalRef(gnssAlmanacObj);
+ env->DeleteLocalRef(ionosphericModelObj);
+ env->DeleteLocalRef(utcModelObj);
+ env->DeleteLocalRef(leapSecondsModelObj);
+ env->DeleteLocalRef(timeModelsObj);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ env->DeleteLocalRef(realTimeIntegrityModelsObj);
+ env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+/** Set the GPS/QZSS satellite ephemeris list. */
+template <class T>
+void GnssAssistanceUtil::setGpsOrQzssSatelliteEphemeris(JNIEnv* env,
+ jobject satelliteEphemerisListObj,
+ std::vector<T>& satelliteEphemerisList) {
+ if (satelliteEphemerisListObj == nullptr) return;
+ auto len = env->CallIntMethod(satelliteEphemerisListObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject satelliteEphemerisObj =
+ env->CallObjectMethod(satelliteEphemerisListObj, method_listGet, i);
+ if (satelliteEphemerisObj == nullptr) continue;
+ T satelliteEphemeris;
+ // Set the svid of the satellite.
+ jint svid = env->CallIntMethod(satelliteEphemerisObj, method_gpsSatelliteEphemerisGetSvid);
+ satelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+ // Set the gpsL2Params of the satellite.
+ jobject gpsL2ParamsObj = env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetGpsL2Params);
+ jint l2Code = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Code);
+ jint l2Flag = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Flag);
+ satelliteEphemeris.gpsL2Params.l2Code = static_cast<int32_t>(l2Code);
+ satelliteEphemeris.gpsL2Params.l2Flag = static_cast<int32_t>(l2Flag);
+ env->DeleteLocalRef(gpsL2ParamsObj);
+
+ // Set the satelliteClockModel of the satellite.
+ jobject satelliteClockModelObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteClockModel);
+ jdouble af0 =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf0);
+ jdouble af1 =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf1);
+ jdouble af2 =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf2);
+ jdouble tgd =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetTgd);
+ jint iodc =
+ env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetIodc);
+ jlong timeOfClockSeconds =
+ env->CallLongMethod(satelliteClockModelObj,
+ method_gpsSatelliteClockModelGetTimeOfClockSeconds);
+ satelliteEphemeris.satelliteClockModel.af0 = af0;
+ satelliteEphemeris.satelliteClockModel.af1 = af1;
+ satelliteEphemeris.satelliteClockModel.af2 = af2;
+ satelliteEphemeris.satelliteClockModel.tgd = tgd;
+ satelliteEphemeris.satelliteClockModel.iodc = static_cast<int32_t>(iodc);
+ satelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+ env->DeleteLocalRef(satelliteClockModelObj);
+
+ // Set the satelliteOrbitModel of the satellite.
+ jobject satelliteOrbitModelObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteOrbitModel);
+ GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+ satelliteEphemeris.satelliteOrbitModel);
+ env->DeleteLocalRef(satelliteOrbitModelObj);
+
+ // Set the satelliteHealth of the satellite.
+ jobject satelliteHealthObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteHealth);
+ jint svHealth =
+ env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvHealth);
+ jdouble svAccur =
+ env->CallDoubleMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvAccur);
+ jdouble fitInt = env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetFitInt);
+ satelliteEphemeris.satelliteHealth.svHealth = static_cast<int32_t>(svHealth);
+ satelliteEphemeris.satelliteHealth.svAccur = svAccur;
+ satelliteEphemeris.satelliteHealth.fitInt = fitInt;
+ env->DeleteLocalRef(satelliteHealthObj);
+
+ // Set the satelliteEphemerisTime of the satellite.
+ jobject satelliteEphemerisTimeObj =
+ env->CallObjectMethod(satelliteEphemerisObj,
+ method_gpsSatelliteEphemerisGetSatelliteEphemerisTime);
+ GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+ satelliteEphemeris.satelliteEphemerisTime);
+ env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+ satelliteEphemerisList.push_back(satelliteEphemeris);
+ env->DeleteLocalRef(satelliteEphemerisObj);
+ }
+}
+
+void GnssAssistanceUtil::setSatelliteCorrections(
+ JNIEnv* env, jobject satelliteCorrectionsObj,
+ std::vector<GnssSatelliteCorrections>& gnssSatelliteCorrectionsList) {
+ if (satelliteCorrectionsObj == nullptr) return;
+ auto len = env->CallIntMethod(satelliteCorrectionsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ GnssSatelliteCorrections gnssSatelliteCorrections;
+ jobject satelliteCorrectionObj =
+ env->CallObjectMethod(satelliteCorrectionsObj, method_listGet, i);
+ if (satelliteCorrectionObj == nullptr) continue;
+ jint svid = env->CallIntMethod(satelliteCorrectionObj, method_satelliteCorrectionGetSvid);
+ gnssSatelliteCorrections.svid = svid;
+ jobject ionosphericCorrectionsObj =
+ env->CallObjectMethod(satelliteCorrectionObj,
+ method_satelliteCorrectionGetIonosphericCorrections);
+ env->DeleteLocalRef(satelliteCorrectionObj);
+ auto size = env->CallIntMethod(ionosphericCorrectionsObj, method_listSize);
+ for (uint16_t j = 0; j < size; ++j) {
+ jobject ionosphericCorrectionObj =
+ env->CallObjectMethod(ionosphericCorrectionsObj, method_listGet, j);
+ if (ionosphericCorrectionObj == nullptr) continue;
+ IonosphericCorrection ionosphericCorrection;
+ jlong carrierFrequencyHz =
+ env->CallLongMethod(ionosphericCorrectionObj,
+ method_ionosphericCorrectionGetCarrierFrequencyHz);
+ ionosphericCorrection.carrierFrequencyHz = carrierFrequencyHz;
+
+ jobject gnssCorrectionComponentObj =
+ env->CallObjectMethod(ionosphericCorrectionObj,
+ method_ionosphericCorrectionGetIonosphericCorrection);
+ env->DeleteLocalRef(ionosphericCorrectionObj);
+
+ jstring sourceKey = static_cast<jstring>(
+ env->CallObjectMethod(gnssCorrectionComponentObj,
+ method_gnssCorrectionComponentGetSourceKey));
+ ScopedJniString jniSourceKey{env, sourceKey};
+ ionosphericCorrection.ionosphericCorrectionComponent.sourceKey =
+ android::String16(jniSourceKey.c_str());
+
+ jobject pseudorangeCorrectionObj =
+ env->CallObjectMethod(gnssCorrectionComponentObj,
+ method_gnssCorrectionComponentGetPseudorangeCorrection);
+ jdouble correctionMeters =
+ env->CallDoubleMethod(pseudorangeCorrectionObj,
+ method_pseudorangeCorrectionGetCorrectionMeters);
+ jdouble correctionUncertaintyMeters = env->CallDoubleMethod(
+ pseudorangeCorrectionObj,
+ method_pseudorangeCorrectionGetCorrectionUncertaintyMeters);
+ jdouble correctionRateMetersPerSecond = env->CallDoubleMethod(
+ pseudorangeCorrectionObj,
+ method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond);
+ ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+ .correctionMeters = correctionMeters;
+ ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+ .correctionUncertaintyMeters = correctionUncertaintyMeters;
+ ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+ .correctionRateMetersPerSecond = correctionRateMetersPerSecond;
+ env->DeleteLocalRef(pseudorangeCorrectionObj);
+
+ jobject gnssIntervalObj =
+ env->CallObjectMethod(gnssCorrectionComponentObj,
+ method_gnssCorrectionComponentGetValidityInterval);
+ jdouble startMillisSinceGpsEpoch =
+ env->CallDoubleMethod(gnssIntervalObj,
+ method_gnssIntervalGetStartMillisSinceGpsEpoch);
+ jdouble endMillisSinceGpsEpoch =
+ env->CallDoubleMethod(gnssIntervalObj,
+ method_gnssIntervalGetEndMillisSinceGpsEpoch);
+ ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+ .startMillisSinceGpsEpoch = startMillisSinceGpsEpoch;
+ ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+ .endMillisSinceGpsEpoch = endMillisSinceGpsEpoch;
+ env->DeleteLocalRef(gnssIntervalObj);
+
+ env->DeleteLocalRef(gnssCorrectionComponentObj);
+ gnssSatelliteCorrections.ionosphericCorrections.push_back(ionosphericCorrection);
+ }
+ gnssSatelliteCorrectionsList.push_back(gnssSatelliteCorrections);
+ env->DeleteLocalRef(ionosphericCorrectionsObj);
+ }
+}
+
+void GnssAssistanceUtil::setRealTimeIntegrityModels(
+ JNIEnv* env, jobject realTimeIntegrityModelsObj,
+ std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels) {
+ if (realTimeIntegrityModelsObj == nullptr) return;
+ auto len = env->CallIntMethod(realTimeIntegrityModelsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject realTimeIntegrityModelObj =
+ env->CallObjectMethod(realTimeIntegrityModelsObj, method_listGet, i);
+ if (realTimeIntegrityModelObj == nullptr) continue;
+ RealTimeIntegrityModel realTimeIntegrityModel;
+ jint badSvid = env->CallIntMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetBadSvid);
+ jobject badSignalTypesObj =
+ env->CallObjectMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetBadSignalTypes);
+ auto badSignalTypesSize = env->CallIntMethod(badSignalTypesObj, method_listSize);
+ for (uint16_t j = 0; j < badSignalTypesSize; ++j) {
+ GnssSignalType badSignalType;
+ jobject badSignalTypeObj = env->CallObjectMethod(badSignalTypesObj, method_listGet, j);
+ if (badSignalTypeObj != nullptr) {
+ setGnssSignalType(env, badSignalTypeObj, badSignalType);
+ realTimeIntegrityModel.badSignalTypes.push_back(badSignalType);
+ env->DeleteLocalRef(badSignalTypeObj);
+ }
+ }
+
+ jlong startDateSeconds =
+ env->CallLongMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetStartDateSeconds);
+ jlong endDateSeconds = env->CallLongMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetEndDateSeconds);
+ jlong publishDateSeconds =
+ env->CallLongMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetPublishDateSeconds);
+ jstring advisoryNumber = static_cast<jstring>(
+ env->CallObjectMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetAdvisoryNumber));
+ ScopedJniString jniAdvisoryNumber{env, advisoryNumber};
+ jstring advisoryType = static_cast<jstring>(
+ env->CallObjectMethod(realTimeIntegrityModelObj,
+ method_realTimeIntegrityModelGetAdvisoryType));
+ ScopedJniString jniAdvisoryType{env, advisoryType};
+
+ realTimeIntegrityModel.badSvid = badSvid;
+ realTimeIntegrityModel.startDateSeconds = startDateSeconds;
+ realTimeIntegrityModel.endDateSeconds = endDateSeconds;
+ realTimeIntegrityModel.publishDateSeconds = publishDateSeconds;
+ realTimeIntegrityModel.advisoryNumber = android::String16(jniAdvisoryNumber.c_str());
+ realTimeIntegrityModel.advisoryType = android::String16(jniAdvisoryType.c_str());
+ realTimeIntegrityModels.push_back(realTimeIntegrityModel);
+ env->DeleteLocalRef(badSignalTypesObj);
+ env->DeleteLocalRef(realTimeIntegrityModelObj);
+ }
+}
+
+void GnssAssistanceUtil::setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+ GnssSignalType& gnssSignalType) {
+ if (gnssSignalTypeObj == nullptr) {
+ ALOGE("gnssSignalTypeObj is null");
+ return;
+ }
+ jint constellationType =
+ env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetConstellationType);
+ jdouble carrierFrequencyHz =
+ env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCarrierFrequencyHz);
+ jstring codeType = static_cast<jstring>(
+ env->CallObjectMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCodeType));
+ ScopedJniString jniCodeType{env, codeType};
+
+ gnssSignalType.constellation = static_cast<GnssConstellationType>(constellationType);
+ gnssSignalType.carrierFrequencyHz = static_cast<int32_t>(carrierFrequencyHz);
+ gnssSignalType.codeType = std::string(jniCodeType.c_str());
+}
+
+void GnssAssistanceUtil::setTimeModels(JNIEnv* env, jobject timeModelsObj,
+ std::vector<TimeModel>& timeModels) {
+ if (timeModelsObj == nullptr) return;
+ auto len = env->CallIntMethod(timeModelsObj, method_listSize);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject timeModelObj = env->CallObjectMethod(timeModelsObj, method_listGet, i);
+ TimeModel timeModel;
+ jint toGnss = env->CallIntMethod(timeModelObj, method_timeModelsGetToGnss);
+ jlong timeOfWeek = env->CallLongMethod(timeModelObj, method_timeModelsGetTimeOfWeek);
+ jint weekNumber = env->CallIntMethod(timeModelObj, method_timeModelsGetWeekNumber);
+ jdouble a0 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA0);
+ jdouble a1 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA1);
+ timeModel.toGnss = static_cast<GnssConstellationType>(toGnss);
+ timeModel.timeOfWeek = timeOfWeek;
+ timeModel.weekNumber = static_cast<int32_t>(weekNumber);
+ timeModel.a0 = a0;
+ timeModel.a1 = a1;
+ timeModels.push_back(timeModel);
+ env->DeleteLocalRef(timeModelObj);
+ }
+}
+
+void GnssAssistanceUtil::setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+ LeapSecondsModel& leapSecondsModel) {
+ if (leapSecondsModelObj == nullptr) {
+ leapSecondsModel.leapSeconds = -1;
+ return;
+ }
+ jint dayNumberLeapSecondsFuture =
+ env->CallIntMethod(leapSecondsModelObj,
+ method_leapSecondsModelGetDayNumberLeapSecondsFuture);
+ jint leapSeconds =
+ env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSeconds);
+ jint leapSecondsFuture =
+ env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSecondsFuture);
+ jint weekNumberLeapSecondsFuture =
+ env->CallIntMethod(leapSecondsModelObj,
+ method_leapSecondsModelGetWeekNumberLeapSecondsFuture);
+ leapSecondsModel.dayNumberLeapSecondsFuture = static_cast<int32_t>(dayNumberLeapSecondsFuture);
+ leapSecondsModel.leapSeconds = static_cast<int32_t>(leapSeconds);
+ leapSecondsModel.leapSecondsFuture = static_cast<int32_t>(leapSecondsFuture);
+ leapSecondsModel.weekNumberLeapSecondsFuture =
+ static_cast<int32_t>(weekNumberLeapSecondsFuture);
+}
+
+void GnssAssistanceUtil::setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+ SatelliteEphemerisTime& satelliteEphemerisTime) {
+ if (satelliteEphemerisTimeObj == nullptr) return;
+ jdouble iode =
+ env->CallDoubleMethod(satelliteEphemerisTimeObj, method_satelliteEphemerisTimeGetIode);
+ jdouble toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+ method_satelliteEphemerisTimeGetToeSeconds);
+ jint weekNumber = env->CallIntMethod(satelliteEphemerisTimeObj,
+ method_satelliteEphemerisTimeGetWeekNumber);
+ satelliteEphemerisTime.iode = iode;
+ satelliteEphemerisTime.toeSeconds = toeSeconds;
+ satelliteEphemerisTime.weekNumber = weekNumber;
+}
+
+void GnssAssistanceUtil::setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+ KeplerianOrbitModel& keplerianOrbitModel) {
+ if (keplerianOrbitModelObj == nullptr) return;
+ jdouble rootA =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetRootA);
+ jdouble eccentricity = env->CallDoubleMethod(keplerianOrbitModelObj,
+ method_keplerianOrbitModelGetEccentricity);
+ jdouble m0 = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetM0);
+ jdouble omega =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmega);
+ jdouble omegaDot =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmegaDot);
+ jdouble deltaN =
+ env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetDeltaN);
+ jdouble iDot = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetIDot);
+ jobject secondOrderHarmonicPerturbationObj =
+ env->CallObjectMethod(keplerianOrbitModelObj,
+ method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation);
+ jdouble cic = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCic);
+ jdouble cis = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCis);
+ jdouble crs = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCrs);
+ jdouble crc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCrc);
+ jdouble cuc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCuc);
+ jdouble cus = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+ method_secondOrderHarmonicPerturbationGetCus);
+ keplerianOrbitModel.rootA = rootA;
+ keplerianOrbitModel.eccentricity = eccentricity;
+ keplerianOrbitModel.m0 = m0;
+ keplerianOrbitModel.omega = omega;
+ keplerianOrbitModel.omegaDot = omegaDot;
+ keplerianOrbitModel.deltaN = deltaN;
+ keplerianOrbitModel.iDot = iDot;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cic = cic;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cis = cis;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.crs = crs;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.crc = crc;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cuc = cuc;
+ keplerianOrbitModel.secondOrderHarmonicPerturbation.cus = cus;
+ env->DeleteLocalRef(secondOrderHarmonicPerturbationObj);
+}
+
+void GnssAssistanceUtil::setKlobucharIonosphericModel(
+ JNIEnv* env, jobject klobucharIonosphericModelObj,
+ KlobucharIonosphericModel& klobucharIonosphericModel) {
+ if (klobucharIonosphericModelObj == nullptr) return;
+ jdouble alpha0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha0);
+ jdouble alpha1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha1);
+ jdouble alpha2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha2);
+ jdouble alpha3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetAlpha3);
+ jdouble beta0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta0);
+ jdouble beta1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta1);
+ jdouble beta2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta2);
+ jdouble beta3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+ method_klobucharIonosphericModelGetBeta3);
+ klobucharIonosphericModel.alpha0 = alpha0;
+ klobucharIonosphericModel.alpha1 = alpha1;
+ klobucharIonosphericModel.alpha2 = alpha2;
+ klobucharIonosphericModel.alpha3 = alpha3;
+ klobucharIonosphericModel.beta0 = beta0;
+ klobucharIonosphericModel.beta1 = beta1;
+ klobucharIonosphericModel.beta2 = beta2;
+ klobucharIonosphericModel.beta3 = beta3;
+}
+
+void GnssAssistanceUtil::setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel) {
+ if (utcModelObj == nullptr) {
+ utcModel.weekNumber = -1;
+ return;
+ }
+ jdouble a0 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA0);
+ jdouble a1 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA1);
+ jlong timeOfWeek = env->CallLongMethod(utcModelObj, method_utcModelGetTimeOfWeek);
+ jint weekNumber = env->CallIntMethod(utcModelObj, method_utcModelGetWeekNumber);
+ utcModel.a0 = a0;
+ utcModel.a1 = a1;
+ utcModel.timeOfWeek = timeOfWeek;
+ utcModel.weekNumber = static_cast<int32_t>(weekNumber);
+}
+
+void GnssAssistanceUtil::setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj,
+ GnssAlmanac& gnssAlmanac) {
+ if (gnssAlmanacObj == nullptr) {
+ gnssAlmanac.weekNumber = -1;
+ return;
+ }
+ jlong issueDateMillis =
+ env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetIssueDateMillis);
+ jint ioda = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetIoda);
+ jint weekNumber = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetWeekNumber);
+ jlong toaSeconds = env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetToaSeconds);
+ jboolean isCompleteAlmanacProvided =
+ env->CallBooleanMethod(gnssAlmanacObj, method_gnssAlmanacIsCompleteAlmanacProvided);
+ gnssAlmanac.issueDateMs = issueDateMillis;
+ gnssAlmanac.ioda = ioda;
+ gnssAlmanac.weekNumber = weekNumber;
+ gnssAlmanac.toaSeconds = toaSeconds;
+ gnssAlmanac.isCompleteAlmanacProvided = isCompleteAlmanacProvided;
+
+ jobject satelliteAlmanacsListObj =
+ env->CallObjectMethod(gnssAlmanacObj, method_gnssAlmanacGetSatelliteAlmanacs);
+ auto len = env->CallIntMethod(satelliteAlmanacsListObj, method_listSize);
+ std::vector<GnssSatelliteAlmanac> list(len);
+ for (uint16_t i = 0; i < len; ++i) {
+ jobject gnssSatelliteAlmanacObj =
+ env->CallObjectMethod(satelliteAlmanacsListObj, method_listGet, i);
+ if (gnssSatelliteAlmanacObj == nullptr) continue;
+ GnssSatelliteAlmanac gnssSatelliteAlmanac;
+ jint svid = env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvid);
+ jint svHealth =
+ env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvHealth);
+ jdouble af0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf0);
+ jdouble af1 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf1);
+ jdouble eccentricity = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+ method_satelliteAlmanacGetEccentricity);
+ jdouble inclination = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+ method_satelliteAlmanacGetInclination);
+ jdouble m0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetM0);
+ jdouble omega =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega);
+ jdouble omega0 =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega0);
+ jdouble omegaDot =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmegaDot);
+ jdouble rootA =
+ env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetRootA);
+ gnssSatelliteAlmanac.svid = static_cast<int32_t>(svid);
+ gnssSatelliteAlmanac.svHealth = static_cast<int32_t>(svHealth);
+ gnssSatelliteAlmanac.af0 = af0;
+ gnssSatelliteAlmanac.af1 = af1;
+ gnssSatelliteAlmanac.eccentricity = eccentricity;
+ gnssSatelliteAlmanac.inclination = inclination;
+ gnssSatelliteAlmanac.m0 = m0;
+ gnssSatelliteAlmanac.omega = omega;
+ gnssSatelliteAlmanac.omega0 = omega0;
+ gnssSatelliteAlmanac.omegaDot = omegaDot;
+ gnssSatelliteAlmanac.rootA = rootA;
+ list.at(i) = gnssSatelliteAlmanac;
+ env->DeleteLocalRef(gnssSatelliteAlmanacObj);
+ }
+ gnssAlmanac.satelliteAlmanacs = list;
+ env->DeleteLocalRef(satelliteAlmanacsListObj);
+}
+
+void GnssAssistanceUtil::setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+ AuxiliaryInformation& auxiliaryInformation) {
+ if (auxiliaryInformationObj == nullptr) {
+ auxiliaryInformation.svid = -1;
+ return;
+ }
+ jint svid = env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSvid);
+ jobject availableSignalTypesObj =
+ env->CallObjectMethod(auxiliaryInformationObj,
+ method_auxiliaryInformationGetAvailableSignalTypes);
+ auto size = env->CallIntMethod(availableSignalTypesObj, method_listSize);
+ std::vector<GnssSignalType> availableSignalTypes(size);
+ for (uint16_t i = 0; i < size; ++i) {
+ jobject availableSignalTypeObj =
+ env->CallObjectMethod(availableSignalTypesObj, method_listGet, i);
+ GnssSignalType availableSignalType;
+ setGnssSignalType(env, availableSignalTypeObj, availableSignalType);
+ availableSignalTypes.at(i) = availableSignalType;
+ env->DeleteLocalRef(availableSignalTypeObj);
+ }
+ jint frequencyChannelNumber =
+ env->CallIntMethod(auxiliaryInformationObj,
+ method_auxiliaryInformationGetFrequencyChannelNumber);
+ jint satType =
+ env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSatType);
+ auxiliaryInformation.svid = static_cast<int32_t>(svid);
+ auxiliaryInformation.availableSignalTypes = availableSignalTypes;
+ auxiliaryInformation.frequencyChannelNumber = static_cast<int32_t>(frequencyChannelNumber);
+ auxiliaryInformation.satType = static_cast<BeidouB1CSatelliteOrbitType>(satType);
+ env->DeleteLocalRef(availableSignalTypesObj);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistance.h b/services/core/jni/gnss/GnssAssistance.h
new file mode 100644
index 0000000..ee97e19
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.h
@@ -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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSSASSITANCE_H
+#define _ANDROID_SERVER_GNSSASSITANCE_H
+
+#include <sys/stat.h>
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceInterface.h>
+#include <log/log.h>
+
+#include "GnssAssistanceCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+using IGnssAssistanceInterface = android::hardware::gnss::gnss_assistance::IGnssAssistanceInterface;
+using IGnssAssistanceCallback = android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
+using BeidouAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::BeidouAssistance;
+using BeidouSatelliteEphemeris = android::hardware::gnss::gnss_assistance::BeidouSatelliteEphemeris;
+using GalileoAssistance =
+ android::hardware::gnss::gnss_assistance::GnssAssistance::GalileoAssistance;
+using GalileoSatelliteEphemeris =
+ android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris;
+using GalileoIonosphericModel = android::hardware::gnss::gnss_assistance::GalileoIonosphericModel;
+using GlonassAssistance =
+ android::hardware::gnss::gnss_assistance::GnssAssistance::GlonassAssistance;
+using GlonassAlmanac = android::hardware::gnss::gnss_assistance::GlonassAlmanac;
+using GlonassSatelliteEphemeris =
+ android::hardware::gnss::gnss_assistance::GlonassSatelliteEphemeris;
+using GnssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance;
+using GnssSignalType = android::hardware::gnss::GnssSignalType;
+using GpsAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::GpsAssistance;
+using QzssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::QzssAssistance;
+using GnssAlmanac = android::hardware::gnss::gnss_assistance::GnssAlmanac;
+using GnssSatelliteCorrections =
+ android::hardware::gnss::gnss_assistance::GnssAssistance::GnssSatelliteCorrections;
+using GpsSatelliteEphemeris = android::hardware::gnss::gnss_assistance::GpsSatelliteEphemeris;
+using SatelliteEphemerisTime = android::hardware::gnss::gnss_assistance::SatelliteEphemerisTime;
+using UtcModel = android::hardware::gnss::gnss_assistance::UtcModel;
+using LeapSecondsModel = android::hardware::gnss::gnss_assistance::LeapSecondsModel;
+using KeplerianOrbitModel = android::hardware::gnss::gnss_assistance::KeplerianOrbitModel;
+using KlobucharIonosphericModel =
+ android::hardware::gnss::gnss_assistance::KlobucharIonosphericModel;
+using TimeModel = android::hardware::gnss::gnss_assistance::TimeModel;
+using RealTimeIntegrityModel = android::hardware::gnss::gnss_assistance::RealTimeIntegrityModel;
+using AuxiliaryInformation = android::hardware::gnss::gnss_assistance::AuxiliaryInformation;
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssAssistanceInterface {
+public:
+ GnssAssistanceInterface(const sp<IGnssAssistanceInterface>& iGnssAssistance);
+ jboolean injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj);
+ jboolean setCallback(const sp<IGnssAssistanceCallback>& callback);
+
+private:
+ const sp<IGnssAssistanceInterface> mGnssAssistanceInterface;
+};
+
+struct GnssAssistanceUtil {
+ static void setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+ GlonassAssistance& galileoAssistance);
+ static void setGlonassSatelliteEphemeris(
+ JNIEnv* env, jobject glonassSatelliteEphemerisObj,
+ std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList);
+ static void setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+ GlonassAlmanac& glonassAlmanac);
+ static void setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+ BeidouAssistance& beidouAssistance);
+ static void setBeidouSatelliteEphemeris(
+ JNIEnv* env, jobject beidouSatelliteEphemerisObj,
+ std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList);
+ static void setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+ GalileoAssistance& galileoAssistance);
+ static void setGalileoSatelliteEphemeris(
+ JNIEnv* env, jobject galileoSatelliteEphemerisObj,
+ std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList);
+ static void setGaliloKlobucharIonosphericModel(JNIEnv* env, jobject galileoIonosphericModelObj,
+ GalileoIonosphericModel& ionosphericModel);
+ static void setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+ GnssAssistance& gnssAssistance);
+ static void setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+ GpsAssistance& gpsAssistance);
+ template <class T>
+ static void setGpsOrQzssSatelliteEphemeris(JNIEnv* env, jobject satelliteEphemerisObj,
+ std::vector<T>& satelliteEphemeris);
+ static void setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+ QzssAssistance& qzssAssistance);
+ static void setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj, GnssAlmanac& gnssAlmanac);
+ static void setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+ GnssSignalType& gnssSignalType);
+ static void setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+ KeplerianOrbitModel& keplerianOrbitModel);
+ static void setKlobucharIonosphericModel(JNIEnv* env, jobject klobucharIonosphericModelObj,
+ KlobucharIonosphericModel& klobucharIonosphericModel);
+ static void setTimeModels(JNIEnv* env, jobject timeModelsObj,
+ std::vector<TimeModel>& timeModels);
+ static void setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+ LeapSecondsModel& leapSecondsModel);
+ static void setRealTimeIntegrityModels(
+ JNIEnv* env, jobject realTimeIntegrityModelsObj,
+ std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels);
+
+ static void setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+ SatelliteEphemerisTime& satelliteEphemerisTime);
+ static void setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel);
+ static void setSatelliteCorrections(
+ JNIEnv* env, jobject satelliteCorrectionsObj,
+ std::vector<GnssSatelliteCorrections>& satelliteCorrections);
+ static void setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+ AuxiliaryInformation& auxiliaryInformation);
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSSASSITANCE__H
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.cpp b/services/core/jni/gnss/GnssAssistanceCallback.cpp
new file mode 100644
index 0000000..dbb27d7
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssAssistanceCbJni"
+
+#include "GnssAssistanceCallback.h"
+
+#include "Utils.h"
+
+namespace {
+jmethodID method_gnssAssistanceInjectRequest;
+} // anonymous namespace
+
+namespace android::gnss {
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz) {
+ method_gnssAssistanceInjectRequest =
+ env->GetStaticMethodID(clazz, "gnssAssistanceInjectRequest", "()V");
+}
+
+// Implementation of android::hardware::gnss::gnss_assistance::GnssAssistanceCallback.
+
+Status GnssAssistanceCallback::injectRequestCb() {
+ ALOGD("%s.", __func__);
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_gnssAssistanceInjectRequest);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.h b/services/core/jni/gnss/GnssAssistanceCallback.h
new file mode 100644
index 0000000..4c8c5fc
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceCallback.h>
+#include <log/log.h>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz);
+
+/*
+ * GnssAssistanceCallback class implements the callback methods required by the
+ * android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback interface.
+ */
+class GnssAssistanceCallback : public hardware::gnss::gnss_assistance::BnGnssAssistanceCallback {
+public:
+ GnssAssistanceCallback() {}
+ binder::Status injectRequestCb() override;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 42e457c..bc5c427 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -51,7 +51,6 @@
import android.credentials.PrepareGetCredentialResponseInternal;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
-import android.credentials.flags.Flags;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.IBinder;
@@ -538,34 +537,31 @@
final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid();
- if (Flags.safeguardCandidateCredentialsApiCaller()) {
- try {
- String credentialManagerAutofillCompName = mContext.getResources().getString(
- R.string.config_defaultCredentialManagerAutofillService);
- ComponentName componentName = ComponentName.unflattenFromString(
- credentialManagerAutofillCompName);
- if (componentName == null) {
- throw new SecurityException(
- "Credential Autofill service does not exist on this device.");
- }
- PackageManager pm = mContext.createContextAsUser(
- UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
- String callingProcessPackage = pm.getNameForUid(callingUid);
- if (callingProcessPackage == null) {
- throw new SecurityException(
- "Couldn't determine the identity of the caller.");
- }
- if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
- throw new SecurityException(callingProcessPackage
- + " is not the device's credential autofill package.");
- }
- } catch (Resources.NotFoundException e) {
+ try {
+ String credentialManagerAutofillCompName = mContext.getResources().getString(
+ R.string.config_defaultCredentialManagerAutofillService);
+ ComponentName componentName = ComponentName.unflattenFromString(
+ credentialManagerAutofillCompName);
+ if (componentName == null) {
throw new SecurityException(
"Credential Autofill service does not exist on this device.");
}
+ PackageManager pm = mContext.createContextAsUser(
+ UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+ String callingProcessPackage = pm.getNameForUid(callingUid);
+ if (callingProcessPackage == null) {
+ throw new SecurityException(
+ "Couldn't determine the identity of the caller.");
+ }
+ if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
+ throw new SecurityException(callingProcessPackage
+ + " is not the device's credential autofill package.");
+ }
+ } catch (Resources.NotFoundException e) {
+ throw new SecurityException(
+ "Credential Autofill service does not exist on this device.");
}
-
// New request session, scoped for this request only.
final GetCandidateRequestSession session =
new GetCandidateRequestSession(
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index de78271..69e856d 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -325,7 +325,8 @@
}
if (newState != INVALID_DEVICE_STATE_IDENTIFIER && newState != mLastReportedState) {
- if (Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
+ if (mLastHingeAngleSensorEvent != null
+ && Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
Trace.instant(TRACE_TAG_SYSTEM_SERVER,
"[Device state changed] Last hinge sensor event timestamp: "
+ mLastHingeAngleSensorEvent.timestamp);
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
index 9117cc8..a38ecc8 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -3186,6 +3186,32 @@
assertEquals(profile, ikev2VpnProfile.toVpnProfile());
}
+ @Test
+ public void testStartAlwaysOnVpnOnSafeMode() throws Exception {
+ final Vpn vpn = createVpn(PRIMARY_USER.id);
+ setMockedUsers(PRIMARY_USER);
+
+ // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+ final int uid = Process.myUid() + 1;
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(uid);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ setAndVerifyAlwaysOnPackage(vpn, uid, false);
+ assertTrue(vpn.startAlwaysOnVpn());
+ assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+
+ // Simulate safe mode and restart the always-on VPN to verify the always-on package is not
+ // reset.
+ doReturn(null).when(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ doReturn(null).when(mPackageManager).queryIntentServicesAsUser(
+ any(), any(), eq(PRIMARY_USER.id));
+ doReturn(true).when(mPackageManager).isSafeMode();
+ assertFalse(vpn.startAlwaysOnVpn());
+ assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+ }
+
// Make it public and un-final so as to spy it
public class TestDeps extends Vpn.Dependencies {
TestDeps() {}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
index 29f0722..fecbc7c8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -25,7 +25,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
@@ -396,7 +396,7 @@
assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
- verifyZeroInteractions(mWakelockController);
+ verifyNoMoreInteractions(mWakelockController);
}
private void setScreenOffBecauseOfPositiveProximityState() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f8b4113..b8a5f34 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -234,6 +234,7 @@
doReturn(true).when(mFlags).isDisplayOffloadEnabled();
doReturn(true).when(mFlags).isEvenDimmerEnabled();
+ doReturn(true).when(mFlags).isDisplayContentModeManagementEnabled();
initDisplayOffloadSession();
}
@@ -1468,6 +1469,103 @@
assertFalse(mDisplayOffloadSession.isActive());
}
+ @Test
+ public void testAllowsContentSwitch_firstDisplay() throws Exception {
+ // Set up a first display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The first display should be allowed to use the content mode switch
+ DisplayDevice firstDisplayDevice = mListener.addedDisplays.get(0);
+ assertTrue((firstDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_secondaryDisplayPublicAndNotShouldShowOwnContent()
+ throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Set the secondary display to be a public display
+ doReturn(new int[0]).when(mMockedResources)
+ .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+ // Disable FLAG_OWN_CONTENT_ONLY for the secondary display
+ doReturn(true).when(mMockedResources)
+ .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // This secondary display should be allowed to use the content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_privateDisplay() throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Set the secondary display to be a private display
+ doReturn(new int[]{ PORT_B }).when(mMockedResources)
+ .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The private display should not be allowed to use the content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_ownContentOnlyDisplay() throws Exception {
+ // Set up a first display and a secondary display
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ updateAvailableDisplays();
+
+ // Enable FLAG_OWN_CONTENT_ONLY for the secondary display
+ doReturn(false).when(mMockedResources)
+ .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // The secondary display with FLAG_OWN_CONTENT_ONLY enabled should not be allowed to use the
+ // content mode switch
+ DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+ assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+ }
+
+ @Test
+ public void testAllowsContentSwitch_flagShouldShowSystemDecorations() throws Exception {
+ // Set up a display
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should not be allowed to use the
+ // content mode switch
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+ int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
+ boolean allowsContentModeSwitch =
+ ((flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+ boolean shouldShowSystemDecorations =
+ ((flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0);
+ assertFalse(allowsContentModeSwitch && shouldShowSystemDecorations);
+ }
+
private void initDisplayOffloadSession() {
when(mDisplayOffloader.startOffload()).thenReturn(true);
when(mDisplayOffloader.allowAutoBrightnessInDoze()).thenReturn(true);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index 019b70e..f067fa1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -21,7 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.display.DisplayManagerInternal;
@@ -210,7 +210,7 @@
// Validate one suspend blocker was released
assertFalse(mWakelockController.isProximityPositiveAcquired());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
@@ -238,7 +238,7 @@
// Validate one suspend blocker was released
assertFalse(mWakelockController.isProximityNegativeAcquired());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
@@ -265,7 +265,7 @@
// Validate one suspend blocker was released
assertFalse(mWakelockController.isOnStateChangedPending());
- verifyZeroInteractions(mDisplayPowerCallbacks);
+ verifyNoMoreInteractions(mDisplayPowerCallbacks);
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 49de801..bf05439 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -422,7 +421,7 @@
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
- verifyZeroInteractions(automaticBrightnessController);
+ verifyNoMoreInteractions(automaticBrightnessController);
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
}
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 d79d884..f0e61ec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -109,7 +109,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.Manifest;
import android.app.ActivityManager;
@@ -3724,8 +3724,8 @@
setDeviceConfigInt(KEY_TEMPORARY_QUOTA_BUMP, 0);
mAppStandbyListener.triggerTemporaryQuotaBump(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
- verifyZeroInteractions(mPackageManagerInternal);
- verifyZeroInteractions(mService.mHandler);
+ verifyNoMoreInteractions(mPackageManagerInternal);
+ verifyNoMoreInteractions(mService.mHandler);
}
private void testTemporaryQuota_bumpedAfterDeferral(int standbyBucket) throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 7dab1c8..859d2d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -30,7 +30,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -244,7 +244,7 @@
addAlarmsToStore(simpleAlarm, alarmClock);
mAlarmStore.remove(simpleAlarm::equals);
- verifyZeroInteractions(onRemoved);
+ verifyNoMoreInteractions(onRemoved);
mAlarmStore.remove(alarmClock::equals);
verify(onRemoved).run();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 35ab2d2..acc06d0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -74,7 +74,6 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.Manifest;
import android.app.ActivityManager;
@@ -584,7 +583,7 @@
if (app.uid == uidRec.getUid() && expectedBlockState == NETWORK_STATE_BLOCK) {
verify(app.getThread()).setNetworkBlockSeq(uidRec.curProcStateSeq);
} else {
- verifyZeroInteractions(app.getThread());
+ verifyNoMoreInteractions(app.getThread());
}
Mockito.reset(app.getThread());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index e678acc..987b9c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -21,6 +21,7 @@
import static com.android.server.am.ActivityManagerService.Injector;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -625,6 +626,60 @@
assertTrue(startInfo.equals(startInfoFromParcel));
}
+ /** Test that new timestamps are added to the correct record (the most recently created one). */
+ @Test
+ public void testTimestampAddedToCorrectRecord() throws Exception {
+ // Use a different start timestamp for each record so we can identify which was added to.
+ final long startTimeRecord1 = 123L;
+ final long startTimeRecord2 = 456L;
+
+ final long forkTime = 789L;
+
+ // Create a process record to use with all starts.
+ ProcessRecord app = makeProcessRecord(
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
+ null, // definingUid
+ APP_1_PROCESS_NAME, // processName
+ APP_1_PACKAGE_NAME); // packageName
+
+ // Trigger a start info record.
+ mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord1, app,
+ buildIntent(COMPONENT), false /* isAlarm */);
+
+ // Wait at least 1 ms for monotonic time to increase.
+ sleep(1);
+
+ // Verify the record was added successfully.
+ ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+ assertEquals(1, list.size());
+ assertEquals(startTimeRecord1, list.get(0).getStartupTimestamps().get(0).longValue());
+
+ // Now trigger another start info record.
+ mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord2, app,
+ buildIntent(COMPONENT), false /* isAlarm */);
+
+ // Add a timestamp to the most recent record.
+ mAppStartInfoTracker.addTimestampToStart(
+ app, forkTime, ApplicationStartInfo.START_TIMESTAMP_FORK);
+
+ // Verify the record was added successfully.
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+ assertEquals(2, list.size());
+ assertEquals(startTimeRecord2, list.get(0).getStartupTimestamps().get(0).longValue());
+ assertEquals(startTimeRecord1, list.get(1).getStartupTimestamps().get(0).longValue());
+
+ // Verify that the new timestamp is set correctly on the 2nd record that was added and not
+ // on the first.
+ assertEquals(forkTime, list.get(0).getStartupTimestamps()
+ .get(ApplicationStartInfo.START_TIMESTAMP_FORK).longValue());
+ assertFalse(list.get(1).getStartupTimestamps().containsKey(
+ ApplicationStartInfo.START_TIMESTAMP_FORK));
+ }
+
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
index ae0452a..b7087c7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
@@ -21,7 +21,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
@@ -105,7 +105,7 @@
} catch (EOFException expected) {
}
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
@@ -126,7 +126,7 @@
} catch (EOFException expected) {
}
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
@@ -141,7 +141,7 @@
FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor, mOutputStreamMock);
- verifyZeroInteractions(mOutputStreamMock);
+ verifyNoMoreInteractions(mOutputStreamMock);
assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index bf7e3a0..346d5f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -32,7 +32,6 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.app.backup.IBackupManagerMonitor;
@@ -239,7 +238,7 @@
mMockPackageManagerInternal, mUserId, mContext);
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
- verifyZeroInteractions(mBackupManagerMonitorMock);
+ verifyNoMoreInteractions(mBackupManagerMonitorMock);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 3e87943..2d84887 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1029,6 +1029,7 @@
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1127,6 +1128,54 @@
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes_Tuning() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+ expectedStats.expirationTimeElapsed = now + 34 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 5;
+ expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 20;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ // There is only one session in the past active bucket window, the empty time for this
+ // window is the bucket window size - duration of the session.
+ expectedStats.expirationTimeElapsed = now + 54 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
*/
@@ -1195,6 +1244,7 @@
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() {
// Set time to 3 minutes after boot.
advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
@@ -1206,10 +1256,10 @@
ExecutionStats expectedStats = new ExecutionStats();
// Exempted
- expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
- expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
- expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 2;
@@ -1268,6 +1318,49 @@
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes_Tunning() {
+ // Set time to 3 minutes after boot.
+ advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+ advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+ expectedStats.expirationTimeElapsed = 30 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 2;
+ expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 2;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.expirationTimeElapsed = 50 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
*/
@@ -1425,6 +1518,7 @@
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() {
for (int i = 0; i < 20; ++i) {
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1581,6 +1675,165 @@
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes_Tuning() {
+ for (int i = 0; i < 20; ++i) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ 5 * MINUTE_IN_MILLIS, 5), false);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ for (int j = 0; j < 5; ++j) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ MINUTE_IN_MILLIS, 2), false);
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ advanceElapsedClock(54 * SECOND_IN_MILLIS);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+ advanceElapsedClock(500);
+ advanceElapsedClock(400);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+ advanceElapsedClock(100);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ }
+ advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(16, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(64, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(192, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(320, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(11, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(11, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * SECOND_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(7, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced
+ assertEquals(28, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced
+ assertEquals(84, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced
+ assertEquals(140, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ MINUTE_IN_MILLIS);
+
+ // Only two TimingSessions there for every hour.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(2, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(8, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(24, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(40, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * MINUTE_IN_MILLIS);
+
+ // Only one TimingSessions there for every hour
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 15 * MINUTE_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+ // between an hour and 15 minutes.
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(1, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
*/
@@ -2231,32 +2484,6 @@
}
}
- @Test
- @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
- public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
- false);
-
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
- // window size = allowed time, so jobs can essentially run non-stop until they reach the
- // max execution time.
- setStandbyBucket(EXEMPTED_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(10 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- }
- }
-
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window.
@@ -2327,6 +2554,7 @@
@Test
@EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Close to RARE boundary.
@@ -2390,7 +2618,30 @@
@Test
@EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes_Tuning() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to ACTIVE boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
+
+ // ACTIVE window != allowed time.
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(17 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(20 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER})
+ @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
public void testGetTimeUntilQuotaConsumedLocked_Installer() {
PackageInfo pi = new PackageInfo();
pi.packageName = SOURCE_PACKAGE;
@@ -2412,7 +2663,7 @@
// Far away from FREQUENT boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// Overlap WORKING_SET boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2422,12 +2673,12 @@
// Close to ACTIVE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// Close to EXEMPTED boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+ now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
2 * MINUTE_IN_MILLIS, 5), false);
// No additional quota for the system installer when the app is in RARE, FREQUENT,
@@ -2486,6 +2737,42 @@
}
}
+ @Test
+ @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+ Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER,
+ Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+ public void testGetTimeUntilQuotaConsumedLocked_Installer_Tuning() {
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = SOURCE_PACKAGE;
+ pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES};
+ pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED};
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.uid = mSourceUid;
+ doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission(
+ eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid));
+ mQuotaController.onSystemServicesReady();
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to EXEMPTED boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), false);
+
+ // Additional quota for the system installer when the app is in EXEMPTED bucket.
+ // EXEMPTED window == allowed time.
+ setStandbyBucket(EXEMPTED_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(38 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 2 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
*/
@@ -2637,33 +2924,6 @@
}
}
- @Test
- @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
- public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS),
- mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5),
- false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5),
- false);
-
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
- // window size != allowed time.
- setStandbyBucket(EXEMPTED_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(0,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- }
- }
-
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window and the session is rolling out of the window.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 3d0c637..29af7d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -27,7 +27,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.AssertJUnit.assertEquals;
@@ -128,7 +128,7 @@
AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
- verifyZeroInteractions(mNotificationManager);
+ verifyNoMoreInteractions(mNotificationManager);
}
@Test
@@ -143,7 +143,7 @@
Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
- verifyZeroInteractions(mNotificationManager);
+ verifyNoMoreInteractions(mNotificationManager);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
index dc04b6a..bf3fe8c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -29,7 +29,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.AppOpsManager;
import android.content.Context;
@@ -176,9 +176,9 @@
prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET,
true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
- verifyZeroInteractions(mLightsManager);
- verifyZeroInteractions(mAppOpsManager);
- verifyZeroInteractions(mSensorManager);
+ verifyNoMoreInteractions(mLightsManager);
+ verifyNoMoreInteractions(mAppOpsManager);
+ verifyNoMoreInteractions(mSensorManager);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index 241ffdc..4f74667 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -33,10 +33,8 @@
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.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -48,7 +46,6 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -95,7 +92,6 @@
@Mock
private Resources mResources;
- private WallpaperCropper mWallpaperCropper;
private static final Point PORTRAIT_ONE = new Point(500, 800);
private static final Point PORTRAIT_TWO = new Point(400, 1000);
@@ -171,7 +167,6 @@
return getWallpaperTestDir(userId);
}).when(() -> WallpaperUtils.getWallpaperDir(anyInt()));
- mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
}
private File getWallpaperTestDir(int userId) {
@@ -738,13 +733,13 @@
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(DEFAULT_DISPLAY));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(100, 100));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ DEFAULT_DISPLAY, wallpaperData)).isTrue();
}
// Test isWallpaperCompatibleForDisplay always return true for the stock wallpaper.
@@ -755,49 +750,67 @@
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ true,
new Point(100, 100));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
// Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
// aspect ratio meets the hard-coded aspect ratio.
@Test
- public void isWallpaperCompatibleForDisplay_wallpaperSizeSuitableForDisplayAndMeetAspectRatio_returnTrue()
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeLargerThanDisplayAndMeetAspectRatio_returnTrue()
throws Exception {
final int displayId = 2;
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(4000, 3000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
- // Test isWallpaperCompatibleForDisplay wallpaper is not suitable for the display and wallpaper
- // aspect ratio meets the hard-coded aspect ratio.
+ // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+ // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
@Test
- public void isWallpaperCompatibleForDisplay_wallpaperSizeNotSuitableForDisplayAndMeetAspectRatio_returnFalse()
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButBeyondThresholdAndMeetAspectRatio_returnTrue()
throws Exception {
final int displayId = 2;
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
- new Point(1000, 500));
+ new Point(2000, 900));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isFalse();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
+ }
+
+ // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+ // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
+ @Test
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButAboveThresholdAndMeetAspectRatio_returnFalse()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(2000, 800));
+
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isFalse();
}
// Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
@@ -809,13 +822,13 @@
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 2560;
displayInfo.logicalHeight = 1044;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(2000, 4000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isFalse();
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isFalse();
}
// Test isWallpaperCompatibleForDisplay, portrait display, wallpaper is suitable for the display
@@ -827,24 +840,13 @@
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1044;
displayInfo.logicalHeight = 2560;
+ setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
new Point(2000, 4000));
- assertThat(
- mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
- wallpaperData)).isTrue();
- }
-
- private void mockDisplay(int displayId, Point displayResolution) {
- final Display mockDisplay = mock(Display.class);
- when(mockDisplay.getDisplayInfo(any(DisplayInfo.class))).thenAnswer(invocation -> {
- DisplayInfo displayInfo = invocation.getArgument(0);
- displayInfo.displayId = displayId;
- displayInfo.logicalWidth = displayResolution.x;
- displayInfo.logicalHeight = displayResolution.y;
- return true;
- });
+ assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+ displayId, wallpaperData)).isTrue();
}
private WallpaperData createWallpaperData(boolean isStockWallpaper, Point wallpaperSize)
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 4e56422..94ce723 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -39,9 +39,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.content.Context;
@@ -53,8 +53,8 @@
import android.os.BatteryStatsInternal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IWakeLockCallback;
import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -205,7 +205,7 @@
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
@Test
@@ -238,7 +238,7 @@
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
@Test
@@ -725,10 +725,11 @@
final int uid = 1234;
final int pid = 5678;
+
mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
- verifyZeroInteractions(mWakeLockLog);
+ verifyNoMoreInteractions(mWakeLockLog);
mTestLooper.dispatchAll();
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
clearInvocations(mBatteryStats);
@@ -790,6 +791,55 @@
}
@Test
+ public void test_wakeLockLogUsesWorkSource() {
+ createNotifier();
+ clearInvocations(mWakeLockLog);
+ 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;
+ WorkSource worksource = new WorkSource(1212);
+ WorkSource worksource2 = new WorkSource(3131);
+
+ mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+ PowerManager.PARTIAL_WAKE_LOCK, -1);
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+ "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, -1);
+
+ // clear the handler
+ mTestLooper.dispatchAll();
+
+ // Now test with improveWakelockLatency flag true
+ clearInvocations(mWakeLockLog);
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+
+ mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+ exceptingCallback);
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+ PowerManager.PARTIAL_WAKE_LOCK, 1);
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+ "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+ exceptingCallback);
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, 1);
+ }
+
+ @Test
public void
test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
@@ -845,7 +895,7 @@
exceptingCallback);
// No interaction because we expect that to happen in async
- verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+ verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
// Progressing the looper, and validating all the interactions
mTestLooper.dispatchAll();
@@ -944,15 +994,23 @@
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
PowerManager.PARTIAL_WAKE_LOCK);
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.DOZE_WAKE_LOCK), -1);
+ }
+
+ @Test
+ public void getWakelockMonitorTypeForLogging_evaluateProximityLevel() {
+ // How proximity wakelock is evaluated depends on boolean configuration. Test both.
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
+ .thenReturn(false);
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
PowerManager.PARTIAL_WAKE_LOCK);
- assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
- PowerManager.DOZE_WAKE_LOCK), -1);
when(mResourcesSpy.getBoolean(
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
.thenReturn(true);
-
createNotifier();
assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
@@ -1239,7 +1297,7 @@
}
@Override
- public WakeLockLog getWakeLockLog(Context context) {
+ public @NonNull WakeLockLog getWakeLockLog(Context context) {
return mWakeLockLog;
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index c1d7c7b..534337e 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -27,6 +27,8 @@
import android.os.PowerManager;
import android.os.Process;
+import com.android.server.power.WakeLockLog.TagData;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -62,8 +64,9 @@
@Test
public void testAddTwoItems_withNoEventTimeSupplied() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
@@ -93,8 +96,9 @@
@Test
public void testAddTwoItems() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -117,8 +121,9 @@
@Test
public void testAddTwoItemsWithTimeReset() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -136,9 +141,10 @@
@Test
public void testAddTwoItemsWithTagOverwrite() {
- final int tagDatabaseSize = 2;
+ final int tagDatabaseSize = 1;
+ final int tagStartingSize = 1;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -157,8 +163,9 @@
@Test
public void testAddFourItemsWithRingBufferOverflow() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Wake lock 1 acquired - log size = 3
@@ -206,8 +213,9 @@
@Test
public void testAddItemWithBadTag() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Bad tag means it wont get written
@@ -224,8 +232,9 @@
@Test
public void testAddItemWithReducedTagName() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
@@ -242,9 +251,10 @@
@Test
public void testAddAcquireAndReleaseWithRepeatTagName() {
- final int tagDatabaseSize = 6;
+ final int tagDatabaseSize = 5;
+ final int tagStartingSize = 5;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -263,8 +273,9 @@
@Test
public void testAddAcquireAndReleaseWithTimeTravel() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1100L);
@@ -283,8 +294,9 @@
@Test
public void testAddSystemWakelock() {
final int tagDatabaseSize = 6;
+ final int tagStartingSize = 2;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -302,8 +314,9 @@
@Test
public void testAddItemWithNoPackageName() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
@@ -322,8 +335,9 @@
@Test
public void testAddItemWithMultiplePackageNames() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(mPackageManager.getPackagesForUid(101)).thenReturn(
@@ -344,8 +358,9 @@
@Test
public void testAddItemsWithRepeatOwnerUid_UsesCache() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 20;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -375,8 +390,9 @@
@Test
public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
final int tagDatabaseSize = 128;
+ final int tagStartingSize = 16;
final int logSize = 10;
- TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
log.onWakeLockAcquired("TagPartial", 101,
@@ -420,6 +436,34 @@
verify(mPackageManager, times(1)).getPackagesForUid(101);
}
+ @Test
+ public void testTagDatabaseGrowsBeyondStartingSize() {
+ final int tagDatabaseSize = 3;
+ final int tagStartingSize = 1;
+ final int logSize = 10;
+ // start with size = 1 and max size
+ TestInjector injector = new TestInjector(tagDatabaseSize, tagStartingSize, logSize);
+ WakeLockLog.TagDatabase td = new WakeLockLog.TagDatabase(injector);
+
+ // Add one
+ TagData data1 = td.findOrCreateTag("Tagname1", 1001, /* shouldCreate= */ true);
+ assertEquals(0, td.getTagIndex(data1));
+
+ // Check that it grows by adding 1 more
+ TagData data2 = td.findOrCreateTag("Tagname2", 1001, /* shouldCreate= */ true);
+ assertEquals(1, td.getTagIndex(data2));
+
+ // Lets add the last one to fill up the DB to maxSize
+ TagData data3 = td.findOrCreateTag("Tagname3", 1001, /* shouldCreate= */ true);
+ assertEquals(2, td.getTagIndex(data3));
+
+ // Adding a fourth one should replace the oldest one (Tagname1)
+ TagData data4 = td.findOrCreateTag("Tagname4", 1001, /* shouldCreate= */ true);
+ assertEquals(0, td.getTagIndex(data4));
+ assertEquals(tagDatabaseSize, td.getTagIndex(data1));
+
+ }
+
private String dumpLog(WakeLockLog log, boolean includeTagDb) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -429,10 +473,12 @@
public static class TestInjector extends WakeLockLog.Injector {
private final int mTagDatabaseSize;
+ private final int mTagStartingSize;
private final int mLogSize;
- public TestInjector(int tagDatabaseSize, int logSize) {
+ public TestInjector(int tagDatabaseSize, int tagStartingSize, int logSize) {
mTagDatabaseSize = tagDatabaseSize;
+ mTagStartingSize = tagStartingSize;
mLogSize = logSize;
}
@@ -442,6 +488,11 @@
}
@Override
+ public int getTagDatabaseStartingSize() {
+ return mTagStartingSize;
+ }
+
+ @Override
public int getLogSize() {
return mLogSize;
}
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index 879aa48..2fd316e 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -409,7 +409,7 @@
final String TAG = "startTestService";
final CountDownLatch latch = new CountDownLatch(1);
LocalIntrusionDetectionEventTransport transport =
- new LocalIntrusionDetectionEventTransport();
+ new LocalIntrusionDetectionEventTransport(mContext);
ServiceConnection serviceConnection = new ServiceConnection() {
// Called when connection with the service is established.
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
index f0012da..b0b7815 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
@@ -18,8 +18,13 @@
package com.android.coretests.apps.testapp;
+import android.app.admin.SecurityLog;
+import android.app.admin.SecurityLog.SecurityEvent;
+import android.content.Context;
+import android.content.Intent;
import android.security.intrusiondetection.IntrusionDetectionEvent;
import android.security.intrusiondetection.IntrusionDetectionEventTransport;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -36,6 +41,44 @@
public class LocalIntrusionDetectionEventTransport extends IntrusionDetectionEventTransport {
private List<IntrusionDetectionEvent> mEvents = new ArrayList<>();
+ private static final String ACTION_SECURITY_EVENT_RECEIVED =
+ "com.android.coretests.apps.testapp.ACTION_SECURITY_EVENT_RECEIVED";
+ private static final String TAG = "LocalIntrusionDetectionEventTransport";
+ private static final String TEST_SECURITY_EVENT_TAG = "test_security_event_tag";
+ private static Context sContext;
+
+ public LocalIntrusionDetectionEventTransport(Context context) {
+ sContext = context;
+ }
+
+ // Broadcast an intent to the CTS test service to indicate that the security
+ // event was received.
+ private static void broadcastSecurityEventReceived() {
+ try {
+ Intent intent = new Intent(ACTION_SECURITY_EVENT_RECEIVED);
+ sContext.sendBroadcast(intent);
+ Log.i(TAG, "LIZ_TESTING: sent broadcast");
+ } catch (Exception e) {
+ Log.e(TAG, "Exception sending broadcast", e);
+ }
+ }
+
+ private static void checkIfSecurityEventReceivedFromCts(List<IntrusionDetectionEvent> events) {
+ // Loop through the events and check if any of them are the security event
+ // that uses the TEST_SECURITY_EVENT_TAG tag, which is set by the CTS test.
+ for (IntrusionDetectionEvent event : events) {
+ if (event.getType() == IntrusionDetectionEvent.SECURITY_EVENT) {
+ SecurityEvent securityEvent = event.getSecurityEvent();
+ Object[] eventData = (Object[]) securityEvent.getData();
+ if (securityEvent.getTag() == SecurityLog.TAG_KEY_GENERATED
+ && eventData[1].equals(TEST_SECURITY_EVENT_TAG)) {
+ broadcastSecurityEventReceived();
+ return;
+ }
+ }
+ }
+ }
+
@Override
public boolean initialize() {
return true;
@@ -43,6 +86,11 @@
@Override
public boolean addData(List<IntrusionDetectionEvent> events) {
+ // Our CTS tests will generate a security event. In order to
+ // verify the event is received with the appropriate data, we will
+ // check the events locally and set a property value that can be
+ // read by the test.
+ checkIfSecurityEventReceivedFromCts(events);
mEvents.addAll(events);
return true;
}
@@ -55,4 +103,4 @@
public List<IntrusionDetectionEvent> getEvents() {
return mEvents;
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
index e4bf987..9183a75 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
@@ -17,19 +17,20 @@
package com.android.coretests.apps.testapp;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
-import android.os.Process;
-
-import com.android.internal.infra.AndroidFuture;
-
public class TestLoggingService extends Service {
private static final String TAG = "TestLoggingService";
private LocalIntrusionDetectionEventTransport mLocalIntrusionDetectionEventTransport;
- public TestLoggingService() {
- mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport();
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Context context = getApplicationContext();
+ mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport(context);
}
// Binder given to clients.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 75df9a8..d254e96 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -35,7 +35,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.Manifest.permission;
@@ -243,7 +242,7 @@
@Test
public void testRequestScores_providerNotConnected() throws Exception {
assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
- verifyZeroInteractions(mRecommendationProvider);
+ verifyNoMoreInteractions(mRecommendationProvider);
}
@Test
@@ -604,7 +603,7 @@
consumer.accept(mNetworkScoreCache, null /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -618,7 +617,7 @@
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -632,7 +631,7 @@
consumer.accept(mNetworkScoreCache, -1 /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -646,7 +645,7 @@
consumer.accept(mNetworkScoreCache, "not an int" /*cookie*/);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
- verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -658,7 +657,7 @@
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
- verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
+ verifyNoMoreInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
}
@Test
@@ -676,7 +675,7 @@
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
verify(mNetworkScoreCache).updateScores(filteredList);
- verifyZeroInteractions(mScanResultsFilter);
+ verifyNoMoreInteractions(mScanResultsFilter);
}
@Test
@@ -694,7 +693,7 @@
consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS);
verify(mNetworkScoreCache).updateScores(filteredList);
- verifyZeroInteractions(mCurrentNetworkFilter);
+ verifyNoMoreInteractions(mCurrentNetworkFilter);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 42b84bd..c7c8c58 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -64,7 +64,6 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
@@ -838,7 +837,7 @@
// ...without secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
@Test
@@ -856,7 +855,7 @@
// ...with secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
@Test
@@ -889,7 +888,7 @@
// ...with secure layers included
assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
// No error sent to callback
- verifyZeroInteractions(mMockCallback);
+ verifyNoMoreInteractions(mMockCallback);
}
private void takeScreenshotOfWindow(int windowFlags) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 8253595..2ccd336 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -50,6 +50,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -66,6 +67,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.RemoteAction;
@@ -77,10 +79,12 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.input.KeyGestureEvent;
import android.net.Uri;
@@ -114,6 +118,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.TestUtils;
import com.android.internal.R;
@@ -136,6 +141,8 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.truth.Correspondence;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -188,6 +195,8 @@
DESCRIPTION,
TEST_PENDING_INTENT);
+ private static final int FAKE_SYSTEMUI_UID = 1000;
+
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
private static final String TARGET_MAGNIFICATION = MAGNIFICATION_CONTROLLER_NAME;
private static final ComponentName TARGET_ALWAYS_ON_A11Y_SERVICE =
@@ -207,11 +216,12 @@
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
@Mock private PackageManager mMockPackageManager;
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
@Mock private WindowManagerInternal mMockWindowManagerService;
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@Mock private SystemActionPerformer mMockSystemActionPerformer;
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
- @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
@Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private IBinder mMockBinder;
@@ -234,6 +244,7 @@
private TestableLooper mTestableLooper;
private Handler mHandler;
private FakePermissionEnforcer mFakePermissionEnforcer;
+ private TestDisplayManagerWrapper mTestDisplayManagerWrapper;
@Before
public void setUp() throws Exception {
@@ -246,6 +257,7 @@
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.removeServiceForTest(PermissionEnforcer.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(
WindowManagerInternal.class, mMockWindowManagerService);
LocalServices.addService(
@@ -256,6 +268,12 @@
mInputFilter = mock(FakeInputFilter.class);
mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+ when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn(
+ new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
+ when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(),
+ anyInt())).thenReturn(FAKE_SYSTEMUI_UID);
+ LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+
when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
@@ -273,15 +291,9 @@
eq(UserHandle.USER_CURRENT)))
.thenReturn(mTestableContext.getUserId());
- final ArrayList<Display> displays = new ArrayList<>();
- final Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(),
- Display.DEFAULT_DISPLAY, new DisplayInfo(),
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- final Display testDisplay = new Display(DisplayManagerGlobal.getInstance(), TEST_DISPLAY,
- new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- displays.add(defaultDisplay);
- displays.add(testDisplay);
- when(mMockA11yDisplayListener.getValidDisplayList()).thenReturn(displays);
+ mTestDisplayManagerWrapper = new TestDisplayManagerWrapper(mTestableContext);
+ mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
mA11yms = new AccessibilityManagerService(
mTestableContext,
@@ -290,7 +302,7 @@
mMockSecurityPolicy,
mMockSystemActionPerformer,
mMockA11yWindowManager,
- mMockA11yDisplayListener,
+ mTestDisplayManagerWrapper,
mMockMagnificationController,
mInputFilter,
mProxyManager,
@@ -2309,6 +2321,73 @@
mA11yms.getCurrentUserIdLocked())).isEmpty();
}
+ @Test
+ public void displayListReturnsDisplays() {
+ mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(
+ Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_WIFI,
+ Display.TYPE_OVERLAY,
+ Display.TYPE_VIRTUAL
+ );
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // In #setUp() we already have TYPE_INTERNAL and TYPE_EXTERNAL. Call the rest.
+ for (int i = 2; i < mTestDisplayManagerWrapper.mDisplays.size(); i++) {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(
+ mTestDisplayManagerWrapper.mDisplays.get(i).getDisplayId());
+ }
+ });
+
+ List<Display> displays = mA11yms.getValidDisplayList();
+ assertThat(displays).hasSize(5);
+ assertThat(displays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .containsExactly(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_WIFI,
+ Display.TYPE_OVERLAY,
+ Display.TYPE_VIRTUAL);
+ }
+
+ @Test
+ public void displayListReturnsDisplays_excludesVirtualPrivate() {
+ // Add a private virtual display whose uid is different from systemui.
+ final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
+ displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID + 100));
+ mTestDisplayManagerWrapper.mDisplays = displays;
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+ });
+
+ List<Display> validDisplays = mA11yms.getValidDisplayList();
+ assertThat(validDisplays).hasSize(2);
+ assertThat(validDisplays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .doesNotContain(Display.TYPE_VIRTUAL);
+ }
+
+ @Test
+ public void displayListReturnsDisplays_includesVirtualSystemUIPrivate() {
+ // Add a private virtual display whose uid is systemui.
+ final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+ Display.TYPE_EXTERNAL);
+ displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID));
+ mTestDisplayManagerWrapper.mDisplays = displays;
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+ });
+
+ List<Display> validDisplays = mA11yms.getValidDisplayList();
+ assertThat(validDisplays).hasSize(3);
+ assertThat(validDisplays)
+ .comparingElementsUsing(
+ Correspondence.transforming(Display::getType, "has a type of"))
+ .contains(Display.TYPE_VIRTUAL);
+ }
+
private Set<String> readStringsFromSetting(String setting) {
final Set<String> result = new ArraySet<>();
mA11yms.readColonDelimitedSettingToSet(
@@ -2422,6 +2501,27 @@
});
}
+ private static List<Display> createFakeDisplayList(int... types) {
+ final ArrayList<Display> displays = new ArrayList<>();
+ for (int i = 0; i < types.length; i++) {
+ final DisplayInfo info = new DisplayInfo();
+ info.type = types[i];
+ final Display display = new Display(DisplayManagerGlobal.getInstance(),
+ i, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ displays.add(display);
+ }
+ return displays;
+ }
+
+ private static Display createFakeVirtualPrivateDisplay(int displayId, int uid) {
+ final DisplayInfo info = new DisplayInfo();
+ info.type = Display.TYPE_VIRTUAL;
+ info.flags |= Display.FLAG_PRIVATE;
+ info.ownerUid = uid;
+ return new Display(DisplayManagerGlobal.getInstance(),
+ displayId, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ }
+
public static class FakeInputFilter extends AccessibilityInputFilter {
FakeInputFilter(Context context,
AccessibilityManagerService service) {
@@ -2506,4 +2606,35 @@
Set<String> setting = readStringsFromSetting(ShortcutUtils.convertToKey(shortcutType));
assertThat(setting).containsExactlyElementsIn(value);
}
+
+ private static class TestDisplayManagerWrapper extends
+ AccessibilityDisplayListener.DisplayManagerWrapper {
+ List<Display> mDisplays;
+ DisplayManager.DisplayListener mRegisteredListener;
+
+ TestDisplayManagerWrapper(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Display[] getDisplays() {
+ return mDisplays.toArray(new Display[0]);
+ }
+
+ @Override
+ public Display getDisplay(int displayId) {
+ for (final Display display : mDisplays) {
+ if (display.getDisplayId() == displayId) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ @Nullable Handler handler) {
+ mRegisteredListener = listener;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 96ae102e5..d0dc2cb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -24,7 +24,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.FingerprintGestureController;
@@ -86,7 +86,7 @@
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGestureDetectionActiveChanged(true);
mFingerprintGestureController.onGestureDetectionActiveChanged(false);
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
}
@Test
@@ -118,7 +118,7 @@
mFingerprintGestureController.onGestureDetectionActiveChanged(true);
mFingerprintGestureController.onGestureDetectionActiveChanged(false);
assertFalse(messageCapturingHandler.hasMessages());
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
messageCapturingHandler.removeAllMessages();
}
@@ -135,7 +135,7 @@
mFingerprintGestureController.unregisterFingerprintGestureCallback(
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
}
@Test
@@ -159,7 +159,7 @@
mMockFingerprintGestureCallback);
mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
assertFalse(messageCapturingHandler.hasMessages());
- verifyZeroInteractions(mMockFingerprintGestureCallback);
+ verifyNoMoreInteractions(mMockFingerprintGestureCallback);
messageCapturingHandler.removeAllMessages();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
index 63c572a..3565244 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -36,6 +36,8 @@
import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -67,6 +69,8 @@
public class HearingDevicePhoneCallNotificationControllerTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
@@ -118,6 +122,7 @@
AudioManager.DEVICE_OUT_BLE_HEADSET);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{hapDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -132,6 +137,7 @@
AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(a2dpDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -146,6 +152,7 @@
AudioManager.DEVICE_OUT_BLE_HEADSET);
when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
new AudioDeviceInfo[]{hapDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -155,6 +162,51 @@
eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH));
}
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+ public void onCallStateChanged_nonHearingDevice_offHookThenIdle_callAddAndRemoveListener() {
+ final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
+
+ verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+ listenerCaptor.capture());
+ verify(mAudioManager).removeOnCommunicationDeviceChangedListener(
+ eq(listenerCaptor.getValue()));
+ }
+
+
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+ public void onCallStateChanged_hearingDeviceFromCommunicationDeviceChanged_showNotification() {
+ final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+ AudioDeviceInfo hapDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLE_HEADSET);
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+ listenerCaptor.capture());
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{hapDeviceInfo});
+ listenerCaptor.getValue().onCommunicationDeviceChanged(hapDeviceInfo);
+
+ verify(mNotificationManager).notify(
+ eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
+ }
+
private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
doReturn(type).when(audioDevicePort).type();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index c3256ca..186f742 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -27,7 +27,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
@@ -159,7 +159,7 @@
mFilter1SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -189,7 +189,7 @@
mFilter2SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -261,7 +261,7 @@
mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
mFilter2SequenceCaptor.getValue());
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -278,7 +278,7 @@
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -293,7 +293,7 @@
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -327,7 +327,7 @@
mFilter1SequenceCaptor.getValue());
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
@@ -344,7 +344,7 @@
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verifyZeroInteractions(mMockPowerManagerService);
+ verifyNoMoreInteractions(mMockPowerManagerService);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 9b8e619..367f2d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -37,7 +37,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import android.accessibilityservice.GestureDescription.GestureStep;
@@ -223,7 +222,7 @@
verifyNoMoreInteractions(next);
reset(next);
- verifyZeroInteractions(mServiceInterface);
+ verifyNoMoreInteractions(mServiceInterface);
mMessageCapturingHandler.sendOneMessage(); // Send a motion event
verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index a048238..99c922c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -75,12 +75,13 @@
private static class MotionEventCaptor extends BaseEventStreamTransformation {
public MotionEvent downEvent;
-
+ public int eventCount = 0;
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downEvent = event;
+ eventCount++;
break;
}
}
@@ -922,6 +923,41 @@
mController.onKeyEvent(keyEvent, /* policyFlags= */ 0);
}
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void sendClick_clickType_doubleclick_triggerClickTwice() {
+ MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+ mController.setNext(motionEventCaptor);
+
+ injectFakeMouseActionHoverMoveEvent();
+ // Set delay to zero so click is scheduled to run immediately.
+ mController.mClickScheduler.updateDelay(0);
+
+ // Set click type to double click.
+ mController.clickPanelController.handleAutoclickTypeChange(
+ AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK);
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+ mTestableLooper.processAllMessages();
+
+ // Verify left click sent.
+ assertThat(motionEventCaptor.downEvent).isNotNull();
+ assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+ MotionEvent.BUTTON_PRIMARY);
+ assertThat(motionEventCaptor.eventCount).isEqualTo(2);
+ }
+
private MotionEvent getFakeMotionHoverMoveEvent() {
return MotionEvent.obtain(
/* downTime= */ 0,
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 3475c8f..20a95e9 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -34,7 +34,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
@@ -196,7 +195,7 @@
@Test
public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
- verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
+ verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index 7a77033..f4e8717 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,7 +24,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.hardware.biometrics.BiometricFaceConstants;
@@ -172,7 +172,7 @@
client.onInteractionDetected();
client.stopHalOperation();
- verifyZeroInteractions(mVibrator);
+ verifyNoMoreInteractions(mVibrator);
}
private FaceDetectClient createClient() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index 67fc564..2e07cd8a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -22,18 +22,25 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.AttributionSource;
import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -49,6 +56,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -65,6 +73,9 @@
private static final int VIRTUAL_SENSOR_TYPE = Sensor.TYPE_ACCELEROMETER;
+ private static final float[] ADDITIONAL_INFO_VALUES_1 = new float[] {1.2f, 3.4f};
+ private static final float[] ADDITIONAL_INFO_VALUES_2 = new float[] {5.6f, 7.8f};
+
@Mock
private SensorManagerInternal mSensorManagerInternalMock;
@Mock
@@ -155,6 +166,53 @@
}
@Test
+ public void sendSensorAdditionalInfo_invalidToken_throwsException() throws Exception {
+ SensorController sensorController = doCreateSensorSuccessfully();
+
+ final VirtualSensorAdditionalInfo info =
+ new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+ .addValues(ADDITIONAL_INFO_VALUES_1)
+ .addValues(ADDITIONAL_INFO_VALUES_2)
+ .build();
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> sensorController.sendSensorAdditionalInfo(
+ new Binder("invalidSensorToken"), info));
+ }
+
+ @Test
+ public void sendSensorAdditionalInfo_success() throws Exception {
+ SensorController sensorController = doCreateSensorSuccessfully();
+
+ clearInvocations(mSensorManagerInternalMock);
+ when(mSensorManagerInternalMock.sendSensorAdditionalInfo(
+ anyInt(), anyInt(), anyInt(), anyLong(), any()))
+ .thenReturn(true);
+ IBinder token = Iterables.getOnlyElement(sensorController.getSensorDescriptors().keySet());
+
+ final VirtualSensorAdditionalInfo info =
+ new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+ .addValues(ADDITIONAL_INFO_VALUES_1)
+ .addValues(ADDITIONAL_INFO_VALUES_2)
+ .build();
+ sensorController.sendSensorAdditionalInfo(token, info);
+
+ InOrder inOrder = inOrder(mSensorManagerInternalMock);
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_BEGIN),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_1));
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+ /*serial=*/ eq(1), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_2));
+ inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+ eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_END),
+ /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+ }
+
+ @Test
public void close_unregistersSensors() throws Exception {
SensorController sensorController = doCreateSensorSuccessfully();
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index b445226..4fa75b9 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -23,7 +23,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -132,8 +132,8 @@
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -147,7 +147,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -157,8 +157,8 @@
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -172,7 +172,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -187,7 +187,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager, never()).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -203,7 +203,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -216,8 +216,8 @@
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -230,8 +230,8 @@
assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionAllowlistManager);
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -248,7 +248,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -265,7 +265,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
verify(mMockContentProtectionAllowlistManager).start(anyLong());
verify(mMockContentProtectionAllowlistManager).stop();
- verifyZeroInteractions(mMockContentProtectionConsentManager);
+ verifyNoMoreInteractions(mMockContentProtectionConsentManager);
}
@Test
@@ -513,7 +513,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -528,7 +528,7 @@
assertThat(mContentProtectionServiceInfosCreated).isEqualTo(1);
assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
index 195ab68..9d37b99 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
@@ -24,7 +24,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.Handler;
@@ -98,10 +98,10 @@
@Test
public void constructor() {
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -110,10 +110,10 @@
mTestLooper.dispatchAll();
assertThat(mHandler.hasMessagesOrCallbacks()).isTrue();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -126,8 +126,8 @@
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -142,8 +142,8 @@
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -153,11 +153,11 @@
mContentProtectionAllowlistManager.stop();
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -169,11 +169,11 @@
mContentProtectionAllowlistManager.stop();
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -188,8 +188,8 @@
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -205,8 +205,8 @@
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -223,8 +223,8 @@
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor, times(2)).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -232,10 +232,10 @@
boolean actual = mContentProtectionAllowlistManager.isAllowed(FIRST_PACKAGE_NAME);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -248,9 +248,9 @@
boolean actual = manager.isAllowed(SECOND_PACKAGE_NAME);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -263,9 +263,9 @@
boolean actual = manager.isAllowed(FIRST_PACKAGE_NAME);
assertThat(actual).isTrue();
- verifyZeroInteractions(mMockContentCaptureManagerService);
- verifyZeroInteractions(mMockPackageMonitor);
- verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockContentCaptureManagerService);
+ verifyNoMoreInteractions(mMockPackageMonitor);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
}
@Test
@@ -276,8 +276,8 @@
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -291,7 +291,7 @@
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -309,7 +309,7 @@
// Does not rethrow
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -321,8 +321,8 @@
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
- verifyZeroInteractions(mMockRemoteContentProtectionService);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -338,7 +338,7 @@
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockRemoteContentProtectionService)
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
@@ -355,7 +355,7 @@
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
verify(mMockRemoteContentProtectionService, times(2))
.onUpdateAllowlistRequest(mMockAllowlistCallback);
- verifyZeroInteractions(mMockAllowlistCallback);
+ verifyNoMoreInteractions(mMockAllowlistCallback);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
index b012aaa..cd36a18 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -27,7 +27,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyCache;
@@ -112,8 +112,8 @@
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -125,8 +125,8 @@
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -138,8 +138,8 @@
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -152,7 +152,7 @@
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -166,7 +166,7 @@
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -179,7 +179,7 @@
assertThat(actual).isFalse();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -192,7 +192,7 @@
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -289,8 +289,8 @@
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -302,8 +302,8 @@
boolean actual = manager.isConsentGranted(TEST_USER_ID);
assertThat(actual).isFalse();
- verifyZeroInteractions(mMockDevicePolicyManagerInternal);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -316,7 +316,7 @@
assertThat(actual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -339,7 +339,7 @@
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -362,7 +362,7 @@
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -385,7 +385,7 @@
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
@Test
@@ -408,7 +408,7 @@
assertThat(thirdActual).isTrue();
verify(mMockDevicePolicyManagerInternal, times(3)).isUserOrganizationManaged(TEST_USER_ID);
- verifyZeroInteractions(mMockDevicePolicyCache);
+ verifyNoMoreInteractions(mMockDevicePolicyCache);
}
private void putGlobalSettings(String key, int value) {
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
index 6a7e286..563a679 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
@@ -20,7 +20,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -87,7 +87,7 @@
@Test
public void doesNotAutoConnect() {
assertThat(mConnectCallCount).isEqualTo(0);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
}
@Test
@@ -124,7 +124,7 @@
mRemoteContentProtectionService.onServiceConnectionStatusChanged(
mMockContentProtectionService, /* isConnected= */ true);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
assertThat(mConnectCallCount).isEqualTo(0);
}
@@ -133,7 +133,7 @@
mRemoteContentProtectionService.onServiceConnectionStatusChanged(
mMockContentProtectionService, /* isConnected= */ false);
- verifyZeroInteractions(mMockContentProtectionService);
+ verifyNoMoreInteractions(mMockContentProtectionService);
assertThat(mConnectCallCount).isEqualTo(0);
}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 0f3f27a..9e98af3 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -21,7 +21,7 @@
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -207,7 +207,7 @@
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
"unsupportedKey", providerPendingIntentResponse);
- verifyZeroInteractions(mGetRequestSession);
+ verifyNoMoreInteractions(mGetRequestSession);
}
@Test
@@ -216,7 +216,7 @@
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY, null);
- verifyZeroInteractions(mGetRequestSession);
+ verifyNoMoreInteractions(mGetRequestSession);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 01bcc25..c50c623 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -88,7 +88,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.testng.Assert.assertThrows;
@@ -5305,7 +5304,7 @@
// both the user restriction and the policy were set by the PO.
verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
MANAGED_PROFILE_USER_ID);
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
}
@Test
@@ -5339,7 +5338,7 @@
// not wiped.
verify(getServices().userManagerInternal, never())
.removeUserEvenWhenDisallowed(anyInt());
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
}
@Test
@@ -5380,7 +5379,7 @@
dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
// DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped.
- verifyZeroInteractions(getServices().recoverySystem);
+ verifyNoMoreInteractions(getServices().recoverySystem);
verify(getServices().userManagerInternal, never())
.removeUserEvenWhenDisallowed(anyInt());
}
@@ -7535,7 +7534,7 @@
verify(getServices().notificationManager, never())
.notify(anyInt(), any(Notification.class));
// Apps shouldn't be suspended.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
setUserUnlocked(CALLER_USER_HANDLE, false);
@@ -7548,7 +7547,7 @@
verify(getServices().notificationManager, never())
.notify(anyInt(), any(Notification.class));
// Apps shouldn't be suspended.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
// Pretend the alarm went off.
@@ -7561,7 +7560,7 @@
verify(getServices().notificationManager, times(1))
.notifyAsUser(any(), anyInt(), any(), any());
// Apps shouldn't be suspended yet.
- verifyZeroInteractions(getServices().ipackageManager);
+ verifyNoMoreInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
clearInvocations(getServices().notificationManager);
@@ -7570,7 +7569,7 @@
sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
// Verify the alarm was not set.
- verifyZeroInteractions(getServices().alarmManager);
+ verifyNoMoreInteractions(getServices().alarmManager);
// Now the user should see a notification about suspended apps.
verify(getServices().notificationManager, times(1))
.notifyAsUser(any(), anyInt(), any(), any());
@@ -8754,7 +8753,7 @@
sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);
// Verify that EuiccManager was not called to delete the subscription.
- verifyZeroInteractions(getServices().euiccManager);
+ verifyNoMoreInteractions(getServices().euiccManager);
}
private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index e20f1e7..a39f071 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -31,7 +31,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.app.ActivityManagerInternal;
import android.content.Context;
@@ -226,7 +226,7 @@
assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
// getApplicationLocales should be never be invoked if not a system app.
- verifyZeroInteractions(mMockActivityTaskManager);
+ verifyNoMoreInteractions(mMockActivityTaskManager);
// Broadcast should be never sent if not a system app.
verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
// It shouldn't write to the file if not a system app.
@@ -244,7 +244,7 @@
Binder.getCallingUid());
// getApplicationLocales should be never be invoked if not installer is not present.
- verifyZeroInteractions(mMockActivityTaskManager);
+ verifyNoMoreInteractions(mMockActivityTaskManager);
// Broadcast should be never sent if installer is not present.
verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
// It shouldn't write to file if no installer present.
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index a58a9cd..4a05ea6 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -48,7 +48,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -488,7 +488,7 @@
projection.stop(StopReason.STOP_UNKNOWN);
- verifyZeroInteractions(mMediaProjectionMetricsLogger);
+ verifyNoMoreInteractions(mMediaProjectionMetricsLogger);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
index d55f967..2ed2704 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
@@ -23,7 +23,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
@@ -273,7 +273,7 @@
assertThat(mMessagesForUid).isEmpty();
assertThat(mWriteTriggered).isFalse();
- verifyZeroInteractions(mPM);
+ verifyNoMoreInteractions(mPM);
}
@Test
diff --git a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
index c8afb78..630a7e4 100644
--- a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
@@ -23,7 +23,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AlarmManager;
@@ -126,7 +126,7 @@
locationListener.onLocationChanged(location);
verify(mMockLocationManager).removeUpdates(locationListener);
- verifyZeroInteractions(mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockTimeDetectorInternal);
verify(mMockAlarmManager).set(
eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
anyLong(),
@@ -150,7 +150,7 @@
// Verify the service returned to location listening.
verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
- verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
}
// Tests what happens when a call is made to startGnssListeningInternal() when service is
@@ -172,7 +172,7 @@
// listening again.
verify(mMockAlarmManager).cancel(alarmListenerCaptor.getValue());
verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
- verifyZeroInteractions(mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockTimeDetectorInternal);
}
private void advanceServiceToSleepingState(
@@ -190,7 +190,7 @@
any(), any(), any(), locationListenerCaptor.capture());
LocationListener locationListener = locationListenerCaptor.getValue();
Location location = new Location(LocationManager.GPS_PROVIDER);
- verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+ verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
locationListener.onLocationChanged(location);
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 66d7611..d34d74d 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -11,13 +11,8 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-android_test {
- name: "FrameworksUiServicesTests",
-
- // Include test java files
- srcs: [
- "src/**/*.java",
- ],
+java_defaults {
+ name: "FrameworksUiServicesTests-defaults",
static_libs: [
"compatibility-device-util-axt-minus-dexmaker",
@@ -95,12 +90,72 @@
javacflags: ["-parameters"],
}
-test_module_config {
- name: "FrameworksUiServicesTests_notification",
- base: "FrameworksUiServicesTests",
- test_suites: [
- "automotive-tests",
- "device-tests",
+// Utility files used by multiple tests
+filegroup {
+ name: "shared-srcs",
+ srcs: [
+ "src/android/app/ExampleActivity.java",
+ "src/android/app/NotificationSystemUtil.java",
+ "src/com/android/frameworks/tests/uiservices/DummyProvider.java",
+ "src/com/android/internal/logging/InstanceIdSequenceFake.java",
+ "src/com/android/server/UiServiceTestCase.java",
+ "src/com/android/server/notification/ZenChangeOrigin.java",
+ "src/com/android/server/notification/ZenModeEventLoggerFake.java",
],
- exclude_annotations: ["androidx.test.filters.LargeTest"],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "notification-srcs",
+ srcs: [
+ "src/**/Notification*.java",
+ "src/com/android/server/notification/*.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "notification-zen-srcs",
+ srcs: [
+ "src/android/app/NotificationManagerZenTest.java",
+ "src/com/android/server/notification/Zen*Test.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+android_test {
+ name: "FrameworksUiServicesTests",
+
+ // Include test java files but not the notification & zen ones which are separated
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ exclude_srcs: [
+ ":notification-srcs",
+ ":notification-zen-srcs",
+ ],
+
+ defaults: ["FrameworksUiServicesTests-defaults"],
+}
+
+android_test {
+ name: "FrameworksUiServicesNotificationTests",
+ srcs: [
+ ":notification-srcs",
+ ":shared-srcs",
+ ],
+ exclude_srcs: [":notification-zen-srcs"],
+ defaults: ["FrameworksUiServicesTests-defaults"],
+ test_config: "notification-tests.xml",
+}
+
+android_test {
+ name: "FrameworksUiServicesZenTests",
+ srcs: [
+ ":notification-zen-srcs",
+ ":shared-srcs",
+ ],
+ defaults: ["FrameworksUiServicesTests-defaults"],
+ test_config: "notification-zen-tests.xml",
}
diff --git a/services/tests/uiservicestests/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
index 11e8f09..93c8c72 100644
--- a/services/tests/uiservicestests/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Runs Frameworks UI Services Tests.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksUiServicesTests.apk" />
</target_preparer>
diff --git a/services/tests/uiservicestests/notification-tests.xml b/services/tests/uiservicestests/notification-tests.xml
new file mode 100644
index 0000000..acfd844
--- /dev/null
+++ b/services/tests/uiservicestests/notification-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (notifications subset).">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksUiServicesNotificationTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="FrameworksUiServicesNotificationTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.uiservices" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/uiservicestests/notification-zen-tests.xml b/services/tests/uiservicestests/notification-zen-tests.xml
new file mode 100644
index 0000000..01d8aab
--- /dev/null
+++ b/services/tests/uiservicestests/notification-zen-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (zen mode subset).">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksUiServicesZenTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="FrameworksUiServicesZenTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.uiservices" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 9930c9f..7b1ce44 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -62,7 +62,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -992,7 +991,7 @@
() -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
verify(mContext, never()).enforceCallingPermission(
eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
- verifyZeroInteractions(mBinder);
+ verifyNoMoreInteractions(mBinder);
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
}
@@ -1008,7 +1007,7 @@
() -> mService.requestProjection(mBinder, multipleProjectionTypes, PACKAGE_NAME));
verify(mContext, never()).enforceCallingPermission(
eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
- verifyZeroInteractions(mBinder);
+ verifyNoMoreInteractions(mBinder);
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
}
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 6b989cb..b332331 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -38,7 +38,6 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.Condition;
-import com.android.internal.R;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -47,8 +46,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
-
public class ConditionProvidersTest extends UiServiceTestCase {
private ConditionProviders mProviders;
@@ -172,15 +169,4 @@
assertTrue(mProviders.getApproved(userId, true).isEmpty());
}
-
- @Test
- public void getDefaultDndAccessPackages_returnsPackages() {
- mContext.getOrCreateTestableResources().addOverride(
- R.string.config_defaultDndAccessPackages,
- "com.example.a:com.example.b::::com.example.c");
-
- List<String> packages = ConditionProviders.getDefaultDndAccessPackages(mContext);
-
- assertThat(packages).containsExactly("com.example.a", "com.example.b", "com.example.c");
- }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 5ce9a3e..8023bdd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -36,7 +36,6 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.KeyguardManager;
@@ -184,7 +183,7 @@
mApplier.apply(noEffects, ORIGIN_USER_IN_SYSTEMUI);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
- verifyZeroInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
+ verifyNoMoreInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
}
@Test
@@ -252,8 +251,8 @@
// Wallpaper dimming was undone, Grayscale was applied, nothing else was touched.
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
verify(mColorDisplayManager).setSaturationLevel(eq(0));
- verifyZeroInteractions(mPowerManager);
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mPowerManager);
+ verifyNoMoreInteractions(mUiModeManager);
}
@Test
@@ -269,7 +268,7 @@
ORIGIN_APP);
// Effect was not yet applied, but a broadcast receiver was registered.
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mUiModeManager);
verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
intentFilterCaptor.capture(), anyInt());
assertThat(intentFilterCaptor.getValue().getAction(0)).isEqualTo(Intent.ACTION_SCREEN_OFF);
@@ -337,7 +336,7 @@
origin.value());
// Effect was not applied, will be on next screen-off.
- verifyZeroInteractions(mUiModeManager);
+ verifyNoMoreInteractions(mUiModeManager);
verify(mContext).registerReceiver(any(),
argThat(filter -> Intent.ACTION_SCREEN_OFF.equals(filter.getAction(0))),
anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 46be9a5..1114365 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -60,7 +60,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.annotation.SuppressLint;
@@ -313,7 +313,7 @@
getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -327,7 +327,7 @@
}
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -340,7 +340,7 @@
}
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -353,7 +353,7 @@
mGroupHelper.onNotificationPosted(
getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a", false),
false);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1744,7 +1744,7 @@
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1759,7 +1759,7 @@
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1775,7 +1775,7 @@
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1791,7 +1791,7 @@
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1811,7 +1811,7 @@
String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1830,7 +1830,7 @@
String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.of(7), "testGrp", true);
notificationList.add(r);
mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1853,7 +1853,7 @@
String.valueOf(AUTOGROUP_AT_COUNT + 1), UserHandle.SYSTEM, "testGrp", false);
notificationList.add(child);
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -1877,7 +1877,7 @@
notificationList.add(child);
summaryByGroup.put(summary.getGroupKey(), summary);
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -2209,7 +2209,7 @@
childrenToRemove.add(child);
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove all child notifications from the valid group => summary without children
Mockito.reset(mCallback);
@@ -2273,7 +2273,7 @@
}
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove some child notifications from the valid group, transform into a singleton group
Mockito.reset(mCallback);
@@ -2329,7 +2329,7 @@
notificationList.add(child);
}
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
// Remove all child notifications from the valid group => summary without children
Mockito.reset(mCallback);
@@ -2343,7 +2343,7 @@
mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
summaryByGroup);
// Check that nothing was force grouped
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -3837,7 +3837,7 @@
mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
}
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@@ -3861,7 +3861,7 @@
mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
}
// FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS is disabled => don't force group
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
@@ -4498,7 +4498,7 @@
mGroupHelper.onNotificationPostedWithDelay(extra, notifList, summaryByGroupKey);
// no autogrouping should have occurred
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index a02f628..43228f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -96,7 +96,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
@@ -6372,7 +6372,7 @@
NotificationChannel same = new NotificationChannel("id", "Bah", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_P, 0, same, true, false, 0, false);
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
@@ -6398,7 +6398,7 @@
mHelper.updateNotificationChannel(PKG_P, 0, same, false, 0, false);
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
@@ -6412,7 +6412,7 @@
public void setShowBadge_same_doesNotRequestSort() {
mHelper.setShowBadge(PKG_P, 0, true); // true == DEFAULT_SHOW_BADGE
- verifyZeroInteractions(mHandler);
+ verifyNoMoreInteractions(mHandler);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
deleted file mode 100644
index 154a905..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.server.UiServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ZenConfigTrimmerTest extends UiServiceTestCase {
-
- private static final String TRUSTED_PACKAGE = "com.trust.me";
- private static final int ONE_PERCENT = 1_500;
-
- private ZenConfigTrimmer mTrimmer;
-
- @Before
- public void setUp() {
- mContext.getOrCreateTestableResources().addOverride(
- R.string.config_defaultDndAccessPackages, TRUSTED_PACKAGE);
-
- mTrimmer = new ZenConfigTrimmer(mContext);
- }
-
- @Test
- public void trimToMaximumSize_belowMax_untouched() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "4", "5");
- }
-
- @Test
- public void trimToMaximumSize_exceedsMax_removesAllRulesOfLargestPackages() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
- addZenRule(config, "7", "pkg4", 38 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "6");
- assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
- .containsExactly("pkg1", "pkg3");
- }
-
- @Test
- public void trimToMaximumSize_keepsRulesFromTrustedPackages() {
- ZenModeConfig config = new ZenModeConfig();
- addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
- addZenRule(config, "4", TRUSTED_PACKAGE, 60 * ONE_PERCENT);
- addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
- addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
-
- mTrimmer.trimToMaximumSize(config);
-
- assertThat(config.automaticRules.keySet()).containsExactly("4", "5");
- assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
- .containsExactly(TRUSTED_PACKAGE, "pkg2");
- }
-
- /**
- * Create a ZenRule that, when serialized to a Parcel, will take <em>approximately</em>
- * {@code desiredSize} bytes (within 100 bytes). Try to make the tests not rely on a very tight
- * fit.
- */
- private static void addZenRule(ZenModeConfig config, String id, String pkg, int desiredSize) {
- ZenRule rule = new ZenRule();
- rule.id = id;
- rule.pkg = pkg;
- config.automaticRules.put(id, rule);
-
- // Make the ZenRule as large as desired. Not to the exact byte, because otherwise this
- // test would have to be adjusted whenever we change the parceling of ZenRule in any way.
- // (Still might need adjustment if we change the serialization _significantly_).
- int nameLength = desiredSize - id.length() - pkg.length() - 232;
- rule.name = "A".repeat(nameLength);
-
- Parcel verification = Parcel.obtain();
- try {
- verification.writeParcelable(rule, 0);
- assertThat(verification.dataSize()).isWithin(100).of(desiredSize);
- } finally {
- verification.recycle();
- }
- }
-}
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 5377102..bfce647 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -90,7 +90,6 @@
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
-import static com.android.server.notification.Flags.FLAG_LIMIT_ZEN_CONFIG_SIZE;
import static com.android.server.notification.Flags.FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING;
import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
@@ -238,7 +237,6 @@
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(ParameterizedAndroidJunit4.class)
-@EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE) // Should be parameterization, but off path does nothing.
@TestableLooper.RunWithLooper
public class ZenModeHelperTest extends UiServiceTestCase {
@@ -7485,45 +7483,6 @@
assertThat(getZenRule(ruleId).lastActivation).isNull();
}
- @Test
- @EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE)
- public void addAutomaticZenRule_trimsConfiguration() {
- mZenModeHelper.mConfig.automaticRules.clear();
- AutomaticZenRule smallRule = new AutomaticZenRule.Builder("Reasonable", CONDITION_ID)
- .setConfigurationActivity(new ComponentName(mPkg, "cls"))
- .build();
- AutomaticZenRule systemRule = new AutomaticZenRule.Builder("System", CONDITION_ID)
- .setOwner(new ComponentName("android", "ScheduleConditionProvider"))
- .build();
-
- AutomaticZenRule bigRule = new AutomaticZenRule.Builder("Yuge", CONDITION_ID)
- .setConfigurationActivity(new ComponentName("evil.package", "cls"))
- .setTriggerDescription("0123456789".repeat(6000)) // ~60k bytes utf16.
- .build();
-
- String systemRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android",
- systemRule, ORIGIN_SYSTEM, "add", SYSTEM_UID);
- String smallRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, smallRule,
- ORIGIN_APP, "add", CUSTOM_PKG_UID);
- String bigRuleId1 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId, bigRuleId1);
-
- String bigRuleId2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId, bigRuleId1, bigRuleId2);
-
- // This should go over the threshold
- String bigRuleId3 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
- bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-
- // Rules from evil.package are gone.
- assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
- systemRuleId, smallRuleId);
- }
-
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/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 79e272b..01698b5 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -33,7 +33,6 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -133,7 +132,7 @@
mVibratorControlService.registerVibratorController(controller1);
mVibratorControlService.unregisterVibratorController(controller2);
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
assertThat(controller1.isLinkedToDeath).isTrue();
}
@@ -187,7 +186,7 @@
verify(mStatsLoggerMock).logVibrationParamResponseIgnored();
verifyNoMoreInteractions(mStatsLoggerMock);
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test(expected = IllegalArgumentException.class)
@@ -242,7 +241,7 @@
mFakeVibratorController);
verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test(expected = IllegalArgumentException.class)
@@ -280,7 +279,7 @@
mFakeVibratorController);
verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
- verifyZeroInteractions(mMockVibrationScaler);
+ verifyNoMoreInteractions(mMockVibrationScaler);
}
@Test
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
index 9a59ede..011971d 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
@@ -28,7 +28,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.media.soundtrigger.RecognitionStatus;
import android.media.soundtrigger_middleware.RecognitionEventSys;
@@ -76,7 +75,7 @@
assertEquals(event.halEventReceivedMillis, -1);
assertEquals(event.recognitionEvent.status, RecognitionStatus.ABORTED);
assertFalse(event.recognitionEvent.recognitionStillActive);
- verifyZeroInteractions(mGlobalCallback);
+ verifyNoMoreInteractions(mGlobalCallback);
clearInvocations(callback, mUnderlying);
mNotifier.setActive(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8992c2e..7f242de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2021,8 +2021,6 @@
display.setFixedRotationLaunchingAppUnchecked(activity);
displayRotation.updateRotationUnchecked(true /* forceUpdate */);
- assertTrue(displayRotation.isRotatingSeamlessly());
-
// The launching rotated app should not be cleared when waiting for remote rotation.
display.continueUpdateOrientationForDiffOrienLaunchingApp();
assertTrue(display.isFixedRotationLaunchingApp(activity));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 48731cb..00b617e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -383,7 +383,7 @@
spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAspectRatioOverrides())
- .isUserFullscreenOverrideEnabled();
+ .hasFullscreenOverride();
final int desiredWidth =
(int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -411,7 +411,7 @@
spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
doReturn(true).when(
mActivity.mAppCompatController.getAspectRatioOverrides())
- .isSystemOverrideToFullscreenEnabled();
+ .hasFullscreenOverride();
final int desiredWidth =
(int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -1170,6 +1170,32 @@
@Test
@EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+ public void testOptionsBoundsSet_flexibleLaunchSizeWithFullscreenOverride_noModifications() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchBounds(new Rect(
+ DISPLAY_STABLE_BOUNDS.left,
+ DISPLAY_STABLE_BOUNDS.top,
+ /* right = */ 500,
+ /* bottom = */ 500))
+ .setFlexibleLaunchSize(true);
+ spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
+ doReturn(true).when(
+ mActivity.mAppCompatController.getAspectRatioOverrides())
+ .hasFullscreenOverride();
+
+ assertEquals(RESULT_DONE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ assertEquals(options.getLaunchBounds(), mResult.mBounds);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
public void testOptionsBoundsSet_flexibleLaunchSize_boundsSizeModified() {
setupDesktopModeLaunchParamsModifier();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 0af41ea..89aa3b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -56,7 +56,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.pm.ActivityInfo;
@@ -174,7 +174,7 @@
da1.reduceOnAllTaskDisplayAreas(callback2, 0);
da1.getItemFromTaskDisplayAreas(callback3);
- verifyZeroInteractions(da2);
+ verifyNoMoreInteractions(da2);
// Traverse the child if the current DA has type ANY
final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWm, ANY, "DA3");
@@ -207,7 +207,7 @@
da5.reduceOnAllTaskDisplayAreas(callback2, 0);
da5.getItemFromTaskDisplayAreas(callback3);
- verifyZeroInteractions(da6);
+ verifyNoMoreInteractions(da6);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4c81f73..ed00a9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -30,7 +30,10 @@
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.view.Display.FLAG_TRUSTED;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.Surface.ROTATION_0;
@@ -1706,8 +1709,6 @@
app.setVisible(true);
doReturn(false).when(app).inTransition();
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
- mStatusBarWindow.finishSeamlessRotation(t);
- mNavBarWindow.finishSeamlessRotation(t);
// The fixed rotation should be cleared and the new rotation is applied to display.
assertFalse(app.hasFixedRotationTransform());
@@ -2925,6 +2926,63 @@
assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
}
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_shouldShowSystemDecorationsDisplay() {
+ // Set up a non-default display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_notAllowContentModeSwitchDisplay() {
+ // Set up a non-default display without FLAG_ALLOWS_CONTENT_MODE_SWITCH enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_TRUSTED;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_untrustedDisplay() {
+ // Set up a non-default display without FLAG_TRUSTED enabled
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
+ @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ @Test
+ public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+ displayInfo.displayId = DEFAULT_DISPLAY + 1;
+ displayInfo.flags = (FLAG_ALLOWS_CONTENT_MODE_SWITCH | FLAG_TRUSTED);
+ final DisplayContent dc = createNewDisplay(displayInfo);
+
+ spyOn(dc.mDisplay);
+ doReturn(false).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+
+ doReturn(true).when(dc.mDisplay).canHostTasks();
+ dc.onDisplayInfoChangeApplied();
+ assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+ }
+
@EnableFlags(FLAG_ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS)
@Test
public void testForcedDensityRatioSetForExternalDisplays_persistDensityScaleFlagEnabled() {
@@ -2993,23 +3051,6 @@
assertEquals(320, displayContent.mBaseDisplayDensity);
}
- @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
- @Test
- public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
- final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
- displayInfo.displayId = DEFAULT_DISPLAY + 1;
- final DisplayContent dc = createNewDisplay(displayInfo);
-
- spyOn(dc.mDisplay);
- doReturn(false).when(dc.mDisplay).canHostTasks();
- dc.onDisplayInfoChangeApplied();
- assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
-
- doReturn(true).when(dc.mDisplay).canHostTasks();
- dc.onDisplayInfoChangeApplied();
- assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
- }
-
private void removeRootTaskTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index eb6d5cf..e293c2f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -19,6 +19,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
+import static com.android.server.wm.WindowStateAnimator.NO_SURFACE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -244,4 +247,33 @@
verify(displayWindowInsetsController, times(1)).setImeInputTargetRequestedVisibility(
eq(true), any());
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+ public void testOnPostLayout_resetServerVisibilityWhenImeIsNotDrawn() {
+ final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
+ final WindowState inputTarget = newWindowBuilder("app", TYPE_APPLICATION).build();
+ makeWindowVisibleAndDrawn(ime);
+ mImeProvider.setWindowContainer(ime, null, null);
+ mImeProvider.setServerVisible(true);
+ mImeProvider.setClientVisible(true);
+ mImeProvider.updateVisibility();
+ mImeProvider.updateControlForTarget(inputTarget, true /* force */, null /* statsToken */);
+
+ // Calling onPostLayout, as the drawn state is initially false.
+ mImeProvider.onPostLayout();
+ assertTrue(mImeProvider.isSurfaceVisible());
+
+ // Reset window's drawn state
+ ime.mWinAnimator.mDrawState = NO_SURFACE;
+ mImeProvider.onPostLayout();
+ assertFalse(mImeProvider.isServerVisible());
+ assertFalse(mImeProvider.isSurfaceVisible());
+
+ // Set it back to drawn
+ ime.mWinAnimator.mDrawState = HAS_DRAWN;
+ mImeProvider.onPostLayout();
+ assertTrue(mImeProvider.isServerVisible());
+ assertTrue(mImeProvider.isSurfaceVisible());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 8a7e743..2c6884e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -52,7 +52,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static java.lang.Integer.MAX_VALUE;
@@ -1293,7 +1293,7 @@
// Add secondTask to top again
mRecentTasks.add(secondTask);
- verifyZeroInteractions(controller);
+ verifyNoMoreInteractions(controller);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 59ee2f5..5624677 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -23,9 +23,6 @@
import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsSource.ID_IME;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
@@ -88,7 +85,6 @@
import android.content.ContentResolver;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -673,56 +669,6 @@
}
@Test
- public void testSeamlesslyRotateWindow() {
- final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
- final SurfaceControl.Transaction t = spy(StubTransaction.class);
-
- makeWindowVisible(app);
- app.mSurfaceControl = mock(SurfaceControl.class);
- final Rect frame = app.getFrame();
- frame.set(10, 20, 60, 80);
- app.updateSurfacePosition(t);
- assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
- app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true /* requested */);
- assertTrue(app.mSeamlesslyRotated);
-
- // Verify we un-rotate the window state surface.
- final Matrix matrix = new Matrix();
- // Un-rotate 90 deg.
- matrix.setRotate(270);
- // Translate it back to origin.
- matrix.postTranslate(0, mDisplayInfo.logicalWidth);
- verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
-
- // Verify we update the position as well.
- final float[] curSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
- matrix.mapPoints(curSurfacePos);
- verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
-
- app.finishSeamlessRotation(t);
- assertFalse(app.mSeamlesslyRotated);
- assertNull(app.mPendingSeamlessRotate);
-
- // Simulate the case with deferred layout and animation.
- app.resetSurfacePositionForAnimationLeash(t);
- clearInvocations(t);
- mWm.mWindowPlacerLocked.deferLayout();
- app.updateSurfacePosition(t);
- // Because layout is deferred, the position should keep the reset value.
- assertTrue(app.mLastSurfacePosition.equals(0, 0));
-
- app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_270, true /* requested */);
- // The last position must be updated so the surface can be unrotated properly.
- assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
- matrix.setRotate(90);
- matrix.postTranslate(mDisplayInfo.logicalHeight, 0);
- curSurfacePos[0] = frame.left;
- curSurfacePos[1] = frame.top;
- matrix.mapPoints(curSurfacePos);
- verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
- }
-
- @Test
public void testVisibilityChangeSwitchUser() {
final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build();
window.mHasSurface = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index a02c3db..8907a72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -35,16 +37,19 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
+import android.graphics.Matrix;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.window.WindowContext;
@@ -335,6 +340,31 @@
}
@Test
+ public void testSeamlesslyRotate() {
+ final SurfaceControl.Transaction t = mTransaction;
+ final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+ token.mLastSurfacePosition.x = 10;
+ token.mLastSurfacePosition.y = 20;
+ final SeamlessRotator rotator = new SeamlessRotator(ROTATION_0, ROTATION_90,
+ mDisplayContent.getDisplayInfo(), false /* applyFixedTransformationHint */);
+ clearInvocations(t);
+ rotator.unrotate(t, token);
+
+ // Verify surface is un-rotated.
+ final Matrix matrix = new Matrix();
+ // Un-rotate 90 deg.
+ matrix.setRotate(270);
+ // Translate it back to origin.
+ matrix.postTranslate(0, mDisplayInfo.logicalWidth);
+ verify(t).setMatrix(eq(token.mSurfaceControl), eq(matrix), any(float[].class));
+
+ final float[] curSurfacePos = {token.mLastSurfacePosition.x, token.mLastSurfacePosition.y};
+ matrix.mapPoints(curSurfacePos);
+ verify(t).setPosition(eq(token.mSurfaceControl),
+ eq(curSurfacePos[0]), eq(curSurfacePos[1]));
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
public void onDisplayChanged_differentDisplay_reparented() {
final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
index a1d35a7..0749c0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
@@ -22,7 +22,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -118,7 +118,7 @@
@Test
public void trace_discared_whenNotTracing() {
mWindowTracing.logState("where");
- verifyZeroInteractions(mWmMock);
+ verifyNoMoreInteractions(mWmMock);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index 9367941..3da279b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -22,7 +22,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -128,7 +128,7 @@
@Test
public void trace_ignoresLogStateCalls_ifTracingIsDisabled() {
sWindowTracing.logState("where");
- verifyZeroInteractions(sWmMock);
+ verifyNoMoreInteractions(sWmMock);
}
@Test
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 50c5a6b6..1491510 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3918,6 +3918,22 @@
"5g_icon_display_secondary_grace_period_string";
/**
+ * When an NR advanced connection is lost and a Physical Cell ID (PCI) change occurs within
+ * the primary timer{@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING}, delay updating the network
+ * icon.
+ *
+ * <p>This delay is implemented because a rapid PCI change often indicates the device is
+ * switching to a nearby cell tower to quickly restore the NR advanced connection. Displaying
+ * an intermediate network icon (like 4G/LTE) might be misleading if the 5G connection is
+ * restored shortly after. This value sets the delay in seconds; 0 disables the feature.</p>
+ *
+ * @hide
+ */
+ public static final String KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT =
+ "nr_advanced_pci_change_secondary_timer_seconds_int";
+
+
+ /**
* The secondary grace periods in seconds to use if NR advanced icon was shown due to connecting
* to bands specified in {@link #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY}.
*
@@ -11222,6 +11238,7 @@
+ "not_restricted_rrc_con:5G");
sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+ sDefaults.putInt(KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT, 0);
sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index f528263..6e23edf 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -614,7 +614,7 @@
/** @hide */
public static int convertRssiAsuToDBm(int rssiAsu) {
- if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+ if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN || rssiAsu == Integer.MAX_VALUE) {
return CellInfo.UNAVAILABLE;
}
if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index e6515f13..850ce3e 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,7 +38,6 @@
import android.telephony.data.DataCallResponse;
import android.telephony.data.Qos;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
@@ -89,35 +87,30 @@
* Unsupported. The unsupported state is used when the data network cannot support the network
* validation function for the current data connection state.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_UNSUPPORTED = 0;
/**
* Not Requested. The not requested status is used when the data network supports the network
* validation function, but no network validation is being performed yet.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1;
/**
* In progress. The in progress state is used when the network validation process for the data
* network is in progress. This state is followed by either success or failure.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_IN_PROGRESS = 2;
/**
* Success. The Success status is used when network validation has been completed for the data
* network and the result is successful.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_SUCCESS = 3;
/**
* Failure. The Failure status is used when network validation has been completed for the data
* network and the result is failure.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public static final int NETWORK_VALIDATION_FAILURE = 4;
/**
@@ -360,7 +353,6 @@
*
* @return the network validation status of the data call
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @NetworkValidationStatus int getNetworkValidationStatus() {
return mNetworkValidationStatus;
}
@@ -615,7 +607,6 @@
* @param networkValidationStatus the network validation status of the data call
* @return The builder
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @NonNull Builder setNetworkValidationStatus(
@NetworkValidationStatus int networkValidationStatus) {
mNetworkValidationStatus = networkValidationStatus;
diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
index 7356cdc..42d09cf 100644
--- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
+++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
@@ -31,7 +31,6 @@
import android.telephony.ims.ImsManager;
import android.telephony.satellite.SatelliteManager;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
@@ -77,9 +76,6 @@
// also check through Compatibility framework a few lines below).
@SuppressWarnings("AndroidFrameworkCompatChange")
private static boolean hasSystemFeature(Context context, String feature) {
- // Check release status of this change in behavior.
- if (!Flags.minimalTelephonyManagersConditionalOnFeatures()) return true;
-
// Check SDK version of the vendor partition.
final int vendorApiLevel = SystemProperties.getInt(
"ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 14d567d..2983e44 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -691,7 +691,7 @@
case UNKNOWN:
modemCount = 1;
// check for voice and data support, 0 if not supported
- if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
+ if (!isDeviceVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
modemCount = 0;
}
break;
@@ -2814,7 +2814,7 @@
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public int getPhoneType() {
- if (!isVoiceCapable() && !isDataCapable()) {
+ if (!isDeviceVoiceCapable() && !isDataCapable()) {
return PHONE_TYPE_NONE;
}
return getCurrentPhoneType();
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index b6f9e1f..0e70306 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -17,7 +17,6 @@
package android.telephony.data;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -32,7 +31,6 @@
import android.telephony.data.ApnSetting.ProtocolType;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -455,7 +453,6 @@
*
* @return The network validation status of data connection.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @PreciseDataConnectionState.NetworkValidationStatus int getNetworkValidationStatus() {
return mNetworkValidationStatus;
}
@@ -936,7 +933,6 @@
* @param status The network validation status.
* @return The same instance of the builder.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public @NonNull Builder setNetworkValidationStatus(
@PreciseDataConnectionState.NetworkValidationStatus int status) {
mNetworkValidationStatus = status;
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f04e1c9..5baf463 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -17,7 +17,6 @@
package android.telephony.data;
import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -40,7 +39,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.FunctionalUtils;
import com.android.telephony.Rlog;
@@ -414,7 +412,6 @@
* @param resultCodeCallback Listener for the {@link DataServiceCallback.ResultCode} that
* request validation to the DataService and checks if the request has been submitted.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public void requestNetworkValidation(int cid,
@NonNull @CallbackExecutor Executor executor,
@NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index f775de6..c42b29c1 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -17,7 +17,6 @@
package android.telephony.data;
import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
@@ -41,7 +40,6 @@
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.flags.FeatureFlagsImpl;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.FunctionalUtils;
import com.android.telephony.Rlog;
@@ -290,7 +288,6 @@
* @param resultCodeCallback A callback to determine whether the request was successfully
* submitted or not.
*/
- @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
public void requestNetworkValidation(
@NetCapability int networkCapability,
@NonNull @CallbackExecutor Executor executor,
@@ -298,15 +295,6 @@
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
- if (!sFeatureFlag.networkValidation()) {
- loge("networkValidation feature is disabled");
- executor.execute(
- () ->
- resultCodeCallback.accept(
- DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
- return;
- }
-
IIntegerConsumer callback = new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index 0c3c7e2..e868a6c 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -34,7 +34,7 @@
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
-import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verifyNoMoreInteractions
/**
* Test for testing revokeTrust & grantTrust for non-renewable trust.
@@ -120,7 +120,7 @@
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
await()
- verifyZeroInteractions(callback)
+ verifyNoMoreInteractions(callback)
}
companion object {
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
index f5d4b0c..cc7eebc 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
@@ -33,7 +33,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -120,7 +120,7 @@
verify(mUsbPortManager).enableUsbData(TEST_PORT_ID,
enable, TEST_TRANSACTION_ID, mCallback, null);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
clearInvocations(mUsbPortManager);
clearInvocations(mCallback);
@@ -131,7 +131,7 @@
assertFalse(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enable,
TEST_TRANSACTION_ID, mCallback, requester, isInternalRequest));
- verifyZeroInteractions(mUsbPortManager);
+ verifyNoMoreInteractions(mUsbPortManager);
verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
clearInvocations(mUsbPortManager);
@@ -188,7 +188,7 @@
mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID,
mCallback, TEST_SECOND_CALLER_ID, false);
- verifyZeroInteractions(mUsbPortManager);
+ verifyNoMoreInteractions(mUsbPortManager);
verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
}
@@ -203,7 +203,7 @@
verify(mUsbPortManager).enableUsbDataWhileDocked(TEST_PORT_ID, TEST_TRANSACTION_ID,
mCallback, null);
- verifyZeroInteractions(mCallback);
+ verifyNoMoreInteractions(mCallback);
}
/**