Merge "PackageWatchdog: ensure the opend resources are eventually closed" into main
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index c775280..ed669be 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -63,12 +63,14 @@
@Test
@Parameters(method = "getData")
- public void timeZipFileOpenClose(int numEntries) throws Exception {
+ public void timeZipFileOpen(int numEntries) throws Exception {
setUp(numEntries);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
ZipFile zf = new ZipFile(mFile);
+ state.pauseTiming();
zf.close();
+ state.resumeTiming();
}
}
diff --git a/api/api.go b/api/api.go
index e4d783e..cbdb7e8 100644
--- a/api/api.go
+++ b/api/api.go
@@ -105,7 +105,7 @@
func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
ctx.WalkDeps(func(child, parent android.Module) bool {
- if _, ok := child.(java.AndroidLibraryDependency); ok && child.Name() != "framework-res" {
+ if _, ok := android.OtherModuleProvider(ctx, child, java.AndroidLibraryInfoProvider); ok && child.Name() != "framework-res" {
// Stubs of BCP and SSCP libraries should not have any dependencies on apps
// This check ensures that we do not run into circular dependencies when UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true
ctx.ModuleErrorf(
diff --git a/core/api/current.txt b/core/api/current.txt
index 1b494c5..7c56a58 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9190,37 +9190,37 @@
package android.app.jank {
@FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats {
- ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.FrameOverrunHistogram);
- method @NonNull public android.app.jank.FrameOverrunHistogram getFrameOverrunHistogram();
+ ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.RelativeFrameTimeHistogram);
method public long getJankyFrameCount();
+ method @NonNull public android.app.jank.RelativeFrameTimeHistogram getRelativeFrameTimeHistogram();
method public long getTotalFrameCount();
method public int getUid();
method @NonNull public String getWidgetCategory();
method @NonNull public String getWidgetId();
method @NonNull public String getWidgetState();
- field public static final String ANIMATING = "animating";
- field public static final String ANIMATION = "animation";
- field public static final String DRAGGING = "dragging";
- field public static final String FLINGING = "flinging";
- field public static final String KEYBOARD = "keyboard";
- field public static final String MEDIA = "media";
- field public static final String NAVIGATION = "navigation";
- field public static final String NONE = "none";
- field public static final String OTHER = "other";
- field public static final String PLAYBACK = "playback";
- field public static final String PREDICTIVE_BACK = "predictive_back";
- field public static final String SCROLL = "scroll";
- field public static final String SCROLLING = "scrolling";
- field public static final String SWIPING = "swiping";
- field public static final String TAPPING = "tapping";
- field public static final String WIDGET_CATEGORY_UNSPECIFIED = "widget_category_unspecified";
- field public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";
- field public static final String ZOOMING = "zooming";
+ field public static final String WIDGET_CATEGORY_ANIMATION = "animation";
+ field public static final String WIDGET_CATEGORY_KEYBOARD = "keyboard";
+ field public static final String WIDGET_CATEGORY_MEDIA = "media";
+ field public static final String WIDGET_CATEGORY_NAVIGATION = "navigation";
+ field public static final String WIDGET_CATEGORY_OTHER = "other";
+ field public static final String WIDGET_CATEGORY_SCROLL = "scroll";
+ field public static final String WIDGET_CATEGORY_UNSPECIFIED = "unspecified";
+ field public static final String WIDGET_STATE_ANIMATING = "animating";
+ field public static final String WIDGET_STATE_DRAGGING = "dragging";
+ field public static final String WIDGET_STATE_FLINGING = "flinging";
+ field public static final String WIDGET_STATE_NONE = "none";
+ field public static final String WIDGET_STATE_PLAYBACK = "playback";
+ field public static final String WIDGET_STATE_PREDICTIVE_BACK = "predictive_back";
+ field public static final String WIDGET_STATE_SCROLLING = "scrolling";
+ field public static final String WIDGET_STATE_SWIPING = "swiping";
+ field public static final String WIDGET_STATE_TAPPING = "tapping";
+ field public static final String WIDGET_STATE_UNSPECIFIED = "unspecified";
+ field public static final String WIDGET_STATE_ZOOMING = "zooming";
}
- @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public class FrameOverrunHistogram {
- ctor public FrameOverrunHistogram();
- method public void addFrameOverrunMillis(int);
+ @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public class RelativeFrameTimeHistogram {
+ ctor public RelativeFrameTimeHistogram();
+ method public void addRelativeFrameTimeMillis(int);
method @NonNull public int[] getBucketCounters();
method @NonNull public int[] getBucketEndpointsMillis();
}
@@ -42521,7 +42521,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.GetValueRequest> CREATOR;
}
- public static final class GetValueRequest.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class GetValueRequest.Builder {
ctor public GetValueRequest.Builder(@NonNull String, @NonNull String);
method @NonNull public android.service.settings.preferences.GetValueRequest build();
}
@@ -42542,7 +42542,7 @@
field public static final int RESULT_UNSUPPORTED = 1; // 0x1
}
- public static final class GetValueResult.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class GetValueResult.Builder {
ctor public GetValueResult.Builder(int);
method @NonNull public android.service.settings.preferences.GetValueResult build();
method @NonNull public android.service.settings.preferences.GetValueResult.Builder setMetadata(@Nullable android.service.settings.preferences.SettingsPreferenceMetadata);
@@ -42555,7 +42555,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.MetadataRequest> CREATOR;
}
- public static final class MetadataRequest.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class MetadataRequest.Builder {
ctor public MetadataRequest.Builder();
method @NonNull public android.service.settings.preferences.MetadataRequest build();
}
@@ -42571,7 +42571,7 @@
field public static final int RESULT_UNSUPPORTED = 1; // 0x1
}
- public static final class MetadataResult.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class MetadataResult.Builder {
ctor public MetadataResult.Builder(int);
method @NonNull public android.service.settings.preferences.MetadataResult build();
method @NonNull public android.service.settings.preferences.MetadataResult.Builder setMetadataList(@NonNull java.util.List<android.service.settings.preferences.SettingsPreferenceMetadata>);
@@ -42586,7 +42586,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SetValueRequest> CREATOR;
}
- public static final class SetValueRequest.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SetValueRequest.Builder {
ctor public SetValueRequest.Builder(@NonNull String, @NonNull String, @NonNull android.service.settings.preferences.SettingsPreferenceValue);
method @NonNull public android.service.settings.preferences.SetValueRequest build();
}
@@ -42608,14 +42608,13 @@
field public static final int RESULT_UNSUPPORTED = 1; // 0x1
}
- public static final class SetValueResult.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SetValueResult.Builder {
ctor public SetValueResult.Builder(int);
method @NonNull public android.service.settings.preferences.SetValueResult build();
}
@FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceMetadata implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getBreadcrumbs();
method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getKey();
method @Nullable public android.content.Intent getLaunchIntent();
@@ -42631,17 +42630,16 @@
method public boolean isWritable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceMetadata> CREATOR;
+ field public static final int DEEPLINK_ONLY = 2; // 0x2
field public static final int EXPECT_POST_CONFIRMATION = 1; // 0x1
- field public static final int EXPECT_PRE_CONFIRMATION = 2; // 0x2
field public static final int NO_DIRECT_ACCESS = 3; // 0x3
field public static final int NO_SENSITIVITY = 0; // 0x0
}
- public static final class SettingsPreferenceMetadata.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SettingsPreferenceMetadata.Builder {
ctor public SettingsPreferenceMetadata.Builder(@NonNull String, @NonNull String);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata build();
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setAvailable(boolean);
- method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setBreadcrumbs(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setEnabled(boolean);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setLaunchIntent(@Nullable android.content.Intent);
@@ -42688,7 +42686,7 @@
field public static final int TYPE_STRING = 3; // 0x3
}
- public static final class SettingsPreferenceValue.Builder {
+ @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final class SettingsPreferenceValue.Builder {
ctor public SettingsPreferenceValue.Builder(int);
method @NonNull public android.service.settings.preferences.SettingsPreferenceValue build();
method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setBooleanValue(boolean);
@@ -53484,9 +53482,9 @@
field public static final int CHANGE_FRAME_RATE_ALWAYS = 1; // 0x1
field public static final int CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0; // 0x0
field @NonNull public static final android.os.Parcelable.Creator<android.view.Surface> CREATOR;
+ field @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_gte_enum") public static final int FRAME_RATE_COMPATIBILITY_AT_LEAST = 2; // 0x2
field public static final int FRAME_RATE_COMPATIBILITY_DEFAULT = 0; // 0x0
field public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1; // 0x1
- field @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_gte_enum") public static final int FRAME_RATE_COMPATIBILITY_GTE = 2; // 0x2
field public static final int ROTATION_0 = 0; // 0x0
field public static final int ROTATION_180 = 2; // 0x2
field public static final int ROTATION_270 = 3; // 0x3
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d95a17f5..d5cb6c0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5079,8 +5079,8 @@
}
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpoint {
- method @Nullable public android.hardware.contexthub.IHubEndpointLifecycleCallback getLifecycleCallback();
- method @Nullable public android.hardware.contexthub.IHubEndpointMessageCallback getMessageCallback();
+ method @Nullable public android.hardware.contexthub.HubEndpointLifecycleCallback getLifecycleCallback();
+ method @Nullable public android.hardware.contexthub.HubEndpointMessageCallback getMessageCallback();
method @NonNull public java.util.Collection<android.hardware.contexthub.HubServiceInfo> getServiceInfoCollection();
method @Nullable public String getTag();
method public int getVersion();
@@ -5095,14 +5095,19 @@
public static final class HubEndpoint.Builder {
ctor public HubEndpoint.Builder(@NonNull android.content.Context);
method @NonNull public android.hardware.contexthub.HubEndpoint build();
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback);
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback);
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull android.hardware.contexthub.IHubEndpointMessageCallback);
- method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointMessageCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull android.hardware.contexthub.HubEndpointLifecycleCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointLifecycleCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull android.hardware.contexthub.HubEndpointMessageCallback);
+ method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointMessageCallback);
method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setServiceInfoCollection(@NonNull java.util.Collection<android.hardware.contexthub.HubServiceInfo>);
method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setTag(@NonNull String);
}
+ @FlaggedApi("android.chre.flags.offload_api") public interface HubEndpointDiscoveryCallback {
+ method public void onEndpointsStarted(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>);
+ method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>, int);
+ }
+
@FlaggedApi("android.chre.flags.offload_api") public final class HubEndpointInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.hardware.contexthub.HubEndpointInfo.HubEndpointIdentifier getIdentifier();
@@ -5126,6 +5131,16 @@
method public long getHub();
}
+ @FlaggedApi("android.chre.flags.offload_api") public interface HubEndpointLifecycleCallback {
+ method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
+ method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable String);
+ method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
+ }
+
+ @FlaggedApi("android.chre.flags.offload_api") public interface HubEndpointMessageCallback {
+ method public void onMessageReceived(@NonNull android.hardware.contexthub.HubEndpointSession, @NonNull android.hardware.contexthub.HubMessage);
+ }
+
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable {
method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void close();
method @Nullable public String getServiceDescriptor();
@@ -5174,21 +5189,6 @@
method @NonNull public android.hardware.contexthub.HubServiceInfo build();
}
- @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointDiscoveryCallback {
- method public void onEndpointsStarted(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>);
- method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>, int);
- }
-
- @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback {
- method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int);
- method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable String);
- method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession);
- }
-
- @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback {
- method public void onMessageReceived(@NonNull android.hardware.contexthub.HubEndpointSession, @NonNull android.hardware.contexthub.HubMessage);
- }
-
}
package android.hardware.devicestate {
@@ -6192,16 +6192,16 @@
method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpoint(@NonNull android.hardware.contexthub.HubEndpoint);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(long, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(long, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback, @NonNull java.util.concurrent.Executor);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull String, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull String, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback, @NonNull java.util.concurrent.Executor);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, long);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, long);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, @NonNull String);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int unloadNanoApp(int);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpoint(@NonNull android.hardware.contexthub.HubEndpoint);
- method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback);
+ method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.HubEndpointDiscoveryCallback);
field public static final int AUTHORIZATION_DENIED = 0; // 0x0
field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1
field public static final int AUTHORIZATION_GRANTED = 2; // 0x2
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fee8cdb..c3ef104 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5834,7 +5834,11 @@
final int size = permissions.length;
int[] results = new int[size];
for (int i = 0; i < size; i++) {
- results[i] = deviceContext.getPermissionRequestState(permissions[i]);
+ if (permissions[i] == null) {
+ results[i] = Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE;
+ } else {
+ results[i] = deviceContext.getPermissionRequestState(permissions[i]);
+ }
}
return results;
}
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 61b5687..599f1a8 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -27,6 +27,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Stores App Compat information about a particular Task.
@@ -58,16 +59,11 @@
public int topActivityLetterboxHeight = PROPERTY_VALUE_UNSET;
/**
- * Contains the current app height of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
+ * Contains the app bounds of the top activity or size compat mode
+ * bounds when in size compat mode. If null, contains bounds.
*/
- public int topActivityLetterboxAppHeight = PROPERTY_VALUE_UNSET;
-
- /**
- * Contains the current app width of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
- */
- public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET;
+ @NonNull
+ public final Rect topActivityAppBounds = new Rect();
/**
* Contains the top activity bounds when the activity is letterboxed.
@@ -350,8 +346,7 @@
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& topActivityLetterboxHorizontalPosition
== that.topActivityLetterboxHorizontalPosition
&& cameraCompatTaskInfo.equalsForTaskOrganizer(that.cameraCompatTaskInfo);
@@ -371,8 +366,7 @@
== that.topActivityLetterboxHorizontalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& cameraCompatTaskInfo.equalsForCompatUi(that.cameraCompatTaskInfo);
}
@@ -385,8 +379,7 @@
topActivityLetterboxHorizontalPosition = source.readInt();
topActivityLetterboxWidth = source.readInt();
topActivityLetterboxHeight = source.readInt();
- topActivityLetterboxAppWidth = source.readInt();
- topActivityLetterboxAppHeight = source.readInt();
+ topActivityAppBounds.set(Objects.requireNonNull(source.readTypedObject(Rect.CREATOR)));
topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR);
cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR);
}
@@ -401,8 +394,7 @@
dest.writeInt(topActivityLetterboxHorizontalPosition);
dest.writeInt(topActivityLetterboxWidth);
dest.writeInt(topActivityLetterboxHeight);
- dest.writeInt(topActivityLetterboxAppWidth);
- dest.writeInt(topActivityLetterboxAppHeight);
+ dest.writeTypedObject(topActivityAppBounds, flags);
dest.writeTypedObject(topActivityLetterboxBounds, flags);
dest.writeTypedObject(cameraCompatTaskInfo, flags);
}
@@ -421,8 +413,7 @@
+ topActivityLetterboxHorizontalPosition
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
- + " topActivityLetterboxAppWidth=" + topActivityLetterboxAppWidth
- + " topActivityLetterboxAppHeight=" + topActivityLetterboxAppHeight
+ + " topActivityAppBounds=" + topActivityAppBounds
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled()
+ " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled()
+ " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride()
diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl
index 1ff7a17..d71ee7c 100644
--- a/core/java/android/app/IUserSwitchObserver.aidl
+++ b/core/java/android/app/IUserSwitchObserver.aidl
@@ -19,10 +19,10 @@
import android.os.IRemoteCallback;
/** {@hide} */
-interface IUserSwitchObserver {
- void onBeforeUserSwitching(int newUserId);
- oneway void onUserSwitching(int newUserId, IRemoteCallback reply);
- oneway void onUserSwitchComplete(int newUserId);
- oneway void onForegroundProfileSwitch(int newProfileId);
- oneway void onLockedBootComplete(int newUserId);
+oneway interface IUserSwitchObserver {
+ void onBeforeUserSwitching(int newUserId, IRemoteCallback reply);
+ void onUserSwitching(int newUserId, IRemoteCallback reply);
+ void onUserSwitchComplete(int newUserId);
+ void onForegroundProfileSwitch(int newProfileId);
+ void onLockedBootComplete(int newUserId);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 17638ee..eeb1ebb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11208,8 +11208,8 @@
private static final String KEY_SEGMENT_LENGTH = "length";
private static final String KEY_POINT_POSITION = "position";
- private static final int MAX_PROGRESS_SEGMENT_LIMIT = 15;
- private static final int MAX_PROGRESS_STOP_LIMIT = 5;
+ private static final int MAX_PROGRESS_SEGMENT_LIMIT = 10;
+ private static final int MAX_PROGRESS_POINT_LIMIT = 4;
private static final int DEFAULT_PROGRESS_MAX = 100;
private List<Segment> mProgressSegments = new ArrayList<>();
@@ -11286,7 +11286,9 @@
mProgressSegments = new ArrayList<>();
}
mProgressSegments.clear();
- mProgressSegments.addAll(progressSegments);
+ for (Segment segment : progressSegments) {
+ addProgressSegment(segment);
+ }
return this;
}
@@ -11302,7 +11304,11 @@
if (mProgressSegments == null) {
mProgressSegments = new ArrayList<>();
}
- mProgressSegments.add(segment);
+ if (segment.getLength() > 0) {
+ mProgressSegments.add(segment);
+ } else {
+ Log.w(TAG, "Dropped the segment. The length is not a positive integer.");
+ }
return this;
}
@@ -11327,7 +11333,14 @@
* @see Point
*/
public @NonNull ProgressStyle setProgressPoints(@NonNull List<Point> points) {
- mProgressPoints = new ArrayList<>(points);
+ if (mProgressPoints == null) {
+ mProgressPoints = new ArrayList<>();
+ }
+ mProgressPoints.clear();
+
+ for (Point point: points) {
+ addProgressPoint(point);
+ }
return this;
}
@@ -11348,7 +11361,17 @@
if (mProgressPoints == null) {
mProgressPoints = new ArrayList<>();
}
- mProgressPoints.add(point);
+ if (point.getPosition() >= 0) {
+ mProgressPoints.add(point);
+
+ if (mProgressPoints.size() > MAX_PROGRESS_POINT_LIMIT) {
+ Log.w(TAG, "Progress points limit is reached. First"
+ + MAX_PROGRESS_POINT_LIMIT + " points will be rendered.");
+ }
+
+ } else {
+ Log.w(TAG, "Dropped the point. The position is a negative integer.");
+ }
return this;
}
@@ -11384,8 +11407,7 @@
} else {
int progressMax = 0;
int validSegmentCount = 0;
- for (int i = 0; i < progressSegment.size()
- && validSegmentCount < MAX_PROGRESS_SEGMENT_LIMIT; i++) {
+ for (int i = 0; i < progressSegment.size(); i++) {
int segmentLength = progressSegment.get(i).getLength();
if (segmentLength > 0) {
try {
@@ -11832,6 +11854,30 @@
totalLength = DEFAULT_PROGRESS_MAX;
segments.add(sanitizeSegment(new Segment(totalLength), backgroundColor,
defaultProgressColor));
+ } else if (segments.size() > MAX_PROGRESS_SEGMENT_LIMIT) {
+ // If segment limit is exceeded. All segments will be replaced
+ // with a single segment
+ boolean allSameColor = true;
+ int firstSegmentColor = segments.get(0).getColor();
+
+ for (int i = 1; i < segments.size(); i++) {
+ if (segments.get(i).getColor() != firstSegmentColor) {
+ allSameColor = false;
+ break;
+ }
+ }
+
+ // This single segment length has same max as total.
+ final Segment singleSegment = new Segment(totalLength);
+ // Single segment color: if all segments have the same color,
+ // use that color. Otherwise, use 0 / default.
+ singleSegment.setColor(allSameColor ? firstSegmentColor
+ : Notification.COLOR_DEFAULT);
+
+ segments.clear();
+ segments.add(sanitizeSegment(singleSegment,
+ backgroundColor,
+ defaultProgressColor));
}
// Ensure point color contrasts.
@@ -11840,6 +11886,9 @@
final int position = point.getPosition();
if (position < 0 || position > totalLength) continue;
points.add(sanitizePoint(point, backgroundColor, defaultProgressColor));
+ if (points.size() == MAX_PROGRESS_POINT_LIMIT) {
+ break;
+ }
}
model = new NotificationProgressModel(segments, points,
@@ -11868,8 +11917,10 @@
* has the same hue as the original color, but is lightened or darkened depending on
* whether the background is dark or light.
*
+ * @hide
*/
- private int sanitizeProgressColor(@ColorInt int color,
+ @VisibleForTesting
+ public static int sanitizeProgressColor(@ColorInt int color,
@ColorInt int bg,
@ColorInt int defaultColor) {
return Builder.ensureColorContrast(
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 8ed66eb..e9b889a 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -55,6 +55,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -677,9 +678,14 @@
}
/** {@hide} */
- @UnsupportedAppUsage
- public NotificationManager(Context context, InstantSource clock)
+ public NotificationManager(Context context)
{
+ this(context, SystemClock.elapsedRealtimeClock());
+ }
+
+ /** {@hide} */
+ @UnsupportedAppUsage
+ public NotificationManager(Context context, InstantSource clock) {
mContext = context;
mClock = clock;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 920b19c..0bbe943 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -17,7 +17,7 @@
package android.app;
import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
-import static android.provider.flags.Flags.stageFlagsForBuild;
+import static android.provider.flags.Flags.newStoragePublicApi;
import static android.server.Flags.removeGameManagerServiceFromWear;
import android.accounts.AccountManager;
@@ -289,7 +289,6 @@
import com.android.internal.policy.PhoneLayoutInflater;
import com.android.internal.util.Preconditions;
-import java.time.InstantSource;
import java.util.Map;
import java.util.Objects;
@@ -625,8 +624,8 @@
com.android.internal.R.style.Theme_Dialog,
com.android.internal.R.style.Theme_Holo_Dialog,
com.android.internal.R.style.Theme_DeviceDefault_Dialog,
- com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)),
- InstantSource.system());
+ com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog))
+ );
}});
registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
@@ -1841,7 +1840,7 @@
VirtualizationFrameworkInitializer.registerServiceWrappers();
ConnectivityFrameworkInitializerBaklava.registerServiceWrappers();
- if (stageFlagsForBuild()) {
+ if (newStoragePublicApi()) {
ConfigInfrastructureFrameworkInitializer.registerServiceWrappers();
}
diff --git a/core/java/android/app/UserSwitchObserver.java b/core/java/android/app/UserSwitchObserver.java
index 727799a1..1664cfb 100644
--- a/core/java/android/app/UserSwitchObserver.java
+++ b/core/java/android/app/UserSwitchObserver.java
@@ -30,7 +30,11 @@
}
@Override
- public void onBeforeUserSwitching(int newUserId) throws RemoteException {}
+ public void onBeforeUserSwitching(int newUserId, IRemoteCallback reply) throws RemoteException {
+ if (reply != null) {
+ reply.sendResult(null);
+ }
+ }
@Override
public void onUserSwitching(int newUserId, IRemoteCallback reply) throws RemoteException {
diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java
index 3438cc8..ad43f27 100644
--- a/core/java/android/app/contextualsearch/ContextualSearchManager.java
+++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java
@@ -48,7 +48,9 @@
/**
* Key to get the entrypoint from the extras of the activity launched by contextual search.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_ENTRYPOINT =
"android.app.contextualsearch.extra.ENTRYPOINT";
@@ -56,14 +58,18 @@
/**
* Key to get the flag_secure value from the extras of the activity launched by contextual
* search. The value will be true if flag_secure is found in any of the visible activities.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_FLAG_SECURE_FOUND =
"android.app.contextualsearch.extra.FLAG_SECURE_FOUND";
/**
* Key to get the screenshot from the extras of the activity launched by contextual search.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_SCREENSHOT =
"android.app.contextualsearch.extra.SCREENSHOT";
@@ -71,7 +77,9 @@
/**
* Key to check whether managed profile is visible from the extras of the activity launched by
* contextual search. The value will be true if any one of the visible apps is managed.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_IS_MANAGED_PROFILE_VISIBLE =
"android.app.contextualsearch.extra.IS_MANAGED_PROFILE_VISIBLE";
@@ -79,7 +87,9 @@
/**
* Key to get the list of visible packages from the extras of the activity launched by
* contextual search.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_VISIBLE_PACKAGE_NAMES =
"android.app.contextualsearch.extra.VISIBLE_PACKAGE_NAMES";
@@ -87,7 +97,9 @@
/**
* Key to get the time the user made the invocation request, based on
* {@link SystemClock#uptimeMillis()}.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*
* TODO: un-hide in W
*
@@ -99,11 +111,24 @@
/**
* Key to get the binder token from the extras of the activity launched by contextual search.
* This token is needed to invoke {@link CallbackToken#getContextualSearchState} method.
- * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
*/
public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
/**
+ * Key to check whether audio is playing when contextual search is invoked.
+ * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH.
+ *
+ * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH
+ *
+ * @hide
+ */
+ public static final String EXTRA_IS_AUDIO_PLAYING =
+ "android.app.contextualsearch.extra.IS_AUDIO_PLAYING";
+
+ /**
* Intent action for contextual search invocation. The app providing the contextual search
* experience must add this intent filter action to the activity it wants to be launched.
* <br>
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index e8cfd79..c19921d 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -8,6 +8,7 @@
bug: "309689654"
is_exported: true
}
+
flag {
name: "enable_token_refresh"
namespace: "machine_learning"
@@ -27,4 +28,11 @@
namespace: "sysui_integrations"
description: "Identify live contextual search UI to exclude from contextual search screenshot."
bug: "372510690"
+}
+
+flag {
+ name: "include_audio_playing_status"
+ namespace: "sysui_integrations"
+ description: "Add audio playing status to the contextual search invocation intent."
+ bug: "372935419"
}
\ No newline at end of file
diff --git a/core/java/android/app/jank/AppJankStats.java b/core/java/android/app/jank/AppJankStats.java
index eea1d2b..6ef6a44 100644
--- a/core/java/android/app/jank/AppJankStats.java
+++ b/core/java/android/app/jank/AppJankStats.java
@@ -41,7 +41,8 @@
// The id that has been set for the widget.
private String mWidgetId;
- // A general category that the widget applies to.
+ // A general category the widget falls into based on the functions it performs or helps
+ // facilitate.
private String mWidgetCategory;
// The states that the UI elements can report
@@ -53,78 +54,78 @@
// Total number of frames determined to be janky during the reported state.
private long mJankyFrames;
- // Histogram of frame duration overruns encoded in predetermined buckets.
- private FrameOverrunHistogram mFrameOverrunHistogram;
+ // Histogram of relative frame times encoded in predetermined buckets.
+ private RelativeFrameTimeHistogram mRelativeFrameTimeHistogram;
/** Used to indicate no widget category has been set. */
- public static final String WIDGET_CATEGORY_UNSPECIFIED =
- "widget_category_unspecified";
+ public static final String WIDGET_CATEGORY_UNSPECIFIED = "unspecified";
/** UI elements that facilitate scrolling. */
- public static final String SCROLL = "scroll";
+ public static final String WIDGET_CATEGORY_SCROLL = "scroll";
/** UI elements that facilitate playing animations. */
- public static final String ANIMATION = "animation";
+ public static final String WIDGET_CATEGORY_ANIMATION = "animation";
/** UI elements that facilitate media playback. */
- public static final String MEDIA = "media";
+ public static final String WIDGET_CATEGORY_MEDIA = "media";
/** UI elements that facilitate in-app navigation. */
- public static final String NAVIGATION = "navigation";
+ public static final String WIDGET_CATEGORY_NAVIGATION = "navigation";
/** UI elements that facilitate displaying, hiding or interacting with keyboard. */
- public static final String KEYBOARD = "keyboard";
-
- /** UI elements that facilitate predictive back gesture navigation. */
- public static final String PREDICTIVE_BACK = "predictive_back";
+ public static final String WIDGET_CATEGORY_KEYBOARD = "keyboard";
/** UI elements that don't fall in one or any of the other categories. */
- public static final String OTHER = "other";
+ public static final String WIDGET_CATEGORY_OTHER = "other";
/** Used to indicate no widget state has been set. */
- public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified";
+ public static final String WIDGET_STATE_UNSPECIFIED = "unspecified";
/** Used to indicate the UI element currently has no state and is idle. */
- public static final String NONE = "none";
+ public static final String WIDGET_STATE_NONE = "none";
/** Used to indicate the UI element is currently scrolling. */
- public static final String SCROLLING = "scrolling";
+ public static final String WIDGET_STATE_SCROLLING = "scrolling";
/** Used to indicate the UI element is currently being flung. */
- public static final String FLINGING = "flinging";
+ public static final String WIDGET_STATE_FLINGING = "flinging";
/** Used to indicate the UI element is currently being swiped. */
- public static final String SWIPING = "swiping";
+ public static final String WIDGET_STATE_SWIPING = "swiping";
/** Used to indicate the UI element is currently being dragged. */
- public static final String DRAGGING = "dragging";
+ public static final String WIDGET_STATE_DRAGGING = "dragging";
/** Used to indicate the UI element is currently zooming. */
- public static final String ZOOMING = "zooming";
+ public static final String WIDGET_STATE_ZOOMING = "zooming";
/** Used to indicate the UI element is currently animating. */
- public static final String ANIMATING = "animating";
+ public static final String WIDGET_STATE_ANIMATING = "animating";
/** Used to indicate the UI element is currently playing media. */
- public static final String PLAYBACK = "playback";
+ public static final String WIDGET_STATE_PLAYBACK = "playback";
/** Used to indicate the UI element is currently being tapped on, for example on a keyboard. */
- public static final String TAPPING = "tapping";
+ public static final String WIDGET_STATE_TAPPING = "tapping";
+
+ /** Used to indicate predictive back navigation is currently being used */
+ public static final String WIDGET_STATE_PREDICTIVE_BACK = "predictive_back";
/**
+ * Provide an organized way to group widgets that have similar purposes or perform related
+ * functions.
* @hide
*/
- @StringDef(value = {
+ @StringDef(prefix = {"WIDGET_CATEGORY_"}, value = {
WIDGET_CATEGORY_UNSPECIFIED,
- SCROLL,
- ANIMATION,
- MEDIA,
- NAVIGATION,
- KEYBOARD,
- PREDICTIVE_BACK,
- OTHER
+ WIDGET_CATEGORY_SCROLL,
+ WIDGET_CATEGORY_ANIMATION,
+ WIDGET_CATEGORY_MEDIA,
+ WIDGET_CATEGORY_NAVIGATION,
+ WIDGET_CATEGORY_KEYBOARD,
+ WIDGET_CATEGORY_OTHER
})
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@@ -133,17 +134,18 @@
/**
* @hide
*/
- @StringDef(value = {
+ @StringDef(prefix = {"WIDGET_STATE_"}, value = {
WIDGET_STATE_UNSPECIFIED,
- NONE,
- SCROLLING,
- FLINGING,
- SWIPING,
- DRAGGING,
- ZOOMING,
- ANIMATING,
- PLAYBACK,
- TAPPING,
+ WIDGET_STATE_NONE,
+ WIDGET_STATE_SCROLLING,
+ WIDGET_STATE_FLINGING,
+ WIDGET_STATE_SWIPING,
+ WIDGET_STATE_DRAGGING,
+ WIDGET_STATE_ZOOMING,
+ WIDGET_STATE_ANIMATING,
+ WIDGET_STATE_PLAYBACK,
+ WIDGET_STATE_TAPPING,
+ WIDGET_STATE_PREDICTIVE_BACK
})
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@@ -156,31 +158,33 @@
*
* @param appUid the Uid of the App that is collecting jank stats.
* @param widgetId the widget id that frames will be associated to.
- * @param widgetCategory a general functionality category that the widget falls into. Must be
- * one of the following: SCROLL, ANIMATION, MEDIA, NAVIGATION, KEYBOARD,
- * PREDICTIVE_BACK, OTHER or will be set to WIDGET_CATEGORY_UNSPECIFIED
- * if no value is passed.
- * @param widgetState the state the widget was in while frames were counted. Must be one of
- * the following: NONE, SCROLLING, FLINGING, SWIPING, DRAGGING, ZOOMING,
- * ANIMATING, PLAYBACK, TAPPING or will be set to WIDGET_STATE_UNSPECIFIED
- * if no value is passed.
+ * @param widgetCategory a category used to organize widgets in a structured way that indicates
+ * they serve a similar purpose or perform related functions. Must be
+ * prefixed with WIDGET_CATEGORY_ and have a suffix of one of the
+ * following:SCROLL, ANIMATION, MEDIA, NAVIGATION, KEYBOARD, OTHER or
+ * will be set to UNSPECIFIED if no value is passed.
+ * @param widgetState the state the widget was in while frames were counted. Must be prefixed
+ * with WIDGET_STATE_ and have a suffix of one of the following:
+ * NONE, SCROLLING, FLINGING, SWIPING, DRAGGING, ZOOMING, ANIMATING,
+ * PLAYBACK, TAPPING, PREDICTIVE_BACK or will be set to
+ * WIDGET_STATE_UNSPECIFIED if no value is passed.
* @param totalFrames the total number of frames that were counted for this stat.
* @param jankyFrames the total number of janky frames that were counted for this stat.
- * @param frameOverrunHistogram the histogram with predefined buckets. See
- * {@link #getFrameOverrunHistogram()} for details.
+ * @param relativeFrameTimeHistogram the histogram with predefined buckets. See
+ * {@link #getRelativeFrameTimeHistogram()} for details.
*
*/
public AppJankStats(int appUid, @NonNull String widgetId,
@Nullable @WidgetCategory String widgetCategory,
@Nullable @WidgetState String widgetState, long totalFrames, long jankyFrames,
- @NonNull FrameOverrunHistogram frameOverrunHistogram) {
+ @NonNull RelativeFrameTimeHistogram relativeFrameTimeHistogram) {
mUid = appUid;
mWidgetId = widgetId;
mWidgetCategory = widgetCategory != null ? widgetCategory : WIDGET_CATEGORY_UNSPECIFIED;
mWidgetState = widgetState != null ? widgetState : WIDGET_STATE_UNSPECIFIED;
mTotalFrames = totalFrames;
mJankyFrames = jankyFrames;
- mFrameOverrunHistogram = frameOverrunHistogram;
+ mRelativeFrameTimeHistogram = relativeFrameTimeHistogram;
}
/**
@@ -203,7 +207,7 @@
/**
* Returns the category that the widget's functionality generally falls into, or
- * widget_category_unspecified {@link #WIDGET_CATEGORY_UNSPECIFIED} if no value was passed in.
+ * {@link #WIDGET_CATEGORY_UNSPECIFIED} if no value was passed in.
*
* @return the category that the widget's functionality generally falls into, this value cannot
* be null.
@@ -213,7 +217,7 @@
}
/**
- * Returns the widget's state that was reported for this stat, or widget_state_unspecified
+ * Returns the widget's state that was reported for this stat, or
* {@link #WIDGET_STATE_UNSPECIFIED} if no value was passed in.
*
* @return the widget's state that was reported for this stat. This value cannot be null.
@@ -241,13 +245,13 @@
}
/**
- * Returns a Histogram containing frame overrun times in millis grouped into predefined buckets.
- * See {@link FrameOverrunHistogram} for more information.
+ * Returns a Histogram containing relative frame times in millis grouped into predefined
+ * buckets. See {@link RelativeFrameTimeHistogram} for more information.
*
- * @return Histogram containing frame overrun times in predefined buckets. This value cannot
+ * @return Histogram containing relative frame times in predefined buckets. This value cannot
* be null.
*/
- public @NonNull FrameOverrunHistogram getFrameOverrunHistogram() {
- return mFrameOverrunHistogram;
+ public @NonNull RelativeFrameTimeHistogram getRelativeFrameTimeHistogram() {
+ return mRelativeFrameTimeHistogram;
}
}
diff --git a/core/java/android/app/jank/FrameOverrunHistogram.java b/core/java/android/app/jank/FrameOverrunHistogram.java
deleted file mode 100644
index 3ad6531..0000000
--- a/core/java/android/app/jank/FrameOverrunHistogram.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.jank;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-
-import java.util.Arrays;
-
-/**
- * This class is intended to be used when reporting {@link AppJankStats} back to the system. It's
- * intended to be used by library widgets to help facilitate the reporting of frame overrun times
- * by adding those times into predefined buckets.
- */
-@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
-public class FrameOverrunHistogram {
- private static int[] sBucketEndpoints = new int[]{
- Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20, -18,
- -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 40,
- 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000
- };
- private int[] mBucketCounts;
-
- /**
- * Create a new instance of FrameOverrunHistogram.
- */
- public FrameOverrunHistogram() {
- mBucketCounts = new int[sBucketEndpoints.length];
- }
-
- /**
- * Increases the count by one for the bucket representing the frame overrun duration.
- *
- * @param frameOverrunMillis frame overrun duration in millis, frame overrun is the difference
- * between a frames deadline and when it was rendered.
- */
- public void addFrameOverrunMillis(int frameOverrunMillis) {
- int countsIndex = getIndexForCountsFromOverrunTime(frameOverrunMillis);
- mBucketCounts[countsIndex]++;
- }
-
- /**
- * Returns the counts for the all the frame overrun buckets.
- *
- * @return an array of integers representing the counts of frame overrun times. This value
- * cannot be null.
- */
- public @NonNull int[] getBucketCounters() {
- return Arrays.copyOf(mBucketCounts, mBucketCounts.length);
- }
-
- /**
- * Returns the predefined endpoints for the histogram.
- *
- * @return array of integers representing the endpoints for the predefined histogram count
- * buckets. This value cannot be null.
- */
- public @NonNull int[] getBucketEndpointsMillis() {
- return Arrays.copyOf(sBucketEndpoints, sBucketEndpoints.length);
- }
-
- // This takes the overrun time and returns what bucket it belongs to in the counters array.
- private int getIndexForCountsFromOverrunTime(int overrunTime) {
- if (overrunTime < 20) {
- if (overrunTime >= -20) {
- return (overrunTime + 20) / 2 + 12;
- }
- if (overrunTime >= -30) {
- return (overrunTime + 30) / 5 + 10;
- }
- if (overrunTime >= -100) {
- return (overrunTime + 100) / 10 + 3;
- }
- if (overrunTime >= -200) {
- return (overrunTime + 200) / 50 + 1;
- }
- return 0;
- }
- if (overrunTime < 30) {
- return (overrunTime - 20) / 5 + 32;
- }
- if (overrunTime < 100) {
- return (overrunTime - 30) / 10 + 34;
- }
- if (overrunTime < 200) {
- return (overrunTime - 50) / 100 + 41;
- }
- if (overrunTime < 1000) {
- return (overrunTime - 200) / 100 + 43;
- }
- return sBucketEndpoints.length - 1;
- }
-}
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index c947259..b4c293e 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -111,7 +111,7 @@
pendingStat.mTotalFrames += jankStat.getTotalFrameCount();
mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
- jankStat.getFrameOverrunHistogram().getBucketCounters());
+ jankStat.getRelativeFrameTimeHistogram().getBucketCounters());
}
private void mergeNewStat(String stateKey, String activityName, AppJankStats jankStats) {
@@ -136,7 +136,7 @@
pendingStat.mJankyFrames = jankStats.getJankyFrameCount();
mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets,
- jankStats.getFrameOverrunHistogram().getBucketCounters());
+ jankStats.getRelativeFrameTimeHistogram().getBucketCounters());
mPendingJankStats.put(stateKey, pendingStat);
}
@@ -271,7 +271,8 @@
private static final int[] sFrameOverrunHistogramBounds = {
Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20,
-18, -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25,
- 30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000
+ 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];
@@ -414,7 +415,7 @@
if (overrunTime < 200) {
return (overrunTime - 50) / 100 + 41;
}
- if (overrunTime < 1000) {
+ if (overrunTime <= 1000) {
return (overrunTime - 200) / 100 + 43;
}
return sFrameOverrunHistogramBounds.length - 1;
diff --git a/core/java/android/app/jank/RelativeFrameTimeHistogram.java b/core/java/android/app/jank/RelativeFrameTimeHistogram.java
new file mode 100644
index 0000000..666f90f
--- /dev/null
+++ b/core/java/android/app/jank/RelativeFrameTimeHistogram.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.jank;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import java.util.Arrays;
+
+/**
+ * A histogram of frame times relative to their deadline.
+ *
+ * This class aids in reporting {@link AppJankStats} to the system and is designed for use by
+ * library widgets. It facilitates the recording of frame times in relation to the frame deadline.
+ * The class records the distribution of time remaining until a frame is considered janky or how
+ * janky the frame was.
+ * <p>
+ * A frame's relative frame time value indicates whether it was delivered early, on time, or late.
+ * A negative relative frame time value indicates the frame was delivered early, a value of zero
+ * indicates the frame was delivered on time and a positive value indicates the frame was delivered
+ * late. The values of the endpoints indicate how early or late a frame was delivered.
+ * <p>
+ * The relative frame times are recorded as a histogram: values are
+ * {@link #addRelativeFrameTimeMillis added} to a bucket by increasing the bucket's counter. The
+ * count of frames with a relative frame time between
+ * {@link #getBucketEndpointsMillis bucket endpoints} {@code i} and {@code i+1} can be obtained
+ * through index {@code i} of {@link #getBucketCounters}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+public class RelativeFrameTimeHistogram {
+ private static int[] sBucketEndpoints = new int[]{
+ Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20, -18,
+ -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 40,
+ 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
+ Integer.MAX_VALUE
+ };
+ //
+ private int[] mBucketCounts;
+
+ /**
+ * Create a new instance of RelativeFrameTimeHistogram.
+ */
+ public RelativeFrameTimeHistogram() {
+ mBucketCounts = new int[sBucketEndpoints.length - 1];
+ }
+
+ /**
+ * Increases the count by one for the bucket representing the relative frame time.
+ *
+ * @param frameTimeMillis relative frame time in millis, relative frame time is the difference
+ * between a frames deadline and when it was rendered.
+ */
+ public void addRelativeFrameTimeMillis(int frameTimeMillis) {
+ int countsIndex = getRelativeFrameTimeBucketIndex(frameTimeMillis);
+ mBucketCounts[countsIndex]++;
+ }
+
+ /**
+ * Returns the counts for the all the relative frame time buckets.
+ *
+ * @return an array of integers representing the counts of relative frame times. This value
+ * cannot be null.
+ */
+ public @NonNull int[] getBucketCounters() {
+ return Arrays.copyOf(mBucketCounts, mBucketCounts.length);
+ }
+
+ /**
+ * Returns the relative frame time endpoints for the histogram.
+ * <p>
+ * Index {@code i} of {@link #getBucketCounters} contains the count of frames that had a
+ * relative frame time between {@code endpoints[i]} (inclusive) and {@code endpoints[i+1]}
+ * (exclusive).
+ *
+ * @return array of integers representing the endpoints for the predefined histogram count
+ * buckets. This value cannot be null.
+ */
+ public @NonNull int[] getBucketEndpointsMillis() {
+ return Arrays.copyOf(sBucketEndpoints, sBucketEndpoints.length);
+ }
+
+ // This takes the relative frame time and returns what bucket it belongs to in the counters
+ // array.
+ private int getRelativeFrameTimeBucketIndex(int relativeFrameTime) {
+ if (relativeFrameTime < 20) {
+ if (relativeFrameTime >= -20) {
+ return (relativeFrameTime + 20) / 2 + 12;
+ }
+ if (relativeFrameTime >= -30) {
+ return (relativeFrameTime + 30) / 5 + 10;
+ }
+ if (relativeFrameTime >= -100) {
+ return (relativeFrameTime + 100) / 10 + 3;
+ }
+ if (relativeFrameTime >= -200) {
+ return (relativeFrameTime + 200) / 50 + 1;
+ }
+ return 0;
+ }
+ if (relativeFrameTime < 30) {
+ return (relativeFrameTime - 20) / 5 + 32;
+ }
+ if (relativeFrameTime < 100) {
+ return (relativeFrameTime - 30) / 10 + 34;
+ }
+ if (relativeFrameTime < 200) {
+ return (relativeFrameTime - 50) / 100 + 41;
+ }
+ if (relativeFrameTime < 1000) {
+ return (relativeFrameTime - 200) / 100 + 43;
+ }
+ return mBucketCounts.length - 1;
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 34c0f7b..321f09b 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -482,8 +482,19 @@
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
Parcel resultParcel = Parcel.obtain();
- mRemoteDevice.getCaptureResultMetadataQueue().writeToParcel(resultParcel, 0);
+
+ // Passing in PARCELABLE_WRITE_RETURN_VALUE closes the ParcelFileDescriptors
+ // owned by MQDescriptor returned by getCaptureResultMetadataQueue()
+ // Though these will be closed when GC runs, that may not happen for a while.
+ // Also, apps running with StrictMode would get warnings / crash in the case they're not
+ // explicitly closed.
+ mRemoteDevice.getCaptureResultMetadataQueue().writeToParcel(resultParcel,
+ Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
mFMQReader = nativeCreateFMQReader(resultParcel);
+ // Recycle since resultParcel would dup fds from MQDescriptor as well. We don't
+ // need them after the native FMQ reader has been created. That is since the native
+ // creates calls MQDescriptor.readFromParcel() which again dups the fds.
+ resultParcel.recycle();
IBinder remoteDeviceBinder = remoteDevice.asBinder();
// For legacy camera device, remoteDevice is in the same process, and
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 99f331f..1e5bed5 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -99,8 +99,8 @@
private final Object mLock = new Object();
private final HubEndpointInfo mPendingHubEndpointInfo;
- @Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback;
- @Nullable private final IHubEndpointMessageCallback mMessageCallback;
+ @Nullable private final HubEndpointLifecycleCallback mLifecycleCallback;
+ @Nullable private final HubEndpointMessageCallback mMessageCallback;
@NonNull private final Executor mLifecycleCallbackExecutor;
@NonNull private final Executor mMessageCallbackExecutor;
@@ -335,9 +335,9 @@
private HubEndpoint(
@NonNull HubEndpointInfo pendingEndpointInfo,
- @Nullable IHubEndpointLifecycleCallback endpointLifecycleCallback,
+ @Nullable HubEndpointLifecycleCallback endpointLifecycleCallback,
@NonNull Executor lifecycleCallbackExecutor,
- @Nullable IHubEndpointMessageCallback endpointMessageCallback,
+ @Nullable HubEndpointMessageCallback endpointMessageCallback,
@NonNull Executor messageCallbackExecutor) {
mPendingHubEndpointInfo = pendingEndpointInfo;
mLifecycleCallback = endpointLifecycleCallback;
@@ -485,12 +485,12 @@
}
@Nullable
- public IHubEndpointLifecycleCallback getLifecycleCallback() {
+ public HubEndpointLifecycleCallback getLifecycleCallback() {
return mLifecycleCallback;
}
@Nullable
- public IHubEndpointMessageCallback getMessageCallback() {
+ public HubEndpointMessageCallback getMessageCallback() {
return mMessageCallback;
}
@@ -498,11 +498,11 @@
public static final class Builder {
private final String mPackageName;
- @Nullable private IHubEndpointLifecycleCallback mLifecycleCallback;
+ @Nullable private HubEndpointLifecycleCallback mLifecycleCallback;
@NonNull private Executor mLifecycleCallbackExecutor;
- @Nullable private IHubEndpointMessageCallback mMessageCallback;
+ @Nullable private HubEndpointMessageCallback mMessageCallback;
@NonNull private Executor mMessageCallbackExecutor;
private int mVersion;
@@ -542,7 +542,7 @@
/** Attach a callback interface for lifecycle events for this Endpoint */
@NonNull
public Builder setLifecycleCallback(
- @NonNull IHubEndpointLifecycleCallback lifecycleCallback) {
+ @NonNull HubEndpointLifecycleCallback lifecycleCallback) {
mLifecycleCallback = lifecycleCallback;
return this;
}
@@ -554,7 +554,7 @@
@NonNull
public Builder setLifecycleCallback(
@NonNull @CallbackExecutor Executor executor,
- @NonNull IHubEndpointLifecycleCallback lifecycleCallback) {
+ @NonNull HubEndpointLifecycleCallback lifecycleCallback) {
mLifecycleCallbackExecutor = executor;
mLifecycleCallback = lifecycleCallback;
return this;
@@ -562,7 +562,7 @@
/** Attach a callback interface for message events for this Endpoint */
@NonNull
- public Builder setMessageCallback(@NonNull IHubEndpointMessageCallback messageCallback) {
+ public Builder setMessageCallback(@NonNull HubEndpointMessageCallback messageCallback) {
mMessageCallback = messageCallback;
return this;
}
@@ -574,7 +574,7 @@
@NonNull
public Builder setMessageCallback(
@NonNull @CallbackExecutor Executor executor,
- @NonNull IHubEndpointMessageCallback messageCallback) {
+ @NonNull HubEndpointMessageCallback messageCallback) {
mMessageCallbackExecutor = executor;
mMessageCallback = messageCallback;
return this;
diff --git a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java b/core/java/android/hardware/contexthub/HubEndpointDiscoveryCallback.java
similarity index 96%
rename from core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java
rename to core/java/android/hardware/contexthub/HubEndpointDiscoveryCallback.java
index a61a7eb..4672bbb 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java
+++ b/core/java/android/hardware/contexthub/HubEndpointDiscoveryCallback.java
@@ -30,7 +30,7 @@
*/
@SystemApi
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
-public interface IHubEndpointDiscoveryCallback {
+public interface HubEndpointDiscoveryCallback {
/**
* Called when a list of hub endpoints have started.
*
diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/HubEndpointLifecycleCallback.java
similarity index 97%
rename from core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
rename to core/java/android/hardware/contexthub/HubEndpointLifecycleCallback.java
index 698ed0a..6d75010 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java
+++ b/core/java/android/hardware/contexthub/HubEndpointLifecycleCallback.java
@@ -29,7 +29,7 @@
*/
@SystemApi
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
-public interface IHubEndpointLifecycleCallback {
+public interface HubEndpointLifecycleCallback {
/**
* Called when an endpoint is requesting a session be opened with another endpoint.
*
diff --git a/core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java b/core/java/android/hardware/contexthub/HubEndpointMessageCallback.java
similarity index 89%
rename from core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java
rename to core/java/android/hardware/contexthub/HubEndpointMessageCallback.java
index fde7017..5db54ef 100644
--- a/core/java/android/hardware/contexthub/IHubEndpointMessageCallback.java
+++ b/core/java/android/hardware/contexthub/HubEndpointMessageCallback.java
@@ -26,18 +26,18 @@
* <p>This interface can be attached to an endpoint through {@link
* HubEndpoint.Builder#setMessageCallback} method. Methods in this interface will only be called
* when the endpoint is currently registered and has an open session. The endpoint will receive
- * session lifecycle callbacks through {@link IHubEndpointLifecycleCallback}.
+ * session lifecycle callbacks through {@link HubEndpointLifecycleCallback}.
*
* @hide
*/
@SystemApi
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
-public interface IHubEndpointMessageCallback {
+public interface HubEndpointMessageCallback {
/**
* Callback interface for receiving messages for a particular endpoint session.
*
* @param session The session this message is sent through. Previously specified in a {@link
- * IHubEndpointLifecycleCallback#onSessionOpened(HubEndpointSession)} call.
+ * HubEndpointLifecycleCallback#onSessionOpened(HubEndpointSession)} call.
* @param message The {@link HubMessage} object representing a message received by the endpoint
* that registered this callback interface. This message is constructed by the
*/
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index 77f937e..f7f5636 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -137,7 +137,7 @@
* no service associated to this session.
*
* <p>For hub initiated sessions, the object was previously used in as an argument for open
- * request in {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}.
+ * request in {@link HubEndpointLifecycleCallback#onSessionOpenRequest}.
*
* <p>For app initiated sessions, the object was previously used in an open request in {@link
* android.hardware.location.ContextHubManager#openSession}
diff --git a/core/java/android/hardware/contexthub/HubEndpointSessionResult.java b/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
index 1f2bdb9..b193d00 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSessionResult.java
@@ -23,7 +23,7 @@
import android.chre.flags.Flags;
/**
- * Return type of {@link IHubEndpointLifecycleCallback#onSessionOpenRequest}. The value determines
+ * Return type of {@link HubEndpointLifecycleCallback#onSessionOpenRequest}. The value determines
* whether a open session request from the remote is accepted or not.
*
* @hide
diff --git a/core/java/android/hardware/contexthub/HubServiceInfo.java b/core/java/android/hardware/contexthub/HubServiceInfo.java
index 2f33e8f..651be3b 100644
--- a/core/java/android/hardware/contexthub/HubServiceInfo.java
+++ b/core/java/android/hardware/contexthub/HubServiceInfo.java
@@ -112,6 +112,7 @@
* <p>The value can be one of {@link HubServiceInfo#FORMAT_CUSTOM}, {@link
* HubServiceInfo#FORMAT_AIDL} or {@link HubServiceInfo#FORMAT_PW_RPC_PROTOBUF}.
*/
+ @ServiceFormat
public int getFormat() {
return mFormat;
}
@@ -178,7 +179,8 @@
* <li>Pigweed RPC with Protobuf: com.example.proto.ExampleService
* </ol>
*
- * @param serviceDescriptor The service descriptor.
+ * @param serviceDescriptor The service descriptor for the interface, provided by the
+ * vendor.
* @param format One of {@link HubServiceInfo#FORMAT_CUSTOM}, {@link
* HubServiceInfo#FORMAT_AIDL} or {@link HubServiceInfo#FORMAT_PW_RPC_PROTOBUF}.
* @param majorVersion Breaking changes should be a major version bump.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index ffa5460..9030810 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -106,7 +106,7 @@
@IntDef(prefix = {"EVENT_DISPLAY_"}, flag = true, value = {
EVENT_DISPLAY_ADDED,
- EVENT_DISPLAY_CHANGED,
+ EVENT_DISPLAY_BASIC_CHANGED,
EVENT_DISPLAY_REMOVED,
EVENT_DISPLAY_BRIGHTNESS_CHANGED,
EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
@@ -119,7 +119,8 @@
public @interface DisplayEvent {}
public static final int EVENT_DISPLAY_ADDED = 1;
- public static final int EVENT_DISPLAY_CHANGED = 2;
+ public static final int EVENT_DISPLAY_BASIC_CHANGED = 2;
+
public static final int EVENT_DISPLAY_REMOVED = 3;
public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;
@@ -130,7 +131,7 @@
@LongDef(prefix = {"INTERNAL_EVENT_FLAG_"}, flag = true, value = {
INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
- INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED,
INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
@@ -143,7 +144,7 @@
public @interface InternalEventFlag {}
public static final long INTERNAL_EVENT_FLAG_DISPLAY_ADDED = 1L << 0;
- public static final long INTERNAL_EVENT_FLAG_DISPLAY_CHANGED = 1L << 1;
+ public static final long INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED = 1L << 1;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_REMOVED = 1L << 2;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED = 1L << 3;
public static final long INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED = 1L << 4;
@@ -485,7 +486,7 @@
// There can be racing condition between DMS and WMS callbacks, so force triggering the
// listener to make sure the client can get the onDisplayChanged callback even if
// DisplayInfo is not changed (Display read from both DisplayInfo and WindowConfiguration).
- handleDisplayEvent(displayId, EVENT_DISPLAY_CHANGED, true /* forceUpdate */);
+ handleDisplayEvent(displayId, EVENT_DISPLAY_BASIC_CHANGED, true /* forceUpdate */);
}
private static Looper getLooperForHandler(@Nullable Handler handler) {
@@ -518,7 +519,8 @@
}
if (mDispatchNativeCallbacks) {
mask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
}
if (!mTopologyListeners.isEmpty()) {
@@ -571,7 +573,8 @@
}
info = getDisplayInfoLocked(displayId);
- if (event == EVENT_DISPLAY_CHANGED && mDispatchNativeCallbacks) {
+ if ((event == EVENT_DISPLAY_BASIC_CHANGED
+ || event == EVENT_DISPLAY_REFRESH_RATE_CHANGED) && mDispatchNativeCallbacks) {
// Choreographer only supports a single display, so only dispatch refresh rate
// changes for the default display.
if (displayId == Display.DEFAULT_DISPLAY) {
@@ -1492,9 +1495,9 @@
mListener.onDisplayAdded(displayId);
}
break;
- case EVENT_DISPLAY_CHANGED:
- if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_CHANGED)
- != 0) {
+ case EVENT_DISPLAY_BASIC_CHANGED:
+ if ((mInternalEventFlagsMask
+ & INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED) != 0) {
if (info != null && (forceUpdate || !info.equals(mDisplayInfo))) {
if (extraLogging()) {
Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
@@ -1691,8 +1694,8 @@
switch (event) {
case EVENT_DISPLAY_ADDED:
return "ADDED";
- case EVENT_DISPLAY_CHANGED:
- return "CHANGED";
+ case EVENT_DISPLAY_BASIC_CHANGED:
+ return "BASIC_CHANGED";
case EVENT_DISPLAY_REMOVED:
return "REMOVED";
case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
@@ -1763,7 +1766,11 @@
}
if ((eventFlags & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
- baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_CHANGED;
+ // For backward compatibility, a client subscribing to
+ // DisplayManager.EVENT_FLAG_DISPLAY_CHANGED will be enrolled to both Basic and
+ // RR changes
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
}
if ((eventFlags
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 211aeff..ba5dfc0 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -129,14 +129,38 @@
}
/**
+ * Update the size of a display and normalize the topology.
+ * @param displayId The logical display ID
+ * @param width The new width
+ * @param height The new height
+ * @return True if the topology has changed.
+ */
+ public boolean updateDisplay(int displayId, float width, float height) {
+ TreeNode display = findDisplay(displayId, mRoot);
+ if (display == null) {
+ return false;
+ }
+ if (floatEquals(display.mWidth, width) && floatEquals(display.mHeight, height)) {
+ return false;
+ }
+ display.mWidth = width;
+ display.mHeight = height;
+ normalize();
+ Slog.i(TAG, "Display with ID " + displayId + " updated, new width: " + width
+ + ", new height: " + height);
+ return true;
+ }
+
+ /**
* Remove a display from the topology.
* The default topology is created from the remaining displays, as if they were reconnected
* one by one.
* @param displayId The logical display ID
+ * @return True if the display was present in the topology and removed.
*/
- public void removeDisplay(int displayId) {
+ public boolean removeDisplay(int displayId) {
if (findDisplay(displayId, mRoot) == null) {
- return;
+ return false;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(mRoot);
@@ -159,6 +183,7 @@
} else {
Slog.i(TAG, "Display with ID " + displayId + " removed");
}
+ return true;
}
/**
@@ -685,12 +710,12 @@
/**
* The width of the display in density-independent pixels (dp).
*/
- private final float mWidth;
+ private float mWidth;
/**
* The height of the display in density-independent pixels (dp).
*/
- private final float mHeight;
+ private float mHeight;
/**
* The position of this display relative to its parent.
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 1e0cc94..0cd3209 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -36,11 +36,11 @@
import android.hardware.contexthub.ErrorCode;
import android.hardware.contexthub.HubDiscoveryInfo;
import android.hardware.contexthub.HubEndpoint;
+import android.hardware.contexthub.HubEndpointDiscoveryCallback;
import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubEndpointLifecycleCallback;
import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback;
-import android.hardware.contexthub.IHubEndpointDiscoveryCallback;
-import android.hardware.contexthub.IHubEndpointLifecycleCallback;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
@@ -207,7 +207,7 @@
private Handler mCallbackHandler;
/** A map of endpoint discovery callbacks currently registered */
- private Map<IHubEndpointDiscoveryCallback, IContextHubEndpointDiscoveryCallback>
+ private Map<HubEndpointDiscoveryCallback, IContextHubEndpointDiscoveryCallback>
mDiscoveryCallbacks = new ConcurrentHashMap<>();
/**
@@ -718,7 +718,19 @@
/**
* Find a list of endpoints that provides a specific service.
*
- * @param serviceDescriptor Statically generated ID for an endpoint.
+ * <p>Service descriptor should uniquely identify the interface (scoped to type). Convention of
+ * the descriptor depend on interface type.
+ *
+ * <p>Examples:
+ *
+ * <ol>
+ * <li>AOSP-defined AIDL: android.hardware.something.IFoo/default
+ * <li>Vendor-defined AIDL: com.example.something.IBar/default
+ * <li>Pigweed RPC with Protobuf: com.example.proto.ExampleService
+ * </ol>
+ *
+ * @param serviceDescriptor The service descriptor for a service provided by the hub. The value
+ * cannot be null or empty.
* @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery.
* @throws IllegalArgumentException if the serviceDescriptor is empty/null.
*/
@@ -750,14 +762,15 @@
/**
* Creates an interface to invoke endpoint discovery callbacks to send down to the service.
*
- * @param callback the callback to invoke at the client process
* @param executor the executor to invoke callbacks for this client
+ * @param callback the callback to invoke at the client process
+ * @param serviceDescriptor an optional descriptor to match discovery list with
* @return the callback interface
*/
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
private IContextHubEndpointDiscoveryCallback createDiscoveryCallback(
- IHubEndpointDiscoveryCallback callback,
Executor executor,
+ HubEndpointDiscoveryCallback callback,
@Nullable String serviceDescriptor) {
return new IContextHubEndpointDiscoveryCallback.Stub() {
@Override
@@ -829,36 +842,36 @@
}
/**
- * Equivalent to {@link #registerEndpointDiscoveryCallback(long, IHubEndpointDiscoveryCallback,
- * Executor)} with the default executor in the main thread.
+ * Equivalent to {@link #registerEndpointDiscoveryCallback(Executor,
+ * HubEndpointDiscoveryCallback, long)} with the default executor in the main thread.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- long endpointId, @NonNull IHubEndpointDiscoveryCallback callback) {
+ @NonNull HubEndpointDiscoveryCallback callback, long endpointId) {
registerEndpointDiscoveryCallback(
- endpointId, callback, new HandlerExecutor(Handler.getMain()));
+ new HandlerExecutor(Handler.getMain()), callback, endpointId);
}
/**
* Registers a callback to be notified when the hub endpoint with the corresponding endpoint ID
* has started or stopped.
*
- * @param endpointId The identifier of the hub endpoint.
- * @param callback The callback to be invoked.
* @param executor The executor to invoke the callback on.
+ * @param callback The callback to be invoked.
+ * @param endpointId The identifier of the hub endpoint.
* @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- long endpointId,
- @NonNull IHubEndpointDiscoveryCallback callback,
- @NonNull Executor executor) {
- Objects.requireNonNull(callback, "callback cannot be null");
+ @NonNull Executor executor,
+ @NonNull HubEndpointDiscoveryCallback callback,
+ long endpointId) {
Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(callback, "callback cannot be null");
IContextHubEndpointDiscoveryCallback iCallback =
- createDiscoveryCallback(callback, executor, null);
+ createDiscoveryCallback(executor, callback, null);
try {
mService.registerEndpointDiscoveryCallbackId(endpointId, iCallback);
} catch (RemoteException e) {
@@ -869,42 +882,42 @@
}
/**
- * Equivalent to {@link #registerEndpointDiscoveryCallback(String,
- * IHubEndpointDiscoveryCallback, Executor)} with the default executor in the main thread.
+ * Equivalent to {@link #registerEndpointDiscoveryCallback(Executor,
+ * HubEndpointDiscoveryCallback, String)} with the default executor in the main thread.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- @NonNull String serviceDescriptor, @NonNull IHubEndpointDiscoveryCallback callback) {
+ @NonNull HubEndpointDiscoveryCallback callback, @NonNull String serviceDescriptor) {
registerEndpointDiscoveryCallback(
- serviceDescriptor, callback, new HandlerExecutor(Handler.getMain()));
+ new HandlerExecutor(Handler.getMain()), callback, serviceDescriptor);
}
/**
* Registers a callback to be notified when the hub endpoint with the corresponding service
* descriptor has started or stopped.
*
+ * @param executor The executor to invoke the callback on.
* @param serviceDescriptor The service descriptor of the hub endpoint.
* @param callback The callback to be invoked.
- * @param executor The executor to invoke the callback on.
* @throws IllegalArgumentException if the serviceDescriptor is empty.
* @throws UnsupportedOperationException If the operation is not supported.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void registerEndpointDiscoveryCallback(
- @NonNull String serviceDescriptor,
- @NonNull IHubEndpointDiscoveryCallback callback,
- @NonNull Executor executor) {
- Objects.requireNonNull(serviceDescriptor, "serviceDescriptor cannot be null");
- Objects.requireNonNull(callback, "callback cannot be null");
+ @NonNull Executor executor,
+ @NonNull HubEndpointDiscoveryCallback callback,
+ @NonNull String serviceDescriptor) {
Objects.requireNonNull(executor, "executor cannot be null");
+ Objects.requireNonNull(callback, "callback cannot be null");
+ Objects.requireNonNull(serviceDescriptor, "serviceDescriptor cannot be null");
if (serviceDescriptor.isBlank()) {
throw new IllegalArgumentException("Invalid service descriptor: " + serviceDescriptor);
}
IContextHubEndpointDiscoveryCallback iCallback =
- createDiscoveryCallback(callback, executor, serviceDescriptor);
+ createDiscoveryCallback(executor, callback, serviceDescriptor);
try {
mService.registerEndpointDiscoveryCallbackDescriptor(serviceDescriptor, iCallback);
} catch (RemoteException e) {
@@ -924,7 +937,7 @@
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
public void unregisterEndpointDiscoveryCallback(
- @NonNull IHubEndpointDiscoveryCallback callback) {
+ @NonNull HubEndpointDiscoveryCallback callback) {
Objects.requireNonNull(callback, "callback cannot be null");
IContextHubEndpointDiscoveryCallback iCallback = mDiscoveryCallbacks.remove(callback);
if (iCallback == null) {
@@ -1291,7 +1304,7 @@
* service.
*
* <p>Context Hub Service will create the endpoint session and notify the registered endpoint.
- * The registered endpoint will receive callbacks on its {@link IHubEndpointLifecycleCallback}
+ * The registered endpoint will receive callbacks on its {@link HubEndpointLifecycleCallback}
* object regarding the lifecycle events of the session.
*
* @param hubEndpoint {@link HubEndpoint} object previously registered via {@link
@@ -1311,7 +1324,7 @@
* described by a {@link HubServiceInfo} object.
*
* <p>Context Hub Service will create the endpoint session and notify the registered endpoint.
- * The registered endpoint will receive callbacks on its {@link IHubEndpointLifecycleCallback}
+ * The registered endpoint will receive callbacks on its {@link HubEndpointLifecycleCallback}
* object regarding the lifecycle events of the session.
*
* @param hubEndpoint {@link HubEndpoint} object previously registered via {@link
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 8a0adfa..ecd90e4 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -480,10 +480,10 @@
map.erase();
map.ensureCapacity(count);
}
- int numLazyValues = 0;
+ int[] numLazyValues = new int[]{0};
try {
- numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
- /* lazy */ ownsParcel, this);
+ parcelledData.readArrayMap(map, count, !parcelledByNative,
+ /* lazy */ ownsParcel, this, numLazyValues);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -494,14 +494,14 @@
} finally {
mWeakParcelledData = null;
if (ownsParcel) {
- if (numLazyValues == 0) {
+ if (numLazyValues[0] == 0) {
recycleParcel(parcelledData);
} else {
mWeakParcelledData = new WeakReference<>(parcelledData);
}
}
- mLazyValues = numLazyValues;
+ mLazyValues = numLazyValues[0];
mParcelledByNative = false;
mMap = map;
// Set field last as it is volatile
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 4bbc61c..5ba6553 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -5565,7 +5565,7 @@
private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
int size, @Nullable ClassLoaderProvider loaderProvider) {
- readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loaderProvider);
+ readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loaderProvider, null);
}
/**
@@ -5575,18 +5575,17 @@
* @param lazy Whether to populate the map with lazy {@link Function} objects for
* length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
* details.
- * @return a count of the lazy values in the map
+ * @param lazyValueCount number of lazy values added here
* @hide
*/
- int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
- boolean lazy, @Nullable ClassLoaderProvider loaderProvider) {
- int lazyValues = 0;
+ void readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+ boolean lazy, @Nullable ClassLoaderProvider loaderProvider, int[] lazyValueCount) {
while (size > 0) {
String key = readString();
Object value = (lazy) ? readLazyValue(loaderProvider) : readValue(
getClassLoader(loaderProvider));
if (value instanceof LazyValue) {
- lazyValues++;
+ lazyValueCount[0]++;
}
if (sorted) {
map.append(key, value);
@@ -5598,7 +5597,6 @@
if (sorted) {
map.validate();
}
- return lazyValues;
}
/**
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index e216992..289b98c 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -41,6 +41,7 @@
private boolean mReleased;
private boolean mLooperBlocked;
+ private final boolean mLooperIsMyLooper;
/**
* @hide
@@ -54,8 +55,11 @@
}
mLooper = looper;
mQueue = mLooper.getQueue();
- // Post a message that will keep the looper blocked as long as we are dispatching.
- new Handler(looper).post(new LooperHolder());
+ mLooperIsMyLooper = Looper.myLooper() == looper;
+ if (!mLooperIsMyLooper) {
+ // Post a message that will keep the looper blocked as long as we are dispatching.
+ new Handler(looper).post(new LooperHolder());
+ }
}
/**
@@ -82,7 +86,7 @@
public Message next() {
// Wait for the looper block to come up, to make sure we don't accidentally get
// the message for the block.
- while (!mLooperBlocked) {
+ while (!mLooperIsMyLooper && !mLooperBlocked) {
synchronized (this) {
try {
wait();
@@ -114,9 +118,6 @@
* should be executed by this queue.
* If the queue is empty or no messages are deliverable, returns null.
* This method never blocks.
- *
- * <p>Callers should always call {@link #recycle(Message)} on the message when all interactions
- * with it have completed.
*/
@FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
@SuppressWarnings("AutoBoxing") // box the primitive long, or return null to indicate no value
@@ -165,6 +166,9 @@
// This is being called from the thread it should be executed on, we can just dispatch.
message.target.dispatchMessage(message);
} else {
+ if (mLooperIsMyLooper) {
+ throw new RuntimeException("Cannot call execute from non Looper thread");
+ }
MessageExecution execution = new MessageExecution();
execution.m = message;
synchronized (execution) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c3a4930..6898fce 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1296,6 +1296,22 @@
public static final String ACTION_LOCKSCREEN_SETTINGS = "android.settings.LOCK_SCREEN_SETTINGS";
/**
+ * Activity Action: Show settings of notifications on lockscreen.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_LOCKSCREEN_NOTIFICATIONS_SETTINGS =
+ "android.settings.LOCK_SCREEN_NOTIFICATIONS_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow pairing bluetooth devices.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/service/settings/preferences/GetValueRequest.java b/core/java/android/service/settings/preferences/GetValueRequest.java
index 4f82800..db5c57c 100644
--- a/core/java/android/service/settings/preferences/GetValueRequest.java
+++ b/core/java/android/service/settings/preferences/GetValueRequest.java
@@ -108,6 +108,7 @@
/**
* Builder to construct {@link GetValueRequest}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
private final String mScreenKey;
private final String mPreferenceKey;
diff --git a/core/java/android/service/settings/preferences/GetValueResult.java b/core/java/android/service/settings/preferences/GetValueResult.java
index 369dea7..7911315 100644
--- a/core/java/android/service/settings/preferences/GetValueResult.java
+++ b/core/java/android/service/settings/preferences/GetValueResult.java
@@ -170,6 +170,7 @@
/**
* Builder to construct {@link GetValueResult}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@ResultCode
private final int mResultCode;
diff --git a/core/java/android/service/settings/preferences/MetadataRequest.java b/core/java/android/service/settings/preferences/MetadataRequest.java
index ffecc6b..e041715 100644
--- a/core/java/android/service/settings/preferences/MetadataRequest.java
+++ b/core/java/android/service/settings/preferences/MetadataRequest.java
@@ -65,6 +65,7 @@
/**
* Builder to construct {@link MetadataRequest}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
/** Constructs an immutable {@link MetadataRequest} object. */
@NonNull
diff --git a/core/java/android/service/settings/preferences/MetadataResult.java b/core/java/android/service/settings/preferences/MetadataResult.java
index 6a65dcc..e62fa8f 100644
--- a/core/java/android/service/settings/preferences/MetadataResult.java
+++ b/core/java/android/service/settings/preferences/MetadataResult.java
@@ -131,6 +131,7 @@
/**
* Builder to construct {@link MetadataResult}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@ResultCode
private final int mResultCode;
diff --git a/core/java/android/service/settings/preferences/SetValueRequest.java b/core/java/android/service/settings/preferences/SetValueRequest.java
index f7600ae..77581d9 100644
--- a/core/java/android/service/settings/preferences/SetValueRequest.java
+++ b/core/java/android/service/settings/preferences/SetValueRequest.java
@@ -123,6 +123,7 @@
/**
* Builder to construct {@link SetValueRequest}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
private final String mScreenKey;
private final String mPreferenceKey;
diff --git a/core/java/android/service/settings/preferences/SetValueResult.java b/core/java/android/service/settings/preferences/SetValueResult.java
index cb1776a..513f7a7 100644
--- a/core/java/android/service/settings/preferences/SetValueResult.java
+++ b/core/java/android/service/settings/preferences/SetValueResult.java
@@ -156,6 +156,7 @@
/**
* Builder to construct {@link SetValueResult}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@ResultCode
private final int mResultCode;
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
index ea7d4a6..30631f2 100644
--- a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java
@@ -102,6 +102,7 @@
/**
* Returns the breadcrumbs (navigation context) of Preference.
* <p>May be empty.
+ * @hide restrict to platform; may be opened wider in the future
*/
@NonNull
public List<String> getBreadcrumbs() {
@@ -189,33 +190,32 @@
@IntDef(value = {
NO_SENSITIVITY,
EXPECT_POST_CONFIRMATION,
- EXPECT_PRE_CONFIRMATION,
+ DEEPLINK_ONLY,
NO_DIRECT_ACCESS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WriteSensitivity {}
/**
- * Indicates preference is not sensitive.
+ * Indicates preference is not write-sensitive.
* <p>Its value is writable without explicit consent, assuming all necessary permissions are
* granted.
*/
public static final int NO_SENSITIVITY = 0;
/**
- * Indicates preference is mildly sensitive.
+ * Indicates preference is mildly write-sensitive.
* <p>In addition to necessary permissions, after writing its value the user should be
* given the option to revert back.
*/
public static final int EXPECT_POST_CONFIRMATION = 1;
/**
- * Indicates preference is sensitive.
- * <p>In addition to necessary permissions, the user should be prompted for confirmation prior
- * to making a change. Otherwise it is suggested to provide a deeplink to the Preference's page
- * instead, accessible via {@link #getLaunchIntent}.
+ * Indicates preference is write-sensitive.
+ * <p>This preference cannot be changed through this API; instead a deeplink to the Preference's
+ * page should be used instead, accessible via {@link #getLaunchIntent}.
*/
- public static final int EXPECT_PRE_CONFIRMATION = 2;
+ public static final int DEEPLINK_ONLY = 2;
/**
- * Indicates preference is highly sensitivity and carries significant user-risk.
+ * Indicates preference is highly write-sensitivity and carries significant user-risk.
* <p>This Preference cannot be changed through this API and no direct deeplink is available.
* Other Metadata is still available.
*/
@@ -303,6 +303,7 @@
/**
* Builder to construct {@link SettingsPreferenceMetadata}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
private final String mScreenKey;
private final String mKey;
@@ -355,6 +356,7 @@
/**
* Sets the preference breadcrumbs (navigation context).
+ * @hide
*/
@NonNull
public Builder setBreadcrumbs(@NonNull List<String> breadcrumbs) {
diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
index 08826ca..eea93b3 100644
--- a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
+++ b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java
@@ -170,6 +170,7 @@
/**
* Builder to construct {@link SettingsPreferenceValue}.
*/
+ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST)
public static final class Builder {
@Type
private final int mType;
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 089b5c2..6c50b5f 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.flags.Flags.bufferStuffingRecovery;
import static android.view.flags.Flags.FLAG_EXPECTED_PRESENTATION_TIME_API;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
@@ -965,22 +966,24 @@
// Evaluate if buffer stuffing recovery needs to start or end, and
// what actions need to be taken for recovery.
- switch (updateBufferStuffingState(frameTimeNanos, vsyncEventData)) {
- case NONE:
- // Without buffer stuffing recovery, offsetFrameTimeNanos is
- // synonymous with frameTimeNanos.
- break;
- case OFFSET:
- // Add animation offset. Used to update frame timeline with
- // offset before jitter is calculated.
- offsetFrameTimeNanos = frameTimeNanos - frameIntervalNanos;
- break;
- case DELAY_FRAME:
- // Intentional frame delay to help reduce queued buffer count.
- scheduleVsyncLocked();
- return;
- default:
- break;
+ if (bufferStuffingRecovery()) {
+ switch (updateBufferStuffingState(frameTimeNanos, vsyncEventData)) {
+ case NONE:
+ // Without buffer stuffing recovery, offsetFrameTimeNanos is
+ // synonymous with frameTimeNanos.
+ break;
+ case OFFSET:
+ // Add animation offset. Used to update frame timeline with
+ // offset before jitter is calculated.
+ offsetFrameTimeNanos = frameTimeNanos - frameIntervalNanos;
+ break;
+ case DELAY_FRAME:
+ // Intentional frame delay to help reduce queued buffer count.
+ scheduleVsyncLocked();
+ return;
+ default:
+ break;
+ }
}
try {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0c8a0d6..ca0959a 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1597,7 +1597,9 @@
// Although we only care about the HDR/SDR ratio changing, that can also come in the
// form of the larger DISPLAY_CHANGED event
mGlobal.registerDisplayListener(toRegister, executor,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal
.INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
ActivityThread.currentPackageName());
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index ba098eb5..e75b1b0 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -447,7 +447,18 @@
}
public boolean equals(DisplayInfo other) {
- return other != null
+ return equals(other, /* compareRefreshRate */ true);
+ }
+
+ /**
+ * Compares if the two DisplayInfo objects are equal or not
+ * @param other The other DisplayInfo against which the comparison is to be done
+ * @param compareRefreshRate Indicates if the refresh rate is also to be considered in
+ * comparison
+ * @return
+ */
+ public boolean equals(DisplayInfo other, boolean compareRefreshRate) {
+ boolean isEqualWithoutRefreshRate = other != null
&& layerStack == other.layerStack
&& flags == other.flags
&& type == other.type
@@ -466,7 +477,6 @@
&& logicalHeight == other.logicalHeight
&& Objects.equals(displayCutout, other.displayCutout)
&& rotation == other.rotation
- && modeId == other.modeId
&& hasArrSupport == other.hasArrSupport
&& Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)
&& Arrays.equals(supportedRefreshRates, other.supportedRefreshRates)
@@ -490,7 +500,6 @@
&& ownerUid == other.ownerUid
&& Objects.equals(ownerPackageName, other.ownerPackageName)
&& removeMode == other.removeMode
- && getRefreshRate() == other.getRefreshRate()
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
&& brightnessDefault == other.brightnessDefault
@@ -504,6 +513,13 @@
&& Objects.equals(
thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId)
&& canHostTasks == other.canHostTasks;
+
+ if (compareRefreshRate) {
+ return isEqualWithoutRefreshRate
+ && (getRefreshRate() == other.getRefreshRate())
+ && (modeId == other.modeId);
+ }
+ return isEqualWithoutRefreshRate;
}
@Override
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6e6e87b..4fc1cfc 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -206,7 +206,8 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FRAME_RATE_COMPATIBILITY_"},
value = {FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
- FRAME_RATE_COMPATIBILITY_GTE})
+ FRAME_RATE_COMPATIBILITY_AT_LEAST, FRAME_RATE_COMPATIBILITY_EXACT,
+ FRAME_RATE_COMPATIBILITY_MIN})
public @interface FrameRateCompatibility {}
// From native_window.h. Keep these in sync.
@@ -219,7 +220,7 @@
* In Android version {@link Build.VERSION_CODES#BAKLAVA} and above, use
* {@link FRAME_RATE_COMPATIBILITY_DEFAULT} for game content.
* For other cases, see {@link FRAME_RATE_COMPATIBILITY_FIXED_SOURCE} and
- * {@link FRAME_RATE_COMPATIBILITY_GTE}.
+ * {@link FRAME_RATE_COMPATIBILITY_AT_LEAST}.
*/
public static final int FRAME_RATE_COMPATIBILITY_DEFAULT = 0;
@@ -234,7 +235,7 @@
public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1;
/**
- * The surface requests a frame rate that is greater than or equal to the specified frame rate.
+ * The surface requests a frame rate that is at least the specified frame rate.
* This value should be used for UIs, animations, scrolling and fling, and anything that is not
* a game or video.
*
@@ -242,7 +243,7 @@
* {@link FRAME_RATE_COMPATIBILITY_DEFAULT}.
*/
@FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_GTE_ENUM)
- public static final int FRAME_RATE_COMPATIBILITY_GTE = 2;
+ public static final int FRAME_RATE_COMPATIBILITY_AT_LEAST = 2;
/**
* This surface belongs to an app on the High Refresh Rate Deny list, and needs the display
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index f22505b..833f2d9 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -22,7 +22,6 @@
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
-import static android.view.flags.Flags.bufferStuffingRecovery;
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
@@ -5118,11 +5117,9 @@
*/
@NonNull
public Transaction setRecoverableFromBufferStuffing(@NonNull SurfaceControl sc) {
- if (bufferStuffingRecovery()) {
- checkPreconditions(sc);
- nativeSetFlags(mNativeObject, sc.mNativeObject, RECOVERABLE_FROM_BUFFER_STUFFING,
- RECOVERABLE_FROM_BUFFER_STUFFING);
- }
+ checkPreconditions(sc);
+ nativeSetFlags(mNativeObject, sc.mNativeObject, RECOVERABLE_FROM_BUFFER_STUFFING,
+ RECOVERABLE_FROM_BUFFER_STUFFING);
return this;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d13f0e2..8f8bfe2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -27,7 +27,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.accessibility.Flags.FLAG_DEPRECATE_ACCESSIBILITY_ANNOUNCEMENT_APIS;
@@ -34251,7 +34251,7 @@
compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
frameRateToSet = frameRate;
} else {
- compatibility = FRAME_RATE_COMPATIBILITY_GTE;
+ compatibility = FRAME_RATE_COMPATIBILITY_AT_LEAST;
frameRateToSet = velocityFrameRate;
}
viewRootImpl.votePreferredFrameRate(frameRateToSet, compatibility);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1d27574..16cdb64 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -38,7 +38,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_BOOST;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED;
import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
@@ -1828,7 +1828,8 @@
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED
: DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
DisplayManagerGlobal
.getInstance()
@@ -13271,7 +13272,7 @@
* We set category to HIGH if the maximum frame rate is greater than 60.
* Otherwise, we set category to NORMAL.
*
- * Use FRAME_RATE_COMPATIBILITY_GTE for velocity and FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+ * Use FRAME_RATE_COMPATIBILITY_AT_LEAST for velocity and FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
* for TextureView video play and user requested frame rate.
*
* @param frameRate the preferred frame rate of a View
@@ -13282,7 +13283,7 @@
if (frameRate <= 0) {
return;
}
- if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && !mIsPressedGesture) {
+ if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_AT_LEAST && !mIsPressedGesture) {
mIsTouchBoosting = false;
mIsFrameRateBoosting = false;
if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 20d193e..f709ed7 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -12,5 +12,5 @@
name: "ccapi_baklava_enabled"
namespace: "machine_learning"
description: "Feature flag for baklava content capture API"
- bug: "309411951"
+ bug: "380381249"
}
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index 1101932..fcd7dfb 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -148,6 +148,9 @@
info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed attachToDisplayContent", e);
+ return false;
}
if (info == null) {
return false;
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 801698c..0d04961 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -58,13 +58,6 @@
}
flag {
- name: "user_min_aspect_ratio_app_default"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether the API PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT is available"
- bug: "310816437"
-}
-
-flag {
name: "allow_hide_scm_button"
namespace: "large_screen_experiences_app_compat"
description: "Whether we should allow hiding the size compat restart button"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index ae84644..c97d3ec 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -148,3 +148,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "condense_configuration_change_for_simple_mode"
+ description: "Condense configuration change for simple mode"
+ bug: "356738240"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index 0c2fd4b..5d66b3c 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -148,8 +148,8 @@
public void registerDisplayListener(DisplayManager.DisplayListener listener) {
manager.registerDisplayListener(listener, handler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal
- .INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
ActivityThread.currentPackageName());
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 3e2f301..f14e1f6 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -236,4 +236,7 @@
/** Shows rear display educational dialog */
void showRearDisplayDialog(int currentBaseState);
+
+ /** Unbundle a categorized notification */
+ void unbundleNotification(String key);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9bd5237..39ddea6 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,6 +25,8 @@
import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.security.Flags.shouldTrustManagerListenForPrimaryAuth;
+import static com.android.internal.widget.flags.Flags.hideLastCharWithPhysicalInput;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,6 +44,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.hardware.input.InputManagerGlobal;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -59,6 +62,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.view.InputDevice;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
@@ -1097,12 +1101,20 @@
return type == CREDENTIAL_TYPE_PATTERN;
}
+ private boolean hasActivePointerDeviceAttached() {
+ return !getEnabledNonTouchInputDevices(InputDevice.SOURCE_CLASS_POINTER).isEmpty();
+ }
+
/**
* @return Whether the visible pattern is enabled.
*/
@UnsupportedAppUsage
public boolean isVisiblePatternEnabled(int userId) {
- return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, true, userId);
+ boolean defaultValue = true;
+ if (hideLastCharWithPhysicalInput()) {
+ defaultValue = !hasActivePointerDeviceAttached();
+ }
+ return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, defaultValue, userId);
}
/**
@@ -1116,11 +1128,39 @@
return getString(Settings.Secure.LOCK_PATTERN_VISIBLE, userId) != null;
}
+ private List<InputDevice> getEnabledNonTouchInputDevices(int source) {
+ final InputManagerGlobal inputManager = InputManagerGlobal.getInstance();
+ final int[] inputIds = inputManager.getInputDeviceIds();
+ List<InputDevice> matchingDevices = new ArrayList<InputDevice>();
+ for (final int deviceId : inputIds) {
+ final InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+ if (!inputDevice.isEnabled()) continue;
+ if (inputDevice.supportsSource(InputDevice.SOURCE_TOUCHSCREEN)) continue;
+ if (inputDevice.isVirtual()) continue;
+ if (!inputDevice.supportsSource(source)) continue;
+ matchingDevices.add(inputDevice);
+ }
+ return matchingDevices;
+ }
+
+ private boolean hasPhysicalKeyboardActive() {
+ final List<InputDevice> keyboards =
+ getEnabledNonTouchInputDevices(InputDevice.SOURCE_KEYBOARD);
+ for (final InputDevice keyboard : keyboards) {
+ if (keyboard.isFullKeyboard()) return true;
+ }
+ return false;
+ }
+
/**
* @return Whether enhanced pin privacy is enabled.
*/
public boolean isPinEnhancedPrivacyEnabled(int userId) {
- return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, false, userId);
+ boolean defaultValue = false;
+ if (hideLastCharWithPhysicalInput()) {
+ defaultValue = hasPhysicalKeyboardActive();
+ }
+ return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, defaultValue, userId);
}
/**
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 027113a..5acdf32 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -482,11 +482,22 @@
"libbinder",
"libhidlbase", // libhwbinder is in here
],
+ version_script: "platform/linux/libandroid_runtime_export.txt",
+ },
+ darwin: {
+ host_ldlibs: [
+ "-framework AppKit",
+ ],
+ dist: {
+ targets: ["layoutlib_jni"],
+ dir: "layoutlib_native/darwin",
+ },
+ exported_symbols_list: "platform/darwin/libandroid_runtime_export.exp",
},
linux_glibc_x86_64: {
ldflags: ["-static-libgcc"],
dist: {
- targets: ["layoutlib"],
+ targets: ["layoutlib_jni"],
dir: "layoutlib_native/linux",
tag: "stripped_all",
},
diff --git a/core/jni/android_hardware_camera2_CameraDevice.cpp b/core/jni/android_hardware_camera2_CameraDevice.cpp
index 493c707..04cfed5 100644
--- a/core/jni/android_hardware_camera2_CameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_CameraDevice.cpp
@@ -30,6 +30,7 @@
#include <nativehelper/JNIHelp.h>
#include "android_os_Parcel.h"
#include "core_jni_helpers.h"
+#include <android/binder_auto_utils.h>
#include <android/binder_parcel_jni.h>
#include <android/hardware/camera2/ICameraDeviceUser.h>
#include <aidl/android/hardware/common/fmq/MQDescriptor.h>
@@ -40,6 +41,7 @@
using namespace android;
using ::android::AidlMessageQueue;
+using ndk::ScopedAParcel;
using ResultMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
class FMQReader {
@@ -75,15 +77,16 @@
static jlong CameraDevice_createFMQReader(JNIEnv *env, jclass thiz,
jobject resultParcel) {
- AParcel *resultAParcel = AParcel_fromJavaParcel(env, resultParcel);
- if (resultAParcel == nullptr) {
+ ScopedAParcel sResultAParcel(AParcel_fromJavaParcel(env, resultParcel));
+ if (sResultAParcel.get() == nullptr) {
ALOGE("%s: Error creating result parcel", __FUNCTION__);
return 0;
}
- AParcel_setDataPosition(resultAParcel, 0);
+
+ AParcel_setDataPosition(sResultAParcel.get(), 0);
MQDescriptor<int8_t, SynchronizedReadWrite> resultMQ;
- if (resultMQ.readFromParcel(resultAParcel) != OK) {
+ if (resultMQ.readFromParcel(sResultAParcel.get()) != OK) {
ALOGE("%s: read from result parcel failed", __FUNCTION__);
return 0;
}
diff --git a/core/jni/platform/darwin/libandroid_runtime_export.exp b/core/jni/platform/darwin/libandroid_runtime_export.exp
new file mode 100644
index 0000000..00a7585
--- /dev/null
+++ b/core/jni/platform/darwin/libandroid_runtime_export.exp
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# symbols needed for the JNI operations
+_JNI_OnLoad
+_ANativeWindow*
+
+# symbols needed to link with layoutlib_jni
+___android_log*
+__ZNK7android7RefBase*
+__ZN7android4base9SetLogger*
+__ZN7android4base10SetAborter*
+__ZN7android4base11GetProperty*
+__ZN7android4Rect*
+__ZN7android5Fence*
+__ZN7android7RefBase*
+__ZN7android7String*
+__ZN7android10VectorImpl*
+__ZN7android11BufferQueue*
+__ZN7android14AndroidRuntime*
+__ZN7android14sp_report_raceEv*
+__ZN7android15KeyCharacterMap*
+__ZN7android15InputDeviceInfo*
+__ZN7android31android_view_InputDevice_create*
+__ZN7android53android_view_Surface_createFromIGraphicBufferProducer*
diff --git a/core/jni/platform/linux/libandroid_runtime_export.txt b/core/jni/platform/linux/libandroid_runtime_export.txt
new file mode 100644
index 0000000..19e3478
--- /dev/null
+++ b/core/jni/platform/linux/libandroid_runtime_export.txt
@@ -0,0 +1,43 @@
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+{
+ global:
+ # symbols needed for the JNI operations
+ JNI_OnLoad;
+ ANativeWindow*;
+
+ # symbols needed to link with layoutlib_jni
+ __android_log*;
+ _ZNK7android7RefBase*;
+ _ZN7android4base9SetLogger*;
+ _ZN7android4base10SetAborter*;
+ _ZN7android4base11GetProperty*;
+ _ZN7android4Rect*;
+ _ZN7android5Fence*;
+ _ZN7android7RefBase*;
+ _ZN7android7String*;
+ _ZN7android10VectorImpl*;
+ _ZN7android11BufferQueue*;
+ _ZN7android14AndroidRuntime*;
+ _ZN7android14sp_report_raceEv*;
+ _ZN7android15KeyCharacterMap*;
+ _ZN7android15InputDeviceInfo*;
+ _ZN7android31android_view_InputDevice_create*;
+ _ZN7android53android_view_Surface_createFromIGraphicBufferProducer*;
+ local:
+ *;
+};
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index e28b646..e6295ea 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -100,4 +100,7 @@
<!-- Whether to enable scaling and fading animation to scrollviews while scrolling.
P.S this is a change only intended for wear devices. -->
<bool name="config_enableViewGroupScalingFading">true</bool>
+
+ <!-- Allow the gesture to double tap the power button to trigger a target action. -->
+ <bool name="config_doubleTapPowerGestureEnabled">false</bool>
</resources>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 9effeec..ca6ad6f 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -105,6 +105,7 @@
import com.android.internal.R;
import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.widget.NotificationProgressModel;
import junit.framework.Assert;
@@ -2414,7 +2415,7 @@
@Test
@EnableFlags(Flags.FLAG_API_RICH_ONGOING)
- public void progressStyle_getProgressMax_nooSegments_returnsDefault() {
+ public void progressStyle_getProgressMax_noSegments_returnsDefault() {
final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
progressStyle.setProgressSegments(Collections.emptyList());
assertThat(progressStyle.getProgressMax()).isEqualTo(100);
@@ -2459,6 +2460,211 @@
@Test
@EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_getProgressMax_onSegmentLimitExceeded_returnsSumOfSegmentLength() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // limit is 10 for ProgressStyle
+ for (int i = 0; i < 30; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10));
+ }
+
+ // THEN
+ assertThat(progressStyle.getProgressMax()).isEqualTo(300);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_addProgressSegment_dropsInvalidSegments() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Segments should be a positive integer.
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(0));
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(-1));
+
+ // THEN
+ assertThat(progressStyle.getProgressSegments()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_setProgressSegment_dropsInvalidSegments() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Segments should be a positive integer.
+ progressStyle
+ .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(0),
+ new Notification.ProgressStyle.Segment(-1)));
+
+ // THEN
+ assertThat(progressStyle.getProgressSegments()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_addProgressPoint_dropsNegativePoints() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Points should not be a negative integer.
+ progressStyle
+ .addProgressPoint(new Notification.ProgressStyle.Point(-1))
+ .addProgressPoint(new Notification.ProgressStyle.Point(-100));
+
+ // THEN
+ assertThat(progressStyle.getProgressPoints()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_setProgressPoint_dropsNegativePoints() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ // Points should not be a negative integer.
+ progressStyle
+ .setProgressPoints(List.of(new Notification.ProgressStyle.Point(-1),
+ new Notification.ProgressStyle.Point(-100)));
+
+ // THEN
+ assertThat(progressStyle.getProgressPoints()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_ignoresPointsExceedingMax() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ progressStyle.addProgressSegment(new Notification.ProgressStyle.Segment(100));
+ // Points should not larger than progress maximum.
+ progressStyle
+ .addProgressPoint(new Notification.ProgressStyle.Point(101))
+ .addProgressPoint(new Notification.ProgressStyle.Point(500));
+
+ // THEN
+ assertThat(progressStyle.createProgressModel(Color.BLUE, Color.RED).getPoints()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_ignoresOverLimitPoints() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ progressStyle.addProgressSegment(new Notification.ProgressStyle.Segment(100));
+
+ // maximum 4 points are going to be rendered.
+ progressStyle
+ .addProgressPoint(new Notification.ProgressStyle.Point(0))
+ .addProgressPoint(new Notification.ProgressStyle.Point(20))
+ .addProgressPoint(new Notification.ProgressStyle.Point(150))
+ .addProgressPoint(new Notification.ProgressStyle.Point(50))
+ .addProgressPoint(new Notification.ProgressStyle.Point(70))
+ .addProgressPoint(new Notification.ProgressStyle.Point(80))
+ .addProgressPoint(new Notification.ProgressStyle.Point(90))
+ .addProgressPoint(new Notification.ProgressStyle.Point(95))
+ .addProgressPoint(new Notification.ProgressStyle.Point(100));
+ final int backgroundColor = Color.RED;
+ final int defaultProgressColor = Color.BLUE;
+ final int expectedProgressColor = Notification.ProgressStyle.sanitizeProgressColor(
+ /* color = */Notification.COLOR_DEFAULT,
+ /* bg = */backgroundColor,
+ /* defaultColor = */defaultProgressColor);
+
+ // THEN
+ assertThat(progressStyle.createProgressModel(defaultProgressColor, backgroundColor)
+ .getPoints()).isEqualTo(
+ List.of(new Notification.ProgressStyle.Point(0)
+ .setColor(expectedProgressColor),
+ new Notification.ProgressStyle.Point(20)
+ .setColor(expectedProgressColor),
+ new Notification.ProgressStyle.Point(50)
+ .setColor(expectedProgressColor),
+ new Notification.ProgressStyle.Point(70)
+ .setColor(expectedProgressColor)
+ )
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_mergeSegmentsOnOverflow() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+
+ for (int i = 0; i < 15; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10));
+ }
+
+ final NotificationProgressModel progressModel = progressStyle.createProgressModel(
+ Color.BLUE, Color.RED);
+
+ // THEN
+ assertThat(progressModel.getSegments().size()).isEqualTo(1);
+ assertThat(progressModel.getProgressMax()).isEqualTo(150);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_useSegmentColorWhenAllMatch() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ final int segmentColor = Color.YELLOW;
+ final int defaultProgressColor = Color.BLUE;
+ final int backgroundColor = Color.RED;
+ // contrast ensured color for segmentColor.
+ final int expectedSegmentColor = Notification.ProgressStyle.sanitizeProgressColor(
+ /* color = */ segmentColor,
+ /* bg = */ backgroundColor,
+ /* defaultColor = */ defaultProgressColor);
+
+ for (int i = 0; i < 15; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10)
+ .setColor(segmentColor));
+ }
+
+ final NotificationProgressModel progressModel = progressStyle.createProgressModel(
+ defaultProgressColor, backgroundColor);
+
+ // THEN
+ assertThat(progressModel.getSegments())
+ .isEqualTo(List.of(new Notification.ProgressStyle.Segment(150)
+ .setColor(expectedSegmentColor)));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_createProgressModel_useDefaultColorWhenAllNotMatch() {
+ // GIVEN
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ final int defaultProgressColor = Color.BLUE;
+ final int backgroundColor = Color.RED;
+ // contrast ensured color for default progress color.
+ final int expectedSegmentColor = Notification.ProgressStyle.sanitizeProgressColor(
+ /* color = */ defaultProgressColor,
+ /* bg = */ backgroundColor,
+ /* defaultColor = */ defaultProgressColor);
+
+ for (int i = 0; i < 15; i++) {
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(5)
+ .setColor(Color.BLUE))
+ .addProgressSegment(new Notification.ProgressStyle.Segment(5)
+ .setColor(Color.CYAN));
+ }
+
+ final NotificationProgressModel progressModel = progressStyle.createProgressModel(
+ defaultProgressColor, backgroundColor);
+
+ // THEN
+ assertThat(progressModel.getSegments())
+ .isEqualTo(List.of(new Notification.ProgressStyle.Segment(150)
+ .setColor(expectedSegmentColor)));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
public void progressStyle_indeterminate_defaultValueFalse() {
final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 911b7ce..10a85bc 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -120,7 +120,8 @@
doReturn(newDisplayInfo).when(mIDisplayManager).getDisplayInfo(123);
mDisplayManager.registerDisplayListener(mListener, mHandler,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
null /* packageName */);
mController.onDisplayChanged(123);
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 7a5b306..a270848 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -73,9 +73,13 @@
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
+ private static final long DISPLAY_CHANGE_EVENTS =
+ DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+
private static final long ALL_DISPLAY_EVENTS =
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DISPLAY_CHANGE_EVENTS
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
@Mock
@@ -127,7 +131,7 @@
final DisplayInfo newDisplayInfo = new DisplayInfo();
newDisplayInfo.rotation++;
doReturn(newDisplayInfo).when(mDisplayManager).getDisplayInfo(displayId);
- callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
Mockito.verify(mDisplayListener).onDisplayChanged(eq(displayId));
Mockito.verifyNoMoreInteractions(mDisplayListener);
@@ -186,8 +190,8 @@
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED, null);
- callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ & ~DISPLAY_CHANGE_EVENTS, null);
+ callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
@@ -257,8 +261,7 @@
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
null /* packageName */);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener2, mHandler,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
- null /* packageName */);
+ DISPLAY_CHANGE_EVENTS, null /* packageName */);
mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
waitForHandler();
@@ -304,8 +307,7 @@
assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
mDisplayManagerGlobal
.mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_ADDED, 0));
- assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED,
- mDisplayManagerGlobal
+ assertEquals(DISPLAY_CHANGE_EVENTS, mDisplayManagerGlobal
.mapFlagsToInternalEventFlag(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED, 0));
assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
mDisplayManagerGlobal
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 4a227d8..255d09b 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -86,7 +86,7 @@
verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1)
val display2 = display1.children[0]
- verifyDisplay(display1.children[0], displayId2, width2, height2, POSITION_TOP,
+ verifyDisplay(display2, displayId2, width2, height2, POSITION_TOP,
offset = width1 / 2 - width2 / 2, noOfChildren = 1)
var display = display2
@@ -99,6 +99,76 @@
}
@Test
+ fun updateDisplay() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+
+ val newWidth = 1000f
+ val newHeight = 500f
+ topology.addDisplay(displayId, width, height)
+ assertThat(topology.updateDisplay(displayId, newWidth, newHeight)).isTrue()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, newWidth, newHeight, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplay_notUpdated() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+ topology.addDisplay(displayId, width, height)
+
+ // Same size
+ assertThat(topology.updateDisplay(displayId, width, height)).isFalse()
+
+ // Display doesn't exist
+ assertThat(topology.updateDisplay(/* displayId= */ 100, width, height)).isFalse()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplayDoesNotAffectDefaultTopology() {
+ val width1 = 700f
+ val height = 600f
+ topology.addDisplay(/* displayId= */ 1, width1, height)
+
+ val width2 = 800f
+ val noOfDisplays = 30
+ for (i in 2..noOfDisplays) {
+ topology.addDisplay(/* displayId= */ i, width2, height)
+ }
+
+ val displaysToUpdate = arrayOf(3, 7, 18)
+ val newWidth = 1000f
+ val newHeight = 1500f
+ for (i in displaysToUpdate) {
+ assertThat(topology.updateDisplay(/* displayId= */ i, newWidth, newHeight)).isTrue()
+ }
+
+ assertThat(topology.primaryDisplayId).isEqualTo(1)
+
+ val display1 = topology.root!!
+ verifyDisplay(display1, id = 1, width1, height, noOfChildren = 1)
+
+ val display2 = display1.children[0]
+ verifyDisplay(display2, id = 2, width2, height, POSITION_TOP,
+ offset = width1 / 2 - width2 / 2, noOfChildren = 1)
+
+ var display = display2
+ for (i in 3..noOfDisplays) {
+ display = display.children[0]
+ // The last display should have no children
+ verifyDisplay(display, id = i, if (i in displaysToUpdate) newWidth else width2,
+ if (i in displaysToUpdate) newHeight else height, POSITION_RIGHT, offset = 0f,
+ noOfChildren = if (i < noOfDisplays) 1 else 0)
+ }
+ }
+
+ @Test
fun removeDisplays() {
val displayId1 = 1
val width1 = 800f
@@ -117,7 +187,7 @@
}
var removedDisplays = arrayOf(20)
- topology.removeDisplay(20)
+ assertThat(topology.removeDisplay(20)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -139,11 +209,11 @@
noOfChildren = if (i < noOfDisplays) 1 else 0)
}
- topology.removeDisplay(22)
+ assertThat(topology.removeDisplay(22)).isTrue()
removedDisplays += 22
- topology.removeDisplay(23)
+ assertThat(topology.removeDisplay(23)).isTrue()
removedDisplays += 23
- topology.removeDisplay(25)
+ assertThat(topology.removeDisplay(25)).isTrue()
removedDisplays += 25
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -174,7 +244,7 @@
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(displayId)
+ assertThat(topology.removeDisplay(displayId)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
assertThat(topology.root).isNull()
@@ -187,7 +257,7 @@
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(3)
+ assertThat(topology.removeDisplay(3)).isFalse()
assertThat(topology.primaryDisplayId).isEqualTo(displayId)
verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
@@ -203,7 +273,7 @@
topology = DisplayTopology(/* root= */ null, displayId2)
topology.addDisplay(displayId1, width, height)
topology.addDisplay(displayId2, width, height)
- topology.removeDisplay(displayId2)
+ assertThat(topology.removeDisplay(displayId2)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
verifyDisplay(topology.root!!, displayId1, width, height, noOfChildren = 0)
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index ed9fc1c..18ab52d 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -25,7 +25,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -861,10 +861,10 @@
assertEquals(mViewRootImpl.getFrameRateCompatibility(),
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
assertFalse(mViewRootImpl.isFrameRateConflicted());
- mViewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
+ mViewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_AT_LEAST);
if (toolkitFrameRateVelocityMappingReadOnly()) {
assertEquals(24, mViewRootImpl.getPreferredFrameRate(), 0.1);
- assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+ assertEquals(FRAME_RATE_COMPATIBILITY_AT_LEAST,
mViewRootImpl.getFrameRateCompatibility());
assertFalse(mViewRootImpl.isFrameRateConflicted());
} else {
@@ -888,10 +888,10 @@
sInstrumentation.runOnMainSync(() -> {
assertFalse(mViewRootImpl.isFrameRateConflicted());
- mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_AT_LEAST);
if (toolkitFrameRateVelocityMappingReadOnly()) {
assertEquals(60, mViewRootImpl.getPreferredFrameRate(), 0.1);
- assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+ assertEquals(FRAME_RATE_COMPATIBILITY_AT_LEAST,
mViewRootImpl.getFrameRateCompatibility());
} else {
assertEquals(FRAME_RATE_CATEGORY_HIGH,
@@ -904,7 +904,7 @@
mViewRootImpl.getFrameRateCompatibility());
// Should be false since 60 is a divisor of 120.
assertFalse(mViewRootImpl.isFrameRateConflicted());
- mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_AT_LEAST);
assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1);
// compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
// since the frame rate 60 is smaller than 120.
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index 00b4f46..d1fbc77c 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -31,6 +31,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
@@ -44,20 +45,27 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.UserInfo;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManagerGlobal;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
+import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.widget.flags.Flags;
import com.google.android.collect.Lists;
@@ -76,6 +84,8 @@
public class LockPatternUtilsTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ILockSettings mLockSettings;
private static final int USER_ID = 1;
@@ -395,4 +405,156 @@
}
};
}
+
+ private InputManagerGlobal.TestSession configureExternalHardwareTest(InputDevice[] devices)
+ throws RemoteException {
+ final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext());
+ final ILockSettings ils = mock(ILockSettings.class);
+ when(ils.getBoolean(anyString(), anyBoolean(), anyInt())).thenThrow(RemoteException.class);
+ mLockPatternUtils = new LockPatternUtils(context, ils);
+
+ IInputManager inputManagerMock = mock(IInputManager.class);
+
+ int[] deviceIds = new int[devices.length];
+
+ for (int i = 0; i < devices.length; i++) {
+ when(inputManagerMock.getInputDevice(i)).thenReturn(devices[i]);
+ }
+
+ when(inputManagerMock.getInputDeviceIds()).thenReturn(deviceIds);
+ InputManagerGlobal.TestSession session =
+ InputManagerGlobal.createTestSession(inputManagerMock);
+
+ return session;
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_noDevicesAttached() throws RemoteException {
+ InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_noEnabledDeviceAttached() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(false);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withoutHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(true).setSources(InputDevice.SOURCE_TOUCHSCREEN);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withoutFullHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withHwKeyboardOldDefault() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noDevices() throws RemoteException {
+ InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noEnabledDevices() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(false);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noPointingDevices() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_TOUCHSCREEN);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_externalPointingDevice() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_CLASS_POINTER);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_externalPointingDeviceOldDefault() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_CLASS_POINTER);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 21c44c9..4bcec70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -571,9 +571,9 @@
// For flexible split, expand app offscreen as well
if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
- bounds1.top = bounds1.bottom - bounds2.width();
+ bounds1.top = bounds1.bottom - bounds2.height();
} else {
- bounds2.bottom = bounds2.top + bounds1.width();
+ bounds2.bottom = bounds2.top + bounds1.height();
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index dfa2d9b..9a60cfe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -38,6 +38,7 @@
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
@@ -236,6 +237,7 @@
) {
// Sessions is finishing, log task updates followed by an exit event
identifyAndLogTaskUpdates(
+ transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
)
@@ -252,12 +254,14 @@
desktopModeEventLogger.logSessionEnter(getEnterReason(transitionInfo))
identifyAndLogTaskUpdates(
+ transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
)
} else if (isSessionActive) {
// Session is neither starting, nor finishing, log task updates if there are any
identifyAndLogTaskUpdates(
+ transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
)
@@ -270,6 +274,7 @@
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
+ transitionInfo: TransitionInfo,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
) {
@@ -304,9 +309,19 @@
// find old tasks that were removed
preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
- desktopModeEventLogger.logTaskRemoved(
- buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())
- )
+ val minimizeReason =
+ if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
+ MinimizeReason.MINIMIZE_BUTTON
+ } else {
+ null
+ }
+ val taskUpdate =
+ buildTaskUpdateForTask(
+ taskInfo,
+ postTransitionVisibleFreeformTasks.size(),
+ minimizeReason,
+ )
+ desktopModeEventLogger.logTaskRemoved(taskUpdate)
Trace.setCounter(
Trace.TRACE_TAG_WINDOW_MANAGER,
VISIBLE_TASKS_COUNTER_NAME,
@@ -320,7 +335,11 @@
}
}
- private fun buildTaskUpdateForTask(taskInfo: TaskInfo, visibleTasks: Int): TaskUpdate {
+ private fun buildTaskUpdateForTask(
+ taskInfo: TaskInfo,
+ visibleTasks: Int,
+ minimizeReason: MinimizeReason? = null,
+ ): TaskUpdate {
val screenBounds = taskInfo.configuration.windowConfiguration.bounds
val positionInParent = taskInfo.positionInParent
return TaskUpdate(
@@ -331,6 +350,7 @@
taskX = positionInParent.x,
taskY = positionInParent.y,
visibleTaskCount = visibleTasks,
+ minimizeReason = minimizeReason,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 606a729..9019134 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -82,7 +82,7 @@
// For portrait resizeable activities, respect apps fullscreen width but
// apply ideal size height.
Size(
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.width(),
idealSize.height,
)
} else {
@@ -104,7 +104,7 @@
// apply custom app width.
Size(
customPortraitWidthForLandscapeApp,
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.height(),
)
} else {
// For portrait resizeable activities, simply apply ideal size.
@@ -196,13 +196,8 @@
/** Calculates the aspect ratio of an activity from its fullscreen bounds. */
fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
- val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth
- val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight
- if (taskInfo.appCompatTaskInfo.isTopActivityLetterboxed || !taskInfo.canChangeAspectRatio) {
- return maxOf(appLetterboxWidth, appLetterboxHeight) /
- minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
- }
- val appBounds = taskInfo.configuration.windowConfiguration.appBounds ?: return 1f
+ if (taskInfo.appCompatTaskInfo.topActivityAppBounds.isEmpty) return 1f
+ val appBounds = taskInfo.appCompatTaskInfo.topActivityAppBounds
return maxOf(appBounds.height(), appBounds.width()) /
minOf(appBounds.height(), appBounds.width()).toFloat()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index a611fe1..c4ff87d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -74,7 +74,7 @@
mInitialDragData = data;
mInitialDragFlags = dragFlags;
displayLayout = dispLayout;
- hideDragSourceTaskId = data.getDescription().getExtras() != null
+ hideDragSourceTaskId = data != null && data.getDescription().getExtras() != null
? data.getDescription().getExtras().getInt(EXTRA_HIDE_DRAG_SOURCE_TASK_ID, -1)
: -1;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsWithKeyboard.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsWithKeyboard.kt
new file mode 100644
index 0000000..56f1dcb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsWithKeyboard.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_LAST_APP
+import com.android.wm.shell.scenarios.MinimizeAppWindows
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Minimize app windows by pressing META + -.
+ *
+ * Assert that the app windows gets hidden.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MinimizeAppsWithKeyboard : MinimizeAppWindows(usingKeyboard = true) {
+ @ExpectedScenarios(["MINIMIZE_APP", "MINIMIZE_LAST_APP"])
+ @Test
+ override fun minimizeAllAppWindows() = super.minimizeAllAppWindows()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(MINIMIZE_APP)
+ .use(MINIMIZE_LAST_APP)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
index 971637b..835559c 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
@@ -43,7 +43,10 @@
*/
@Ignore("Test Base Class")
abstract class MinimizeAppWindows
-constructor(private val rotation: Rotation = Rotation.ROTATION_0) {
+constructor(
+ private val rotation: Rotation = Rotation.ROTATION_0,
+ private val usingKeyboard: Boolean = false
+) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
private val wmHelper = WindowManagerStateHelper(instrumentation)
@@ -68,9 +71,9 @@
@Test
open fun minimizeAllAppWindows() {
- testApp3.minimizeDesktopApp(wmHelper, device)
- testApp2.minimizeDesktopApp(wmHelper, device)
- testApp1.minimizeDesktopApp(wmHelper, device)
+ testApp3.minimizeDesktopApp(wmHelper, device, usingKeyboard = usingKeyboard)
+ testApp2.minimizeDesktopApp(wmHelper, device, usingKeyboard = usingKeyboard)
+ testApp1.minimizeDesktopApp(wmHelper, device, usingKeyboard = usingKeyboard)
}
@After
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 abd7078..c0ff2f0 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
@@ -45,6 +45,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -77,6 +78,12 @@
doReturn(DISPLAY_HEIGHT).whenever(displayLayout).height()
}
+ @After
+ fun tearDown() {
+ clearInvocations(staticMockMarker(FrameworkStatsLog::class.java))
+ clearInvocations(staticMockMarker(EventLogTags::class.java))
+ }
+
@Test
fun logSessionEnter_logsEnterReasonWithNewSessionId() {
desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
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 43684fb..0154ff3 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
@@ -46,6 +46,7 @@
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
@@ -566,7 +567,10 @@
assertFalse(transitionObserver.isSessionActive)
verify(desktopModeEventLogger, times(1)).logSessionExit(eq(ExitReason.TASK_MINIMIZED))
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(DEFAULT_TASK_UPDATE))
+ verify(desktopModeEventLogger, times(1))
+ .logTaskRemoved(
+ eq(DEFAULT_TASK_UPDATE.copy(minimizeReason = MinimizeReason.MINIMIZE_BUTTON))
+ )
verifyZeroInteractions(desktopModeEventLogger)
}
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 4f37180b..e1c2153 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
@@ -4160,8 +4160,7 @@
screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
configuration.windowConfiguration.appBounds = bounds
}
- appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
- appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, bounds.width(), bounds.height())
isResizeable = false
}
@@ -4879,15 +4878,19 @@
appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
if (deviceOrientation == ORIENTATION_LANDSCAPE) {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_LONG,
+ DISPLAY_DIMENSION_SHORT,
+ )
} else {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_SHORT,
+ DISPLAY_DIMENSION_LONG,
+ )
}
if (shouldLetterbox) {
@@ -4897,17 +4900,15 @@
screenOrientation == SCREEN_ORIENTATION_PORTRAIT
) {
// Letterbox to portrait size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1200, 1600)
} else if (
deviceOrientation == ORIENTATION_PORTRAIT &&
screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
) {
// Letterbox to landscape size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1600, 1200)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
index 3d59342..8ccca07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt
@@ -59,6 +59,13 @@
}
@Test
+ fun testNullClipData() {
+ // Start a new drag session with null data
+ val session = DragSession(activityTaskManager, displayLayout, null, 0)
+ assertThat(session.hideDragSourceTaskId).isEqualTo(-1)
+ }
+
+ @Test
fun testGetRunningTask() {
// Set up running tasks
val runningTasks = listOf(
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index e618245..5955915 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -1420,18 +1420,20 @@
Mutex AssetManager::SharedZip::gLock;
DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen;
-AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
- : mPath(path), mZipFile(NULL), mModWhen(modWhen),
- mResourceTableAsset(NULL), mResourceTable(NULL)
-{
- if (kIsDebug) {
- ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str());
- }
- ALOGV("+++ opening zip '%s'\n", mPath.c_str());
- mZipFile = ZipFileRO::open(mPath.c_str());
- if (mZipFile == NULL) {
- ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
- }
+AssetManager::SharedZip::SharedZip(const String8& path, ModDate modWhen)
+ : mPath(path),
+ mZipFile(NULL),
+ mModWhen(modWhen),
+ mResourceTableAsset(NULL),
+ mResourceTable(NULL) {
+ if (kIsDebug) {
+ ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str());
+ }
+ ALOGV("+++ opening zip '%s'\n", mPath.c_str());
+ mZipFile = ZipFileRO::open(mPath.c_str());
+ if (mZipFile == NULL) {
+ ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
+ }
}
AssetManager::SharedZip::SharedZip(int fd, const String8& path)
@@ -1453,7 +1455,7 @@
bool createIfNotPresent)
{
AutoMutex _l(gLock);
- time_t modWhen = getFileModDate(path.c_str());
+ auto modWhen = getFileModDate(path.c_str());
sp<SharedZip> zip = gOpen.valueFor(path).promote();
if (zip != NULL && zip->mModWhen == modWhen) {
return zip;
@@ -1520,8 +1522,8 @@
bool AssetManager::SharedZip::isUpToDate()
{
- time_t modWhen = getFileModDate(mPath.c_str());
- return mModWhen == modWhen;
+ auto modWhen = getFileModDate(mPath.c_str());
+ return mModWhen == modWhen;
}
void AssetManager::SharedZip::addOverlay(const asset_path& ap)
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index ce0985b..376c881 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -280,21 +280,21 @@
~SharedZip();
private:
- SharedZip(const String8& path, time_t modWhen);
- SharedZip(int fd, const String8& path);
- SharedZip(); // <-- not implemented
+ SharedZip(const String8& path, ModDate modWhen);
+ SharedZip(int fd, const String8& path);
+ SharedZip(); // <-- not implemented
- String8 mPath;
- ZipFileRO* mZipFile;
- time_t mModWhen;
+ String8 mPath;
+ ZipFileRO* mZipFile;
+ ModDate mModWhen;
- Asset* mResourceTableAsset;
- ResTable* mResourceTable;
+ Asset* mResourceTableAsset;
+ ResTable* mResourceTable;
- Vector<asset_path> mOverlays;
+ Vector<asset_path> mOverlays;
- static Mutex gLock;
- static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
+ static Mutex gLock;
+ static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
};
/*
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index e213fbd..ac75eb3 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -25,8 +25,9 @@
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
#include "androidfw/ConfigDescription.h"
-#include "androidfw/StringPiece.h"
#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+#include "androidfw/misc.h"
#include "utils/ByteOrder.h"
namespace android {
@@ -213,7 +214,7 @@
android::base::unique_fd idmap_fd_;
std::string_view overlay_apk_path_;
std::string_view target_apk_path_;
- time_t idmap_last_mod_time_;
+ ModDate idmap_last_mod_time_;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index 077609d..c9ba8a0 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include <sys/types.h>
+#include <time.h>
//
// Handy utility functions and portability code.
//
-#ifndef _LIBS_ANDROID_FW_MISC_H
-#define _LIBS_ANDROID_FW_MISC_H
namespace android {
@@ -41,15 +40,35 @@
} FileType;
/* get the file's type; follows symlinks */
FileType getFileType(const char* fileName);
-/* get the file's modification date; returns -1 w/errno set on failure */
-time_t getFileModDate(const char* fileName);
+
+// MinGW doesn't support nanosecond resolution in stat() modification time, and given
+// that it only matters on the device it's ok to keep it at a seconds level there.
+#ifdef _WIN32
+using ModDate = time_t;
+inline constexpr ModDate kInvalidModDate = ModDate(-1);
+inline constexpr unsigned long long kModDateResolutionNs = 1ull * 1000 * 1000 * 1000;
+inline time_t toTimeT(ModDate m) {
+ return m;
+}
+#else
+using ModDate = timespec;
+inline constexpr ModDate kInvalidModDate = {-1, -1};
+inline constexpr unsigned long long kModDateResolutionNs = 1;
+inline time_t toTimeT(ModDate m) {
+ return m.tv_sec;
+}
+#endif
+
+/* get the file's modification date; returns kInvalidModDate w/errno set on failure */
+ModDate getFileModDate(const char* fileName);
/* same, but also returns -1 if the file has already been deleted */
-time_t getFileModDate(int fd);
+ModDate getFileModDate(int fd);
// Check if |path| or |fd| resides on a readonly filesystem.
bool isReadonlyFilesystem(const char* path);
bool isReadonlyFilesystem(int fd);
-}; // namespace android
+} // namespace android
-#endif // _LIBS_ANDROID_FW_MISC_H
+// Whoever uses getFileModDate() will need this as well
+bool operator==(const timespec& l, const timespec& r);
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 93dcaf5..32f3624 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -28,11 +28,13 @@
#include <sys/vfs.h>
#endif // __linux__
-#include <cstring>
-#include <cstdio>
#include <errno.h>
#include <sys/stat.h>
+#include <cstdio>
+#include <cstring>
+#include <tuple>
+
namespace android {
/*
@@ -73,27 +75,34 @@
}
}
-/*
- * Get a file's modification date.
- */
-time_t getFileModDate(const char* fileName) {
- struct stat sb;
- if (stat(fileName, &sb) < 0) {
- return (time_t)-1;
- }
- return sb.st_mtime;
+static ModDate getModDate(const struct stat& st) {
+#ifdef _WIN32
+ return st.st_mtime;
+#elif defined(__APPLE__)
+ return st.st_mtimespec;
+#else
+ return st.st_mtim;
+#endif
}
-time_t getFileModDate(int fd) {
- struct stat sb;
- if (fstat(fd, &sb) < 0) {
- return (time_t)-1;
- }
- if (sb.st_nlink <= 0) {
- errno = ENOENT;
- return (time_t)-1;
- }
- return sb.st_mtime;
+ModDate getFileModDate(const char* fileName) {
+ struct stat sb;
+ if (stat(fileName, &sb) < 0) {
+ return kInvalidModDate;
+ }
+ return getModDate(sb);
+}
+
+ModDate getFileModDate(int fd) {
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ return kInvalidModDate;
+ }
+ if (sb.st_nlink <= 0) {
+ errno = ENOENT;
+ return kInvalidModDate;
+ }
+ return getModDate(sb);
}
#ifndef __linux__
@@ -124,4 +133,8 @@
}
#endif // __linux__
-}; // namespace android
+} // namespace android
+
+bool operator==(const timespec& l, const timespec& r) {
+ return std::tie(l.tv_sec, l.tv_nsec) == std::tie(r.tv_sec, l.tv_nsec);
+}
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 60aa7d8..cb2e56f 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <chrono>
+#include <thread>
+
#include "android-base/file.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/AssetManager2.h"
@@ -27,6 +30,7 @@
#include "data/overlayable/R.h"
#include "data/system/R.h"
+using namespace std::chrono_literals;
using ::testing::NotNull;
namespace overlay = com::android::overlay;
@@ -218,10 +222,13 @@
unlink(temp_file.path);
ASSERT_FALSE(apk_assets->IsUpToDate());
- sleep(2);
+
+ const auto sleep_duration =
+ std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull));
+ std::this_thread::sleep_for(sleep_duration);
base::WriteStringToFile("hello", temp_file.path);
- sleep(2);
+ std::this_thread::sleep_for(sleep_duration);
ASSERT_FALSE(apk_assets->IsUpToDate());
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 09f40e0..f42017d 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -226,6 +226,10 @@
@GuardedBy("mSessionLock")
private final ArrayMap<String, MediaStreams> mOngoingMediaStreams = new ArrayMap<>();
+ @GuardedBy("mSessionLock")
+ private final ArrayMap<String, RoutingSessionInfo> mPendingSystemSessionReleases =
+ new ArrayMap<>();
+
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
}
@@ -358,7 +362,9 @@
* @return a {@link MediaStreams} instance that holds the media streams to route as part of the
* newly created routing session. May be null if system media capture failed, in which case
* you can ignore the return value, as you will receive a call to {@link #onReleaseSession}
- * where you can clean up this session
+ * where you can clean up this session. {@link AudioRecord#startRecording()} must be called
+ * immediately on {@link MediaStreams#getAudioRecord()} after calling this method, in order
+ * to start streaming audio to the receiver.
* @hide
*/
// TODO: b/362507305 - Unhide once the implementation and CTS are in place.
@@ -417,7 +423,7 @@
}
AudioFormat audioFormat = formats.mAudioFormat;
- var mediaStreamsBuilder = new MediaStreams.Builder();
+ var mediaStreamsBuilder = new MediaStreams.Builder(sessionInfo);
if (audioFormat != null) {
populateAudioStream(audioFormat, uid, mediaStreamsBuilder);
}
@@ -458,7 +464,6 @@
if (uid != Process.INVALID_UID) {
audioMixingRuleBuilder.addMixRule(AudioMixingRule.RULE_MATCH_UID, uid);
}
-
AudioMix mix =
new AudioMix.Builder(audioMixingRuleBuilder.build())
.setFormat(audioFormat)
@@ -471,7 +476,11 @@
Log.e(TAG, "Couldn't fetch the audio manager.");
return;
}
- audioManager.registerAudioPolicy(audioPolicy);
+ int audioPolicyResult = audioManager.registerAudioPolicy(audioPolicy);
+ if (audioPolicyResult != AudioManager.SUCCESS) {
+ Log.e(TAG, "Failed to register the audio policy.");
+ return;
+ }
var audioRecord = audioPolicy.createAudioRecordSink(mix);
if (audioRecord == null) {
Log.e(TAG, "Audio record creation failed.");
@@ -521,8 +530,14 @@
RoutingSessionInfo sessionInfo;
synchronized (mSessionLock) {
sessionInfo = mSessionInfos.remove(sessionId);
- maybeReleaseMediaStreams(sessionId);
-
+ if (Flags.enableMirroringInMediaRouter2()) {
+ if (sessionInfo == null) {
+ sessionInfo = maybeReleaseMediaStreams(sessionId);
+ }
+ if (sessionInfo == null) {
+ sessionInfo = mPendingSystemSessionReleases.remove(sessionId);
+ }
+ }
if (sessionInfo == null) {
Log.w(TAG, "notifySessionReleased: Ignoring unknown session info.");
return;
@@ -539,18 +554,26 @@
}
}
- /** Releases any system media routing resources associated with the given {@code sessionId}. */
- private void maybeReleaseMediaStreams(String sessionId) {
+ /**
+ * Releases any system media routing resources associated with the given {@code sessionId}.
+ *
+ * @return The {@link RoutingSessionInfo} that corresponds to the released media streams, or
+ * null if no streams were released.
+ */
+ @Nullable
+ private RoutingSessionInfo maybeReleaseMediaStreams(String sessionId) {
if (!Flags.enableMirroringInMediaRouter2()) {
- return;
+ return null;
}
synchronized (mSessionLock) {
var streams = mOngoingMediaStreams.remove(sessionId);
if (streams != null) {
releaseAudioStream(streams.mAudioPolicy, streams.mAudioRecord);
// TODO: b/380431086: Release the video stream once implemented.
+ return streams.mSessionInfo;
}
}
+ return null;
}
// We cannot reach the code that requires MODIFY_AUDIO_ROUTING without holding it.
@@ -1019,12 +1042,16 @@
if (!checkCallerIsSystem()) {
return;
}
- if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
- return;
+ synchronized (mSessionLock) {
+ // We proactively release the system media routing session resources when the
+ // system requests it, to ensure it happens immediately.
+ RoutingSessionInfo releasedSession = maybeReleaseMediaStreams(sessionId);
+ if (releasedSession != null) {
+ mPendingSystemSessionReleases.put(sessionId, releasedSession);
+ } else if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
+ return;
+ }
}
- // We proactively release the system media routing once the system requests it, to
- // ensure it happens immediately.
- maybeReleaseMediaStreams(sessionId);
addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
@@ -1047,9 +1074,19 @@
@Nullable private final AudioPolicy mAudioPolicy;
@Nullable private final AudioRecord mAudioRecord;
+ /**
+ * Holds the last {@link RoutingSessionInfo} associated with these streams.
+ *
+ * @hide
+ */
+ @GuardedBy("MediaRoute2ProviderService.this.mSessionLock")
+ @NonNull
+ private RoutingSessionInfo mSessionInfo;
+
// TODO: b/380431086: Add the video equivalent.
private MediaStreams(Builder builder) {
+ this.mSessionInfo = builder.mSessionInfo;
this.mAudioPolicy = builder.mAudioPolicy;
this.mAudioRecord = builder.mAudioRecord;
}
@@ -1070,9 +1107,19 @@
*/
public static final class Builder {
+ @NonNull private RoutingSessionInfo mSessionInfo;
@Nullable private AudioPolicy mAudioPolicy;
@Nullable private AudioRecord mAudioRecord;
+ /**
+ * Constructor.
+ *
+ * @param sessionInfo The {@link RoutingSessionInfo} associated with these streams.
+ */
+ Builder(@NonNull RoutingSessionInfo sessionInfo) {
+ mSessionInfo = requireNonNull(sessionInfo);
+ }
+
/** Populates system media audio-related structures. */
public Builder setAudioStream(
@NonNull AudioPolicy audioPolicy, @NonNull AudioRecord audioRecord) {
diff --git a/nfc/tests/src/android/nfc/tech/NfcATest.java b/nfc/tests/src/android/nfc/tech/NfcATest.java
new file mode 100644
index 0000000..40076eb
--- /dev/null
+++ b/nfc/tests/src/android/nfc/tech/NfcATest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import static android.nfc.tech.NfcA.EXTRA_ATQA;
+import static android.nfc.tech.NfcA.EXTRA_SAK;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.nfc.ErrorCodes;
+import android.nfc.INfcTag;
+import android.nfc.Tag;
+import android.nfc.TransceiveResult;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+
+public class NfcATest {
+ @Mock
+ private Tag mMockTag;
+ @Mock
+ private INfcTag mMockTagService;
+ @Mock
+ private Bundle mMockBundle;
+ private NfcA mNfcA;
+ private final byte[] mSampleArray = new byte[] {1, 2, 3};
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ when(mMockBundle.getShort(EXTRA_SAK)).thenReturn((short) 1);
+ when(mMockBundle.getByteArray(EXTRA_ATQA)).thenReturn(mSampleArray);
+ when(mMockTag.getTechExtras(TagTechnology.NFC_A)).thenReturn(mMockBundle);
+
+ mNfcA = new NfcA(mMockTag);
+ }
+
+ @Test
+ public void testGetNfcAWithTech() {
+ Tag mockTag = mock(Tag.class);
+ when(mockTag.getTechExtras(TagTechnology.NFC_A)).thenReturn(mMockBundle);
+ when(mockTag.hasTech(TagTechnology.NFC_A)).thenReturn(true);
+
+ assertNotNull(NfcA.get(mockTag));
+ verify(mockTag).getTechExtras(TagTechnology.NFC_A);
+ verify(mockTag).hasTech(TagTechnology.NFC_A);
+ }
+
+ @Test
+ public void testGetNfcAWithoutTech() {
+ when(mMockTag.hasTech(TagTechnology.NFC_A)).thenReturn(false);
+ assertNull(NfcA.get(mMockTag));
+ }
+
+ @Test
+ public void testGetAtga() {
+ assertNotNull(mNfcA.getAtqa());
+ }
+
+ @Test
+ public void testGetSak() {
+ assertEquals((short) 1, mNfcA.getSak());
+ }
+
+ @Test
+ public void testTransceive() throws IOException, RemoteException {
+ TransceiveResult mockTransceiveResult = mock(TransceiveResult.class);
+ when(mMockTag.getConnectedTechnology()).thenReturn(TagTechnology.NFC_A);
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTag.getServiceHandle()).thenReturn(1);
+ when(mMockTagService.transceive(1, mSampleArray, true))
+ .thenReturn(mockTransceiveResult);
+ when(mockTransceiveResult.getResponseOrThrow()).thenReturn(mSampleArray);
+
+ mNfcA.transceive(mSampleArray);
+ verify(mMockTag).getTagService();
+ verify(mMockTag).getServiceHandle();
+ }
+
+ @Test
+ public void testGetMaxTransceiveLength() throws RemoteException {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ when(mMockTagService.getMaxTransceiveLength(TagTechnology.NFC_A)).thenReturn(1);
+
+ mNfcA.getMaxTransceiveLength();
+ verify(mMockTag).getTagService();
+ }
+
+ @Test
+ public void testSetTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_A, 1000)).thenReturn(
+ ErrorCodes.SUCCESS);
+
+ mNfcA.setTimeout(1000);
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).setTimeout(TagTechnology.NFC_A, 1000);
+ } catch (Exception e) {
+ fail("Unexpected exception during valid setTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSetTimeoutInvalidTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_A, -1)).thenReturn(
+ ErrorCodes.ERROR_TIMEOUT);
+
+ assertThrows(IllegalArgumentException.class, () -> mNfcA.setTimeout(-1));
+ } catch (Exception e) {
+ fail("Unexpected exception during invalid setTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSetTimeoutRemoteException() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.setTimeout(TagTechnology.NFC_A, 1000)).thenThrow(
+ new RemoteException());
+
+ mNfcA.setTimeout(1000); // Should not throw an exception but log it
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).setTimeout(TagTechnology.NFC_A, 1000);
+ } catch (Exception e) {
+ fail("Unexpected exception during RemoteException in setTimeout: " + e.getMessage());
+ }
+
+ }
+
+ @Test
+ public void testGetTimeout() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.getTimeout(TagTechnology.NFC_A)).thenReturn(2000);
+
+ assertEquals(2000, mNfcA.getTimeout());
+ verify(mMockTag).getTagService();
+ verify(mMockTagService).getTimeout(TagTechnology.NFC_A);
+ } catch (Exception e) {
+ fail("Unexpected exception during valid getTimeout: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetTimeoutRemoteException() {
+ when(mMockTag.getTagService()).thenReturn(mMockTagService);
+ try {
+ when(mMockTagService.getTimeout(TagTechnology.NFC_A)).thenThrow(new RemoteException());
+
+ assertEquals(0, mNfcA.getTimeout());
+ } catch (Exception e) {
+ fail("Unexpected exception during RemoteException in getTimeout: " + e.getMessage());
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index cf45231..c9aac91 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.MainThread;
+import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -1643,7 +1644,7 @@
CharSequence appLabel = "";
ApplicationInfo appInfo = null;
try {
- int userId = UserHandle.getUserId(UserHandle.USER_CURRENT);
+ int userId = ActivityManager.getCurrentUser();
appInfo = packageManager.getApplicationInfoAsUser(packageName, 0 /* flags */, userId);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Failed to get app info", e);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 1c4def3..e01cb84 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -16,6 +16,19 @@
package com.android.providers.settings;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_DEVICE_SPECIFIC_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_GLOBAL;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCALE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCK_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_NETWORK_POLICIES;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SECURE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS_2;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SYSTEM;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_SETTINGS_BACKUP_DATA;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -99,22 +112,6 @@
private static final int NULL_SIZE = -1;
private static final float FONT_SCALE_DEF_VALUE = 1.0f;
- private static final String KEY_SYSTEM = "system";
- private static final String KEY_SECURE = "secure";
- private static final String KEY_GLOBAL = "global";
- private static final String KEY_LOCALE = "locale";
- private static final String KEY_LOCK_SETTINGS = "lock_settings";
- private static final String KEY_SOFTAP_CONFIG = "softap_config";
- private static final String KEY_NETWORK_POLICIES = "network_policies";
- private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
- private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
- private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
- // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
- // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
- // restoring this data.
- private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
- private static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
-
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
private static final int STATE_VERSION = 9;
@@ -257,6 +254,7 @@
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (com.android.server.backup.Flags.enableMetricsSettingsBackupAgents()) {
mBackupRestoreEventLogger = this.getBackupRestoreEventLogger();
+ mSettingsHelper.setBackupRestoreEventLogger(mBackupRestoreEventLogger);
numberOfSettingsPerKey = new HashMap<>();
areAgentMetricsEnabled = true;
}
@@ -412,9 +410,7 @@
mSettingsHelper
.setLocaleData(
localeData,
- size,
- mBackupRestoreEventLogger,
- KEY_LOCALE);
+ size);
break;
case KEY_WIFI_CONFIG :
@@ -552,8 +548,7 @@
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
mSettingsHelper
- .setLocaleData(
- buffer, nBytes, mBackupRestoreEventLogger, KEY_LOCALE);
+ .setLocaleData(buffer, nBytes);
// Restore older backups performing the necessary migrations.
if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
new file mode 100644
index 0000000..745c2fb
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Class to store the keys used for backup and restore.
+ */
+final class SettingsBackupRestoreKeys {
+ static final String KEY_UNKNOWN = "unknown";
+ static final String KEY_SYSTEM = "system";
+ static final String KEY_SECURE = "secure";
+ static final String KEY_GLOBAL = "global";
+ static final String KEY_LOCALE = "locale";
+ static final String KEY_LOCK_SETTINGS = "lock_settings";
+ static final String KEY_SOFTAP_CONFIG = "softap_config";
+ static final String KEY_NETWORK_POLICIES = "network_policies";
+ static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
+ static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
+ // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
+ // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
+ // restoring this data.
+ static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
+ static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
+
+ /**
+ * Returns the key corresponding to the given URI.
+ *
+ * @param uri The URI of the setting's destination.
+ * @return The key corresponding to the given URI, or KEY_UNKNOWN if the URI is not recognized.
+ */
+ static String getKeyFromUri(Uri uri) {
+ if (uri.equals(Settings.Secure.CONTENT_URI)) {
+ return KEY_SECURE;
+ } else if (uri.equals(Settings.System.CONTENT_URI)) {
+ return KEY_SYSTEM;
+ } else if (uri.equals(Settings.Global.CONTENT_URI)) {
+ return KEY_GLOBAL;
+ } else {
+ return KEY_UNKNOWN;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 924c151..ab8d739 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -17,6 +17,7 @@
package com.android.providers.settings;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.backup.BackupRestoreEventLogger;
@@ -84,6 +85,7 @@
private Context mContext;
private AudioManager mAudioManager;
private TelephonyManager mTelephonyManager;
+ @Nullable private BackupRestoreEventLogger mBackupRestoreEventLogger;
/**
* A few settings elements are special in that a restore of those values needs to
@@ -741,11 +743,8 @@
*
* @param data the comma separated BCP-47 language tags in bytes.
* @param size the size of the data in bytes.
- * @param backupRestoreEventLogger the logger to log the restore event.
- * @param dataType the data type of the setting for logging purposes.
*/
- /* package */ void setLocaleData(
- byte[] data, int size, BackupRestoreEventLogger backupRestoreEventLogger, String dataType) {
+ /* package */ void setLocaleData(byte[] data, int size) {
final Configuration conf = mContext.getResources().getConfiguration();
// Replace "_" with "-" to deal with older backups.
@@ -772,15 +771,15 @@
am.updatePersistentConfigurationWithAttribution(config, mContext.getOpPackageName(),
mContext.getAttributionTag());
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
- .logItemsRestored(dataType, localeList.size());
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
+ .logItemsRestored(SettingsBackupRestoreKeys.KEY_LOCALE, localeList.size());
}
} catch (RemoteException e) {
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
.logItemsRestoreFailed(
- dataType,
+ SettingsBackupRestoreKeys.KEY_LOCALE,
localeList.size(),
ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA);
}
@@ -795,4 +794,13 @@
AudioManager am = new AudioManager(mContext);
am.reloadAudioSettings();
}
+
+ /**
+ * Sets the backup restore event logger.
+ *
+ * @param backupRestoreEventLogger the logger to log B&R metrics.
+ */
+ void setBackupRestoreEventLogger(BackupRestoreEventLogger backupRestoreEventLogger) {
+ mBackupRestoreEventLogger = backupRestoreEventLogger;
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 55f48e3..f1f03c3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -120,6 +120,7 @@
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
@@ -2914,6 +2915,14 @@
};
}
+ @VisibleForTesting
+ void injectServices(UserManager userManager, IPackageManager packageManager,
+ SystemConfigManager sysConfigManager) {
+ mUserManager = userManager;
+ mPackageManager = packageManager;
+ mSysConfigManager = sysConfigManager;
+ }
+
private static final class Arguments {
private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS =
Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*");
@@ -3080,6 +3089,7 @@
private static final String SSAID_USER_KEY = "userkey";
+ @GuardedBy("mLock")
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
private GenerationRegistry mGenerationRegistry;
@@ -3992,6 +4002,14 @@
}
}
+ @VisibleForTesting
+ void injectSettings(SettingsState settings, int type, int userId) {
+ int key = makeKey(type, userId);
+ synchronized (mLock) {
+ mSettingsStates.put(key, settings);
+ }
+ }
+
private final class MyHandler extends Handler {
private static final int MSG_NOTIFY_URI_CHANGED = 1;
private static final int MSG_NOTIFY_DATA_CHANGED = 2;
@@ -4023,12 +4041,21 @@
}
}
- private final class UpgradeController {
+ @VisibleForTesting
+ final class UpgradeController {
private static final int SETTINGS_VERSION = 226;
private final int mUserId;
+ private final Injector mInjector;
+
public UpgradeController(int userId) {
+ this(/* injector= */ null, userId);
+ }
+
+ @VisibleForTesting
+ UpgradeController(Injector injector, int userId) {
+ mInjector = injector == null ? new Injector() : injector;
mUserId = userId;
}
@@ -6136,8 +6163,8 @@
systemSettings.getSettingLocked(Settings.System.PEAK_REFRESH_RATE);
final Setting minRefreshRateSetting =
systemSettings.getSettingLocked(Settings.System.MIN_REFRESH_RATE);
- float highestRefreshRate = RefreshRateSettingsUtils
- .findHighestRefreshRateForDefaultDisplay(getContext());
+ float highestRefreshRate =
+ mInjector.findHighestRefreshRateForDefaultDisplay(getContext());
if (!peakRefreshRateSetting.isNull()) {
try {
@@ -6318,6 +6345,14 @@
private long getBitMask(int capability) {
return 1 << (capability - 1);
}
+
+ @VisibleForTesting
+ static class Injector {
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(
+ context);
+ }
+ }
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5cd534e..bf3afed 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -107,7 +107,7 @@
* the same lock to grab the current state to write to disk.
* </p>
*/
-final class SettingsState {
+public class SettingsState {
private static final boolean DEBUG = false;
private static final boolean DEBUG_PERSISTENCE = false;
@@ -1838,7 +1838,7 @@
}
}
- class Setting {
+ public class Setting {
private String name;
private String value;
private String defaultValue;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
new file mode 100644
index 0000000..ef537e8
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SettingsBackupRestoreKeys}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SettingsBackupRestoreKeysTest {
+
+ @Test
+ public void getKeyFromUri_secureUri_returnsSecureKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Secure.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SECURE);
+ }
+
+ @Test
+ public void getKeyFromUri_systemUri_returnsSystemKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.System.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SYSTEM);
+ }
+
+ @Test
+ public void getKeyFromUri_globalUri_returnsGlobalKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Global.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_GLOBAL);
+ }
+
+ @Test
+ public void getKeyFromUri_unknownUri_returnsUnknownKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Uri.parse("content://unknown")))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_UNKNOWN);
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
index 9cce431..119b287 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
@@ -16,7 +16,7 @@
package com.android.providers.settings;
-import static android.provider.Settings.Secure.ACCESSIBILITY_ENABLED;
+import static android.provider.Settings.Secure.CONTENT_CAPTURE_ENABLED;
import static android.provider.Settings.Secure.SYNC_PARENT_SOUNDS;
import static android.provider.Settings.System.RINGTONE;
@@ -67,7 +67,7 @@
private static final String SPACE_SYSTEM = "system";
private static final String SPACE_SECURE = "secure";
- private static final String CLONE_TO_MANAGED_PROFILE_SETTING = ACCESSIBILITY_ENABLED;
+ private static final String CLONE_TO_MANAGED_PROFILE_SETTING = CONTENT_CAPTURE_ENABLED;
private static final String CLONE_FROM_PARENT_SETTINGS = RINGTONE;
private static final String SYNC_FROM_PARENT_SETTINGS = SYNC_PARENT_SOUNDS;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
new file mode 100644
index 0000000..26ff376
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import static android.provider.Settings.System.MIN_REFRESH_RATE;
+import static android.provider.Settings.System.PEAK_REFRESH_RATE;
+
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_GLOBAL;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SECURE;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SYSTEM;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.IPackageManager;
+import android.os.Looper;
+import android.os.SystemConfigManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class UpgradeControllerTest {
+ private static final int USER_ID = UserHandle.USER_SYSTEM;
+ private static final float HIGHEST_REFRESH_RATE = 130f;
+
+ private final Context mContext =
+ spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ private final SettingsProvider.SettingsRegistry.UpgradeController.Injector mInjector =
+ new SettingsProvider.SettingsRegistry.UpgradeController.Injector() {
+ @Override
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return HIGHEST_REFRESH_RATE;
+ }
+ };
+ private final SettingsProvider mSettingsProvider = new SettingsProvider() {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+ };
+ private final SettingsProvider.SettingsRegistry mSettingsRegistry =
+ mSettingsProvider.new SettingsRegistry(Looper.getMainLooper());
+ private final SettingsProvider.SettingsRegistry.UpgradeController mUpgradeController =
+ mSettingsRegistry.new UpgradeController(mInjector, USER_ID);
+
+ @Mock
+ private UserManager mUserManager;
+
+ @Mock
+ private IPackageManager mPackageManager;
+
+ @Mock
+ private SystemConfigManager mSysConfigManager;
+
+ @Mock
+ private SettingsState mSystemSettings;
+
+ @Mock
+ private SettingsState mSecureSettings;
+
+ @Mock
+ private SettingsState mGlobalSettings;
+
+ @Mock
+ private SettingsState.Setting mMockSetting;
+
+ @Mock
+ private SettingsState.Setting mPeakRefreshRateSetting;
+
+ @Mock
+ private SettingsState.Setting mMinRefreshRateSetting;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSettingsProvider.attachInfoForTesting(mContext, /* info= */ null);
+ mSettingsProvider.injectServices(mUserManager, mPackageManager, mSysConfigManager);
+ when(mSystemSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mSecureSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mGlobalSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mMockSetting.isNull()).thenReturn(true);
+ when(mMockSetting.getValue()).thenReturn("0");
+
+ when(mSystemSettings.getSettingLocked(PEAK_REFRESH_RATE))
+ .thenReturn(mPeakRefreshRateSetting);
+ when(mSystemSettings.getSettingLocked(MIN_REFRESH_RATE))
+ .thenReturn(mMinRefreshRateSetting);
+
+ mSettingsRegistry.injectSettings(mSystemSettings, SETTINGS_TYPE_SYSTEM, USER_ID);
+ mSettingsRegistry.injectSettings(mSecureSettings, SETTINGS_TYPE_SECURE, USER_ID);
+ mSettingsRegistry.injectSettings(mGlobalSettings, SETTINGS_TYPE_GLOBAL, USER_ID);
+
+ // Lowest version so that all upgrades are run
+ when(mSecureSettings.getVersionLocked()).thenReturn(118);
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_defaultValues() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(true);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(true);
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_enabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+ when(mMinRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Highest refresh rate gets converted to infinity
+ verify(mSystemSettings).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ verify(mSystemSettings).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_disabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn("70f");
+ when(mMinRefreshRateSetting.getValue()).thenReturn("70f");
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 3d250fd..0600fb3 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -85,6 +85,7 @@
filegroup {
name: "SystemUI-tests-broken-robofiles-run",
srcs: [
+ "tests/src/**/systemui/dreams/touch/CommunalTouchHandlerTest.java",
"tests/src/**/systemui/shade/NotificationShadeWindowViewControllerTest.kt",
"tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt",
@@ -418,6 +419,9 @@
"androidx.slice_slice-view",
],
manifest: "AndroidManifest-res.xml",
+ flags_packages: [
+ "com_android_systemui_flags",
+ ],
}
android_library {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1b1c91d..c4d13ba 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1229,6 +1229,14 @@
}
flag {
+ name: "glanceable_hub_v2_resources"
+ namespace: "systemui"
+ description: "Read only flag for rolling out glanceable hub v2 resource values"
+ bug: "375689917"
+ is_fixed_read_only: true
+}
+
+flag {
name: "dream_overlay_updated_font"
namespace: "systemui"
description: "Flag to enable updated font settings for dream overlay"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index a17a1d4..0a0003e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -19,17 +19,20 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
@@ -39,8 +42,11 @@
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject
+import kotlin.math.min
+import kotlin.math.roundToInt
/** Renders the content of the glanceable hub. */
class CommunalContent
@@ -48,6 +54,7 @@
constructor(
private val viewModel: CommunalViewModel,
private val interactionHandler: SmartspaceInteractionHandler,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val dialogFactory: SystemUIDialogFactory,
private val lockSection: LockSection,
private val bottomAreaSection: BottomAreaSection,
@@ -77,11 +84,20 @@
sceneScope = this@Content,
)
}
- with(lockSection) {
- LockIcon(
- overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_lock),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier = Modifier.element(Communal.Elements.LockIcon),
)
+ } else {
+ with(lockSection) {
+ LockIcon(
+ overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ modifier = Modifier.element(Communal.Elements.LockIcon),
+ )
+ }
}
with(bottomAreaSection) {
IndicationArea(
@@ -98,14 +114,42 @@
val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconPlaceable =
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ val lockIconSizeInt = lockIconSize.roundToPx()
+ lockIconMeasurable.measure(
+ Constraints.fixed(width = lockIconSizeInt, height = lockIconSizeInt)
+ )
+ } else {
+ lockIconMeasurable.measure(noMinConstraints)
+ }
val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ val lockIconDistanceFromBottom =
+ min(
+ (constraints.maxHeight * lockIconPercentDistanceFromBottom)
+ .roundToInt(),
+ lockIconMinDistanceFromBottom.roundToPx(),
+ )
+ val x = constraints.maxWidth / 2 - lockIconPlaceable.width / 2
+ val y =
+ constraints.maxHeight -
+ lockIconDistanceFromBottom -
+ lockIconPlaceable.height
+ IntRect(
+ left = x,
+ top = y,
+ right = x + lockIconPlaceable.width,
+ bottom = y + lockIconPlaceable.height,
+ )
+ } else {
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+ }
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
@@ -129,12 +173,17 @@
val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
+
+ val screensaverButtonPaddingInt = screensaverButtonPadding.roundToPx()
screensaverButtonPlaceable?.place(
x =
constraints.maxWidth -
screensaverButtonSizeInt -
- Dimensions.ItemSpacing.roundToPx(),
- y = lockIconBounds.top,
+ screensaverButtonPaddingInt,
+ y =
+ constraints.maxHeight -
+ screensaverButtonSizeInt -
+ screensaverButtonPaddingInt,
)
}
}
@@ -142,6 +191,12 @@
}
companion object {
- val screensaverButtonSize: Dp = 64.dp
+ private val screensaverButtonSize: Dp = 64.dp
+ private val screensaverButtonPadding: Dp = 24.dp
+ // TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
+ // position are sorted.
+ private val lockIconSize: Dp = 54.dp
+ private val lockIconPercentDistanceFromBottom = 0.1f
+ private val lockIconMinDistanceFromBottom = 70.dp
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 5c7ca97..0344ab8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -38,7 +38,6 @@
import com.android.compose.modifiers.thenIf
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
@@ -90,14 +89,10 @@
) {
init {
- if (!MigrateClocksToBlueprint.isEnabled) {
- throw IllegalStateException("this requires MigrateClocksToBlueprint.isEnabled")
- }
// This scene container section moves the NSSL to the SharedNotificationContainer.
// This also requires that SharedNotificationContainer gets moved to the
// SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container,
- // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this
- // container by the NotificationStackScrollLayoutSection.
+ // NSSL is moved into this container by the NotificationStackScrollLayoutSection.
// Ensure stackScrollLayout is a child of sharedNotificationContainer.
if (stackScrollLayout.parent != sharedNotificationContainer) {
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 8a5c96d..f821e42 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
@@ -20,6 +20,9 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.PaddingValues
@@ -41,16 +44,21 @@
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.effect.rememberOffsetOverscrollEffect
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.res.R
@@ -61,7 +69,22 @@
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
- Box(modifier) {
+ // TODO(b/384653288) This should be removed when b/378470603 is done.
+ val idleEffect = rememberOffsetOverscrollEffect(Orientation.Vertical)
+ Box(
+ modifier
+ .overscroll(idleEffect)
+ .nestedScroll(
+ remember {
+ object : NestedScrollConnection {
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ return available
+ }
+ }
+ }
+ )
+ .scrollable(rememberScrollableState { 0f }, Orientation.Vertical, idleEffect)
+ ) {
Scrim(onClicked = onScrimClicked)
Box(modifier = Modifier.fillMaxSize().panelPadding(), contentAlignment = Alignment.TopEnd) {
@@ -180,7 +203,6 @@
object Dimensions {
val PanelCornerRadius = 46.dp
- val OverscrollLimit = 32.dp
}
object Shapes {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index fa5f72b..1480db9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -37,12 +37,14 @@
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.CustomAccessibilityAction
@@ -66,6 +68,10 @@
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
+import kotlin.math.round
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
@Composable
fun VolumeSlider(
@@ -196,9 +202,17 @@
)
}
}
-
- // Perform haptics due to UI composition
- hapticsViewModel?.onValueChange(value)
+ var lastDiscreteStep by remember { mutableFloatStateOf(round(value)) }
+ LaunchedEffect(value) {
+ snapshotFlow { value }
+ .map { round(it) }
+ .filter { it != lastDiscreteStep }
+ .distinctUntilChanged()
+ .collect { discreteStep ->
+ lastDiscreteStep = discreteStep
+ hapticsViewModel?.onValueChange(discreteStep)
+ }
+ }
PlatformSlider(
modifier =
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 801a2d6..b76656d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -71,7 +71,6 @@
}
var hasCustomPositionUpdatedAnimation: Boolean = false
- var migratedClocks: Boolean = false
private val time = Calendar.getInstance()
@@ -228,11 +227,7 @@
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
logger.d("onMeasure")
- if (
- migratedClocks &&
- !isSingleLineInternal &&
- MeasureSpec.getMode(heightMeasureSpec) == EXACTLY
- ) {
+ if (!isSingleLineInternal && MeasureSpec.getMode(heightMeasureSpec) == EXACTLY) {
// Call straight into TextView.setTextSize to avoid setting lastUnconstrainedTextSize
val size = min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
super.setTextSize(COMPLEX_UNIT_PX, size)
@@ -248,7 +243,7 @@
}
}
- if (migratedClocks && hasCustomPositionUpdatedAnimation) {
+ if (hasCustomPositionUpdatedAnimation) {
// Expand width to avoid clock being clipped during stepping animation
val targetWidth = measuredWidth + MeasureSpec.getSize(widthMeasureSpec) / 2
@@ -582,12 +577,10 @@
}
override fun onRtlPropertiesChanged(layoutDirection: Int) {
- if (migratedClocks) {
- if (layoutDirection == LAYOUT_DIRECTION_RTL) {
- textAlignment = TEXT_ALIGNMENT_TEXT_END
- } else {
- textAlignment = TEXT_ALIGNMENT_TEXT_START
- }
+ if (layoutDirection == LAYOUT_DIRECTION_RTL) {
+ textAlignment = TEXT_ALIGNMENT_TEXT_END
+ } else {
+ textAlignment = TEXT_ALIGNMENT_TEXT_START
}
super.onRtlPropertiesChanged(layoutDirection)
}
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 ad9eba8..74d595c 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
@@ -20,7 +20,6 @@
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
-import android.view.View
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.customization.R
@@ -55,7 +54,6 @@
private val layoutInflater: LayoutInflater,
private val resources: Resources,
private val settings: ClockSettings?,
- private val migratedClocks: Boolean = false,
messageBuffers: ClockMessageBuffers? = null,
) : ClockController {
override val smallClock: DefaultClockFaceController
@@ -67,7 +65,6 @@
private val burmeseLineSpacing =
resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
- protected var onSecondaryDisplay: Boolean = false
override val events: DefaultClockEvents
override val config: ClockConfig by lazy {
@@ -175,10 +172,7 @@
recomputePadding(targetRegion)
}
- override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {
- this@DefaultClockController.onSecondaryDisplay = onSecondaryDisplay
- recomputePadding(null)
- }
+ override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {}
}
open fun recomputePadding(targetRegion: Rect?) {}
@@ -197,32 +191,11 @@
override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
init {
- view.migratedClocks = migratedClocks
view.hasCustomPositionUpdatedAnimation = true
animations = LargeClockAnimations(view, 0f, 0f)
}
- override fun recomputePadding(targetRegion: Rect?) {
- if (migratedClocks) {
- return
- }
- // We center the view within the targetRegion instead of within the parent
- // view by computing the difference and adding that to the padding.
- val lp = view.getLayoutParams() as FrameLayout.LayoutParams
- lp.topMargin =
- if (onSecondaryDisplay) {
- // On the secondary display we don't want any additional top/bottom margin.
- 0
- } else {
- val parent = view.parent
- val yDiff =
- if (targetRegion != null && parent is View && parent.isLaidOut())
- targetRegion.centerY() - parent.height / 2f
- else 0f
- (-0.5f * view.bottom + yDiff).toInt()
- }
- view.setLayoutParams(lp)
- }
+ override fun recomputePadding(targetRegion: Rect?) {}
/** See documentation at [AnimatableClockView.offsetGlyphsForStepClockAnimation]. */
fun offsetGlyphsForStepClockAnimation(fromLeft: Int, direction: Int, fraction: Float) {
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 e898725..c73e1c3 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
@@ -47,7 +47,6 @@
val ctx: Context,
val layoutInflater: LayoutInflater,
val resources: Resources,
- private val migratedClocks: Boolean = false,
private val isClockReactiveVariantsEnabled: Boolean = false,
) : ClockProvider {
private var messageBuffers: ClockMessageBuffers? = null
@@ -83,14 +82,7 @@
FLEX_DESIGN,
)
} else {
- DefaultClockController(
- ctx,
- layoutInflater,
- resources,
- settings,
- migratedClocks,
- messageBuffers,
- )
+ DefaultClockController(ctx, layoutInflater, resources, settings, messageBuffers)
}
}
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 21d41ae..4a47f1b 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
@@ -140,8 +140,8 @@
}
/**
- * targetRegion passed to all customized clock applies counter translationY of
- * KeyguardStatusView and keyguard_large_clock_top_margin from default clock
+ * targetRegion passed to all customized clock applies counter translationY of Keyguard and
+ * keyguard_large_clock_top_margin from default clock
*/
override fun onTargetRegionChanged(targetRegion: Rect?) {
// When a clock needs to be aligned with screen, like weather clock
diff --git a/packages/SystemUI/docs/clock-plugins.md b/packages/SystemUI/docs/clock-plugins.md
index fee82df..813038e 100644
--- a/packages/SystemUI/docs/clock-plugins.md
+++ b/packages/SystemUI/docs/clock-plugins.md
@@ -43,12 +43,6 @@
SystemUI event dispatchers to the clock controllers. It maintains a set of event listeners, but
otherwise attempts to do as little work as possible. It does maintain some state where necessary.
-[KeyguardClockSwitchController](../src/com/android/keyguard/KeyguardClockSwitchController.java) is
-the primary controller for the [KeyguardClockSwitch](../src/com/android/keyguard/KeyguardClockSwitch.java),
-which serves as the view parent within SystemUI. Together they ensure the correct clock (either
-large or small) is shown, handle animation between clock sizes, and control some sizing/layout
-parameters for the clocks.
-
### Creating a custom clock
In order to create a custom clock, a partner must:
- Write an implementation of ClockProviderPlugin and the subinterfaces relevant to your use-case.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
index fa5af51..77e3869 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
@@ -127,6 +127,7 @@
@Test
@DisableFlags(
Flags.FLAG_COMMUNAL_HUB,
+ Flags.FLAG_GLANCEABLE_HUB_V2,
Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX,
Flags.FLAG_SCENE_CONTAINER,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
index 08f139c..9c9d5ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -51,7 +51,7 @@
title = title,
subtitle = subtitle,
description = description,
- contentView = contentView
+ contentView = contentView,
),
BiometricUserInfo(USER_ID),
BiometricOperationInfo(OPERATION_ID),
@@ -101,9 +101,7 @@
val fpPros = fingerprintSensorPropertiesInternal().first()
val request =
BiometricPromptRequest.Biometric(
- promptInfo(
- logoBitmap = logoBitmap,
- ),
+ promptInfo(logoBitmap = logoBitmap),
BiometricUserInfo(USER_ID),
BiometricOperationInfo(OPERATION_ID),
BiometricModalities(fingerprintProperties = fpPros),
@@ -162,7 +160,7 @@
BiometricUserInfo(USER_ID),
BiometricOperationInfo(OPERATION_ID),
stealth,
- )
+ ),
)
for (request in toCheck) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
index 370adee..03bf79b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalMetricsStartableTest.kt
@@ -19,6 +19,7 @@
import android.app.StatsManager
import android.app.StatsManager.StatsPullAtomCallback
import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.util.StatsEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -32,6 +33,7 @@
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.shared.system.SysUiStatsLog
@@ -75,10 +77,7 @@
// Set up an existing user, which is required for widgets to show
val userInfos = listOf(UserInfo(0, "main", UserInfo.FLAG_MAIN))
userRepository.setUserInfos(userInfos)
- userTracker.set(
- userInfos = userInfos,
- selectedUserIndex = 0,
- )
+ userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
underTest =
CommunalMetricsStartable(
@@ -90,14 +89,16 @@
)
}
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
@Test
- fun start_communalFlagDisabled_doNotSetPullAtomCallback() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ fun start_communalFlagDisabled_doNotSetPullAtomCallback() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- underTest.start()
+ underTest.start()
- verify(statsManager, never()).setPullAtomCallback(anyInt(), anyOrNull(), any(), any())
- }
+ verify(statsManager, never()).setPullAtomCallback(anyInt(), anyOrNull(), any(), any())
+ }
@Test
fun onPullAtom_atomTagDoesNotMatch_pullSkip() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index b66727e..038ea9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -26,8 +26,8 @@
import android.os.UserManager.USER_TYPE_PROFILE_MANAGED
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
@@ -35,10 +35,13 @@
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.model.DisabledReason
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
+import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.nullable
@@ -51,15 +54,21 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos =
testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
private val testScope = kosmos.testScope
private lateinit var underTest: CommunalSettingsRepository
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags!!)
+ }
+
@Before
fun setUp() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -164,17 +173,17 @@
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_INVALID_USER)
}
- @EnableFlags(FLAG_COMMUNAL_HUB)
+ @EnableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun classicFlagIsDisabled() =
- testScope.runTest {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ kosmos.runTest {
+ setCommunalV2Enabled(false)
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isFalse()
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
}
- @DisableFlags(FLAG_COMMUNAL_HUB)
+ @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun communalHubFlagIsDisabled() =
testScope.runTest {
@@ -295,6 +304,34 @@
}
}
+ @Test
+ fun screensaverDisabledByUser() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
+
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 0,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(enabledState).isFalse()
+ }
+
+ @Test
+ fun screensaverEnabledByUser() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
+
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 1,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(enabledState).isTrue()
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
@@ -310,5 +347,11 @@
val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
val WORK_PROFILE =
UserInfo(10, "work", /* iconPath= */ "", /* flags= */ 0, USER_TYPE_PROFILE_MANAGED)
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index b9e646f..7ae0577 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID
import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
@@ -232,7 +233,7 @@
@Test
fun isCommunalAvailable_communalDisabled_false() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
+ mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
index 8820685..b780808 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.viewmodel
import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
import android.service.dream.dreamManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -29,12 +30,16 @@
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.policy.batteryController
import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
@@ -56,10 +61,9 @@
}
@Test
- fun shouldShowDreamButtonOnHub_trueWhenCanDream() =
+ fun shouldShowDreamButtonOnHub_trueWhenPluggedIn() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
whenever(batteryController.isPluggedIn()).thenReturn(true)
val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
@@ -68,22 +72,9 @@
}
@Test
- fun shouldShowDreamButtonOnHub_falseWhenCannotDream() =
- with(kosmos) {
- runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(false)
- whenever(batteryController.isPluggedIn()).thenReturn(true)
-
- val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
- assertThat(shouldShowButton).isFalse()
- }
- }
-
- @Test
fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
whenever(batteryController.isPluggedIn()).thenReturn(false)
val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
@@ -92,13 +83,40 @@
}
@Test
- fun onShowDreamButtonTap_startsDream() =
+ fun onShowDreamButtonTap_dreamsEnabled_startsDream() =
with(kosmos) {
runTest {
+ val currentUser = fakeUserRepository.asMainUser()
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 1,
+ currentUser.id,
+ )
+ runCurrent()
+
underTest.onShowDreamButtonTap()
runCurrent()
verify(dreamManager).startDream()
}
}
+
+ @Test
+ fun onShowDreamButtonTap_dreamsDisabled_startsActivity() =
+ with(kosmos) {
+ runTest {
+ val currentUser = fakeUserRepository.asMainUser()
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 0,
+ currentUser.id,
+ )
+ runCurrent()
+
+ underTest.onShowDreamButtonTap()
+ runCurrent()
+
+ verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt())
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlActionCoordinatorImplTest.kt
similarity index 80%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlActionCoordinatorImplTest.kt
index 9285146..c8661cf5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlActionCoordinatorImplTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.controls.ui
+import android.view.HapticFeedbackConstants
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.view.HapticFeedbackConstants
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.wm.shell.taskview.TaskViewFactory
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,31 +46,20 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.Optional
@SmallTest
@RunWith(AndroidJUnit4::class)
class ControlActionCoordinatorImplTest : SysuiTestCase() {
- @Mock
- private lateinit var vibratorHelper: VibratorHelper
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var bgExecutor: DelayableExecutor
- @Mock
- private lateinit var uiExecutor: DelayableExecutor
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var broadcastSender: BroadcastSender
- @Mock
- private lateinit var taskViewFactory: Optional<TaskViewFactory>
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private lateinit var cvh: ControlViewHolder
- @Mock
- private lateinit var metricsLogger: ControlsMetricsLogger
- @Mock
- private lateinit var controlsSettingsDialogManager: ControlsSettingsDialogManager
+ @Mock private lateinit var vibratorHelper: VibratorHelper
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var bgExecutor: DelayableExecutor
+ @Mock private lateinit var uiExecutor: DelayableExecutor
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var broadcastSender: BroadcastSender
+ @Mock private lateinit var taskViewFactory: Optional<TaskViewFactory>
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var cvh: ControlViewHolder
+ @Mock private lateinit var metricsLogger: ControlsMetricsLogger
+ @Mock private lateinit var controlsSettingsDialogManager: ControlsSettingsDialogManager
companion object {
fun <T> any(): T = Mockito.any<T>()
@@ -89,18 +79,21 @@
controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
controlsSettingsRepository.setCanShowControlsInLockscreen(true)
- coordinator = spy(ControlActionCoordinatorImpl(
- mContext,
- bgExecutor,
- uiExecutor,
- activityStarter,
- broadcastSender,
- keyguardStateController,
- taskViewFactory,
- metricsLogger,
- vibratorHelper,
- controlsSettingsRepository,
- ))
+ coordinator =
+ spy(
+ ControlActionCoordinatorImpl(
+ mContext,
+ bgExecutor,
+ uiExecutor,
+ activityStarter,
+ broadcastSender,
+ keyguardStateController,
+ taskViewFactory,
+ metricsLogger,
+ vibratorHelper,
+ controlsSettingsRepository,
+ )
+ )
coordinator.activityContext = mContext
`when`(cvh.cws.ci.controlId).thenReturn(ID)
@@ -198,19 +191,15 @@
fun drag_isEdge_performsSegmentTickHaptics() {
coordinator.drag(cvh, true)
- verify(vibratorHelper).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.SEGMENT_TICK)
- )
+ verify(vibratorHelper)
+ .performHapticFeedback(any(), eq(HapticFeedbackConstants.SEGMENT_TICK))
}
@Test
fun drag_isNotEdge_performsFrequentTickHaptics() {
coordinator.drag(cvh, false)
- verify(vibratorHelper).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.SEGMENT_FREQUENT_TICK)
- )
+ verify(vibratorHelper)
+ .performHapticFeedback(any(), eq(HapticFeedbackConstants.SEGMENT_FREQUENT_TICK))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index b07097d..5921e94 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -92,10 +92,10 @@
import org.mockito.ArgumentCaptor
import org.mockito.Mockito
import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.isNull
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.firstValue
@@ -768,7 +768,7 @@
runCurrent()
verify(mDreamOverlayCallback).onRedirectWake(true)
client.onWakeRequested()
- verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), any(), isNull())
+ verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), any(), anyOrNull())
verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 004aeb0..3d9fb0a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -56,6 +56,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -84,6 +85,7 @@
FakeExecutor mMainExecutor;
FakeExecutor mBackgroundExecutor;
DeviceConfigProxyFake mDeviceConfigProxyFake;
+ FakeShadeDialogContextInteractor mFakeShadeDialogContextInteractor;
@Mock
IActivityManager mIActivityManager;
@@ -118,6 +120,7 @@
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
+ mFakeShadeDialogContextInteractor = new FakeShadeDialogContextInteractor(mContext);
mDeviceConfigProxyFake = new DeviceConfigProxyFake();
mSystemClock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(mSystemClock);
@@ -346,7 +349,6 @@
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"true", false);
FgsManagerController fmc = new FgsManagerControllerImpl(
- mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -359,7 +361,8 @@
mDialogTransitionAnimator,
mBroadcastDispatcher,
mDumpManager,
- mSystemUIDialogFactory
+ mSystemUIDialogFactory,
+ mFakeShadeDialogContextInteractor
);
fmc.init();
Assert.assertTrue(fmc.getIncludesUserVisibleJobs());
@@ -374,7 +377,6 @@
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
"false", false);
fmc = new FgsManagerControllerImpl(
- mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -387,7 +389,8 @@
mDialogTransitionAnimator,
mBroadcastDispatcher,
mDumpManager,
- mSystemUIDialogFactory
+ mSystemUIDialogFactory,
+ mFakeShadeDialogContextInteractor
);
fmc.init();
Assert.assertFalse(fmc.getIncludesUserVisibleJobs());
@@ -487,7 +490,6 @@
ArgumentCaptor.forClass(BroadcastReceiver.class);
FgsManagerController result = new FgsManagerControllerImpl(
- mContext,
mContext.getResources(),
mMainExecutor,
mBackgroundExecutor,
@@ -500,7 +502,8 @@
mDialogTransitionAnimator,
mBroadcastDispatcher,
mDumpManager,
- mSystemUIDialogFactory
+ mSystemUIDialogFactory,
+ mFakeShadeDialogContextInteractor
);
result.init();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/EditModeButtonViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index fbbdc46..0e823cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.DataSaverController
import com.android.systemui.util.mockito.whenever
@@ -96,6 +97,7 @@
dataSaverController,
mDialogTransitionAnimator,
systemUIDialogFactory,
+ FakeShadeDialogContextInteractor(mContext),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index b888617..5c6657b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -8,6 +8,7 @@
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -231,7 +232,8 @@
mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
- verify(mSpyContext).startActivity(any());
+ verify(mInternetDialogController).startActivityForDialog(any());
+ verify(mSpyContext, never()).startActivity(any());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index 039a1dc..518a0a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.qs.PseudoGridView
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -84,6 +85,7 @@
mDialogTransitionAnimator,
uiEventLogger,
dialogFactory,
+ FakeShadeDialogContextInteractor(mContext),
)
}
@@ -91,32 +93,32 @@
fun showDialog_callsDialogShow() {
val launchController = mock<DialogTransitionAnimator.Controller>()
`when`(launchExpandable.dialogTransitionController(any())).thenReturn(launchController)
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(mDialogTransitionAnimator).show(eq(dialog), eq(launchController), anyBoolean())
verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
}
@Test
fun dialog_showForAllUsers() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog).setShowForAllUsers(true)
}
@Test
fun dialog_cancelOnTouchOutside() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog).setCanceledOnTouchOutside(true)
}
@Test
fun adapterAndGridLinked() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(userDetailViewAdapter).linkToViewGroup(any<PseudoGridView>())
}
@Test
fun doneButtonLogsCorrectly() {
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog).setPositiveButton(anyInt(), capture(clickCaptor))
@@ -129,7 +131,7 @@
fun clickSettingsButton_noFalsing_opensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(false)
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog)
.setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
@@ -150,7 +152,7 @@
fun clickSettingsButton_Falsing_notOpensSettings() {
`when`(falsingManager.isFalseTap(anyInt())).thenReturn(true)
- controller.showDialog(context, launchExpandable)
+ controller.showDialog(launchExpandable)
verify(dialog)
.setNeutralButton(anyInt(), capture(clickCaptor), eq(false) /* dismissOnClick */)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index e56b965..3187cca6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -37,7 +37,6 @@
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
@@ -48,9 +47,9 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.currentValue
-import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.verifyCurrent
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -77,12 +76,9 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.verify
/**
* Integration test cases for the Scene Framework.
@@ -137,10 +133,10 @@
sceneContainerViewModel.activateIn(testScope)
assertWithMessage("Initial scene key mismatch!")
- .that(sceneContainerViewModel.currentScene.value)
+ .that(currentValue(sceneContainerViewModel.currentScene))
.isEqualTo(sceneContainerConfig.initialSceneKey)
assertWithMessage("Initial scene container visibility mismatch!")
- .that(sceneContainerViewModel.isVisible)
+ .that(currentValue { sceneContainerViewModel.isVisible })
.isTrue()
}
@@ -337,7 +333,6 @@
.that(bouncerActionButton)
.isNotNull()
kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
- runCurrent()
// TODO(b/369765704): Assert that an activity was started once we use ActivityStarter.
}
@@ -358,9 +353,8 @@
.that(bouncerActionButton)
.isNotNull()
kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
- runCurrent()
- verify(mockTelecomManager).showInCallScreen(any())
+ verifyCurrent(mockTelecomManager).showInCallScreen(any())
}
@Test
@@ -413,7 +407,7 @@
* the UI must gradually transition between scenes.
*/
private fun Kosmos.getCurrentSceneInUi(): SceneKey {
- return when (val state = transitionState.value) {
+ return when (val state = currentValue(transitionState)) {
is ObservableTransitionState.Idle -> state.currentScene
is ObservableTransitionState.Transition.ChangeScene -> state.fromScene
is ObservableTransitionState.Transition.ShowOrHideOverlay -> state.currentScene
@@ -436,7 +430,6 @@
// is not an observable that can trigger a new evaluation.
fakeDeviceEntryRepository.setLockscreenEnabled(enableLockscreen)
fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
- testScope.runCurrent()
}
/** Emulates a phone call in progress. */
@@ -447,7 +440,6 @@
setIsInCall(true)
setCallState(TelephonyManager.CALL_STATE_OFFHOOK)
}
- testScope.runCurrent()
}
/**
@@ -480,24 +472,21 @@
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
- testScope.runCurrent()
// Report progress of transition.
- while (progressFlow.value < 1f) {
+ while (currentValue(progressFlow) < 1f) {
progressFlow.value += 0.2f
- testScope.runCurrent()
}
// End the transition and report the change.
transitionState.value = ObservableTransitionState.Idle(to)
fakeSceneDataSource.unpause(force = true)
- testScope.runCurrent()
assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
- .that(sceneContainerViewModel.isVisible)
+ .that(currentValue { sceneContainerViewModel.isVisible })
.isEqualTo(expectedVisible)
- assertThat(sceneContainerViewModel.currentScene.value).isEqualTo(to)
+ assertThat(currentValue(sceneContainerViewModel.currentScene)).isEqualTo(to)
bouncerSceneJob =
if (to == Scenes.Bouncer) {
@@ -510,7 +499,6 @@
bouncerSceneJob?.cancel()
null
}
- testScope.runCurrent()
}
/**
@@ -556,13 +544,12 @@
)
powerInteractor.setAwakeForTest()
- testScope.runCurrent()
}
/** Unlocks the device by entering the correct PIN. Ends up in the Gone scene. */
private fun Kosmos.unlockDevice() {
assertWithMessage("Cannot unlock a device that's already unlocked!")
- .that(deviceEntryInteractor.isUnlocked.value)
+ .that(currentValue(deviceEntryInteractor.isUnlocked))
.isFalse()
emulateUserDrivenTransition(Scenes.Bouncer)
@@ -595,7 +582,6 @@
pinBouncerViewModel.onPinButtonClicked(digit)
}
pinBouncerViewModel.onAuthenticateButtonClicked()
- testScope.runCurrent()
}
/**
@@ -625,26 +611,23 @@
}
pinBouncerViewModel.onAuthenticateButtonClicked()
fakeMobileConnectionsRepository.isAnySimSecure.value = false
- testScope.runCurrent()
setAuthMethod(authMethodAfterSimUnlock, enableLockscreen)
- testScope.runCurrent()
}
/** Changes device wakefulness state from asleep to awake, going through intermediary states. */
private fun Kosmos.wakeUpDevice() {
- val wakefulnessModel = powerInteractor.detailedWakefulness.value
+ val wakefulnessModel = currentValue(powerInteractor.detailedWakefulness)
assertWithMessage("Cannot wake up device as it's already awake!")
.that(wakefulnessModel.isAwake())
.isFalse()
powerInteractor.setAwakeForTest()
- testScope.runCurrent()
}
/** Changes device wakefulness state from awake to asleep, going through intermediary states. */
private suspend fun Kosmos.putDeviceToSleep(waitForLock: Boolean = true) {
- val wakefulnessModel = powerInteractor.detailedWakefulness.value
+ val wakefulnessModel = currentValue(powerInteractor.detailedWakefulness)
assertWithMessage("Cannot put device to sleep as it's already asleep!")
.that(wakefulnessModel.isAwake())
.isTrue()
@@ -659,22 +642,18 @@
)
.toLong()
)
- } else {
- testScope.runCurrent()
}
}
/** Emulates the dismissal of the IME (soft keyboard). */
private fun Kosmos.dismissIme() {
- (bouncerSceneContentViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
- it.onImeDismissed()
- testScope.runCurrent()
- }
+ (currentValue(bouncerSceneContentViewModel.authMethodViewModel)
+ as? PasswordBouncerViewModel)
+ ?.let { it.onImeDismissed() }
}
private fun Kosmos.introduceLockedSim() {
setAuthMethod(AuthenticationMethodModel.Sim)
fakeMobileConnectionsRepository.isAnySimSecure.value = true
- testScope.runCurrent()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 929537dc..0361ffe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -212,18 +212,6 @@
}
@Test
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testDragDownHelperCalledWhenDraggingDown() =
- testScope.runTest {
- whenever(dragDownHelper.isDraggingDown).thenReturn(true)
- val now = SystemClock.elapsedRealtime()
- val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
- underTest.onTouchEvent(ev)
- verify(dragDownHelper).onTouchEvent(ev)
- ev.recycle()
- }
-
- @Test
fun testNoInterceptTouch() =
testScope.runTest {
captureInteractionEventHandler()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index aa8b4f1..4423426 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -117,7 +117,6 @@
verify(mockLargeClockView).onTimeZoneChanged(notNull())
verify(mockSmallClockView).refreshTime()
verify(mockLargeClockView).refreshTime()
- verify(mockLargeClockView).setLayoutParams(any())
}
@Test
@@ -163,7 +162,6 @@
clock.largeClock.events.onFontSettingChanged(200f)
verify(mockLargeClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), eq(200f))
- verify(mockLargeClockView).setLayoutParams(any())
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
similarity index 74%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index abd0a28..3359db0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -43,7 +43,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-class PromotedNotificationContentExtractorTest : SysuiTestCase() {
+class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val provider =
@@ -54,7 +54,7 @@
@Test
@DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun shouldNotExtract_bothFlagsDisabled() {
- val notif = createEntry().also { provider.promotedEntries.add(it) }
+ val notif = createEntry()
val content = extractContent(notif)
assertThat(content).isNull()
}
@@ -63,7 +63,7 @@
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
fun shouldExtract_promotedNotificationUiFlagEnabled() {
- val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val entry = createEntry()
val content = extractContent(entry)
assertThat(content).isNotNull()
}
@@ -72,7 +72,7 @@
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
fun shouldExtract_statusBarNotifChipsFlagEnabled() {
- val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val entry = createEntry()
val content = extractContent(entry)
assertThat(content).isNotNull()
}
@@ -80,7 +80,7 @@
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun shouldExtract_bothFlagsEnabled() {
- val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val entry = createEntry()
val content = extractContent(entry)
assertThat(content).isNotNull()
}
@@ -88,22 +88,19 @@
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun shouldNotExtract_providerDidNotPromote() {
- val entry = createEntry().also { provider.promotedEntries.remove(it) }
+ val entry = createEntry(promoted = false)
val content = extractContent(entry)
assertThat(content).isNull()
}
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
- fun extractContent_commonFields() {
- val entry =
- createEntry {
- setSubText(TEST_SUB_TEXT)
- setContentTitle(TEST_CONTENT_TITLE)
- setContentText(TEST_CONTENT_TEXT)
- setShortCriticalText(TEST_SHORT_CRITICAL_TEXT)
- }
- .also { provider.promotedEntries.add(it) }
+ fun extractsContent_commonFields() {
+ val entry = createEntry {
+ setSubText(TEST_SUB_TEXT)
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ }
val content = extractContent(entry)
@@ -117,9 +114,7 @@
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
@DisableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() {
- val entry =
- createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
val content = extractContent(entry)
@@ -134,9 +129,7 @@
android.app.Flags.FLAG_API_RICH_ONGOING,
)
fun extractContent_apiFlagOn_shortCriticalTextExtracted() {
- val entry =
- createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
val content = extractContent(entry)
@@ -151,7 +144,7 @@
android.app.Flags.FLAG_API_RICH_ONGOING,
)
fun extractContent_noShortCriticalTextSet_textIsNull() {
- val entry = createEntry {}.also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setShortCriticalText(null) }
val content = extractContent(entry)
@@ -161,9 +154,8 @@
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
- fun extractContent_fromBigPictureStyle() {
- val entry =
- createEntry { setStyle(BigPictureStyle()) }.also { provider.promotedEntries.add(it) }
+ fun extractsContent_fromBigPictureStyle() {
+ val entry = createEntry { setStyle(BigPictureStyle()) }
val content = extractContent(entry)
@@ -174,8 +166,7 @@
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromBigTextStyle() {
- val entry =
- createEntry { setStyle(BigTextStyle()) }.also { provider.promotedEntries.add(it) }
+ val entry = createEntry { setStyle(BigTextStyle()) }
val content = extractContent(entry)
@@ -187,11 +178,13 @@
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromCallStyle() {
val hangUpIntent =
- PendingIntent.getBroadcast(context, 0, Intent("hangup"), PendingIntent.FLAG_IMMUTABLE)
-
- val entry =
- createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) }
- .also { provider.promotedEntries.add(it) }
+ PendingIntent.getBroadcast(
+ context,
+ 0,
+ Intent("hangup_action"),
+ PendingIntent.FLAG_IMMUTABLE,
+ )
+ val entry = createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) }
val content = extractContent(entry)
@@ -202,11 +195,9 @@
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromProgressStyle() {
- val entry =
- createEntry {
- setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
- }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry {
+ setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
+ }
val content = extractContent(entry)
@@ -220,13 +211,9 @@
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromIneligibleStyle() {
- val entry =
- createEntry {
- setStyle(
- MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON)
- )
- }
- .also { provider.promotedEntries.add(it) }
+ val entry = createEntry {
+ setStyle(MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON))
+ }
val content = extractContent(entry)
@@ -239,9 +226,14 @@
return underTest.extractContent(entry, recoveredBuilder)
}
- private fun createEntry(builderBlock: Notification.Builder.() -> Unit = {}): NotificationEntry {
- val notif = Notification.Builder(context, "a").also(builderBlock).build()
- return NotificationEntryBuilder().setNotification(notif).build()
+ private fun createEntry(
+ promoted: Boolean = true,
+ builderBlock: Notification.Builder.() -> Unit = {},
+ ): NotificationEntry {
+ val notif = Notification.Builder(context, "channel").also(builderBlock).build()
+ return NotificationEntryBuilder().setNotification(notif).build().also {
+ provider.shouldPromoteForEntry[it] = promoted
+ }
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 6eb2764..cdc8bc1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -30,7 +30,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -61,7 +60,7 @@
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
@@ -105,7 +104,8 @@
@Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
@Mock private HeadsUpStyleProvider mHeadsUpStyleProvider;
@Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
- @Mock private PromotedNotificationContentExtractor mPromotedNotificationContentExtractor;
+ private final FakePromotedNotificationContentExtractor mPromotedNotificationContentExtractor =
+ new FakePromotedNotificationContentExtractor();
private final SmartReplyStateInflater mSmartReplyStateInflater =
new SmartReplyStateInflater() {
@@ -395,12 +395,11 @@
public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, never()).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyZeroExtractCalls();
}
@Test
@@ -410,12 +409,11 @@
throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
}
@@ -425,12 +423,11 @@
public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
}
@@ -439,23 +436,22 @@
public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception {
final PromotedNotificationContentModel content =
new PromotedNotificationContentModel.Builder("key").build();
- when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
}
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
public void testExtractsPromotedContent_null() throws Exception {
- when(mPromotedNotificationContentExtractor.extractContent(any(), any())).thenReturn(null);
+ mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), null);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
- verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ mPromotedNotificationContentExtractor.verifyOneExtractCall();
assertNull(mRow.getEntry().getPromotedNotificationContentModel());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 1851799..9fb72fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
@@ -67,7 +67,6 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -110,7 +109,7 @@
return inflatedSmartReplyState
}
}
- private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor = mock()
+ private val promotedNotificationContentExtractor = FakePromotedNotificationContentExtractor()
@Before
fun setUp() {
@@ -234,7 +233,7 @@
packageContext = mContext,
remoteViews = NewRemoteViews(),
contentModel = NotificationContentModel(headsUpStatusBarModel),
- extractedPromotedNotificationContentModel = null,
+ promotedContent = null,
)
val countDownLatch = CountDownLatch(1)
NotificationRowContentBinderImpl.applyRemoteView(
@@ -475,12 +474,11 @@
@DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_notWhenBothFlagsDisabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, never()).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyZeroExtractCalls()
}
@Test
@@ -488,12 +486,11 @@
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyOneExtractCall()
Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
}
@@ -502,12 +499,11 @@
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyOneExtractCall()
Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
}
@@ -515,15 +511,25 @@
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenBothFlagsEnabled() {
val content = PromotedNotificationContentModel.Builder("key").build()
- whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
- .thenReturn(content)
+ promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
- verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ promotedNotificationContentExtractor.verifyOneExtractCall()
Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
}
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun testExtractsPromotedContent_null() {
+ promotedNotificationContentExtractor.resetForEntry(row.entry, null)
+
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+
+ promotedNotificationContentExtractor.verifyOneExtractCall()
+ Assert.assertNull(row.entry.promotedNotificationContentModel)
+ }
+
private class ExceptionHolder {
var exception: Exception? = null
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index b323ef8..b8d1875 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -81,7 +81,7 @@
import com.android.systemui.statusbar.notification.icon.IconBuilder;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -201,7 +201,7 @@
new MockSmartReplyInflater(),
mock(NotifLayoutInflaterFactory.Provider.class),
mock(HeadsUpStyleProvider.class),
- mock(PromotedNotificationContentExtractor.class),
+ new FakePromotedNotificationContentExtractor(),
mock(NotificationRowContentBinderLogger.class))
: new NotificationContentInflater(
mock(NotifRemoteViewCache.class),
@@ -212,7 +212,7 @@
new MockSmartReplyInflater(),
mock(NotifLayoutInflaterFactory.Provider.class),
mock(HeadsUpStyleProvider.class),
- mock(PromotedNotificationContentExtractor.class),
+ new FakePromotedNotificationContentExtractor(),
mock(NotificationRowContentBinderLogger.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index de40abb..c6cffa9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -952,11 +952,10 @@
@Test
@EnableSceneContainer
public void onTouchEvent_stopExpandingNotification_sceneContainerEnabled() {
- boolean touchHandled = stopExpandingNotification();
+ stopExpandingNotification();
- verify(mNotificationStackScrollLayout).startOverscrollAfterExpanding();
+ verify(mExpandHelper).finishExpanding();
verify(mNotificationStackScrollLayout, never()).dispatchDownEventToScroller(any());
- assertTrue(touchHandled);
}
@Test
@@ -964,11 +963,11 @@
public void onTouchEvent_stopExpandingNotification_sceneContainerDisabled() {
stopExpandingNotification();
- verify(mNotificationStackScrollLayout, never()).startOverscrollAfterExpanding();
+ verify(mExpandHelper, never()).finishExpanding();
verify(mNotificationStackScrollLayout).dispatchDownEventToScroller(any());
}
- private boolean stopExpandingNotification() {
+ private void stopExpandingNotification() {
when(mNotificationStackScrollLayout.getExpandHelper()).thenReturn(mExpandHelper);
when(mNotificationStackScrollLayout.getIsExpanded()).thenReturn(true);
when(mNotificationStackScrollLayout.getExpandedInThisMotion()).thenReturn(true);
@@ -983,13 +982,13 @@
NotificationStackScrollLayoutController.TouchHandler touchHandler =
mController.getTouchHandler();
- return touchHandler.onTouchEvent(MotionEvent.obtain(
- /* downTime= */ 0,
- /* eventTime= */ 0,
- MotionEvent.ACTION_DOWN,
- 0,
- 0,
- /* metaState= */ 0
+ touchHandler.onTouchEvent(MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ MotionEvent.ACTION_DOWN,
+ 0,
+ 0,
+ /* metaState= */ 0
));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
index 06b3b57..b2378d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.runOnMainThreadAndWaitForIdleSync
+import com.android.systemui.shade.data.repository.shadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.systemUIDialogFactory
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
@@ -78,6 +79,7 @@
{ kosmos.modesDialogViewModel },
mockDialogEventLogger,
kosmos.mainCoroutineContext,
+ kosmos.shadeDialogContextInteractor,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 7b52dd8..5cd0846 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -501,6 +501,7 @@
}
@Test
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
public void onWallpaperColorsChanged_changeLockWallpaper() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
diff --git a/packages/SystemUI/res/color/slider_active_track_color.xml b/packages/SystemUI/res/color/slider_active_track_color.xml
deleted file mode 100644
index 8ba5e49..0000000
--- a/packages/SystemUI/res/color/slider_active_track_color.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="@androidprv:color/materialColorPrimary" android:state_enabled="true" />
- <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/slider_inactive_track_color.xml b/packages/SystemUI/res/color/slider_inactive_track_color.xml
deleted file mode 100644
index 7980f80..0000000
--- a/packages/SystemUI/res/color/slider_inactive_track_color.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" android:state_enabled="true" />
- <item android:color="@androidprv:color/materialColorPrimary" />
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/slider_thumb_color.xml b/packages/SystemUI/res/color/slider_thumb_color.xml
deleted file mode 100644
index 8a98902..0000000
--- a/packages/SystemUI/res/color/slider_thumb_color.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" android:state_enabled="false" />
- <item android:color="@androidprv:color/materialColorPrimary" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/audio_bars_idle.xml b/packages/SystemUI/res/drawable/audio_bars_idle.xml
new file mode 100644
index 0000000..92a2475
--- /dev/null
+++ b/packages/SystemUI/res/drawable/audio_bars_idle.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="168dp"
+ android:height="168dp"
+ android:viewportWidth="168"
+ android:viewportHeight="168">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_2_G"
+ android:translateX="121.161"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="102.911"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="139.661"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index a3bad8f..5ccedea 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -56,8 +56,8 @@
android:layout_marginTop="@dimen/volume_dialog_components_spacing"
android:background="@drawable/ripple_drawable_20dp"
android:contentDescription="@string/accessibility_volume_settings"
+ android:scaleType="centerInside"
android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
android:tint="@androidprv:color/materialColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index 9ac456c..0acf410 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -14,7 +14,6 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
@@ -24,11 +23,6 @@
android:layout_width="@dimen/volume_dialog_slider_width"
android:layout_height="@dimen/volume_dialog_slider_height"
android:layout_gravity="center"
- android:theme="@style/Theme.Material3.Light"
android:orientation="vertical"
- app:thumbHeight="52dp"
- app:trackCornerSize="12dp"
- app:trackHeight="40dp"
- app:trackStopIndicatorSize="6dp"
- app:trackInsideCornerSize="2dp" />
+ android:theme="@style/Theme.Material3.DayNight" />
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_in.json b/packages/SystemUI/res/raw/audio_bars_in.json
new file mode 100644
index 0000000..c90a59c
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_in.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":18,"w":168,"h":168,"nm":"audio_bars_in","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_out.json b/packages/SystemUI/res/raw/audio_bars_out.json
new file mode 100644
index 0000000..5eab65e0
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_out.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":31,"w":168,"h":168,"nm":"audio_bars_out","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_playing.json b/packages/SystemUI/res/raw/audio_bars_playing.json
new file mode 100644
index 0000000..6ee8e19
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_playing.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":121,"w":168,"h":168,"nm":"audio_bars_playing","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":70,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":102,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":32,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":65,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":97,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":29,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":91,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":54,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":86,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":19,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":48,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":81,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0}],"markers":[{"tm":60,"cm":"1","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index c1eff5f..a77f5e4 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -108,6 +108,9 @@
<color name="people_tile_background">@color/material_dynamic_secondary20</color>
+ <!-- Dark Theme colors for notification shade/scrim -->
+ <color name="shade_panel">@android:color/system_accent1_900</color>
+
<!-- Keyboard shortcut helper dialog -->
<color name="ksh_key_item_color">@*android:color/system_on_surface_variant_dark</color>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index b438315..ab0f788 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -19,7 +19,7 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">4</integer>
@@ -51,7 +51,9 @@
ignored. -->
<string-array name="config_keyguardQuickAffordanceDefaults" translatable="false">
<item>bottom_start:home</item>
- <item>bottom_end:create_note</item>
+ <!-- TODO(b/384119565): revisit decision on defaults -->
+ <item android:featureFlag="!com.android.systemui.glanceable_hub_v2_resources">bottom_end:create_note</item>
+ <item android:featureFlag="com.android.systemui.glanceable_hub_v2_resources">bottom_end:glanceable_hub</item>
</string-array>
<!-- Whether volume panel should use the large screen layout or not -->
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 28df2e2..d2b7d0b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -31,6 +31,9 @@
<!-- The dark background color behind the shade -->
<color name="shade_scrim_background_dark">@androidprv:color/system_under_surface_light</color>
+ <!-- Colors for notification shade/scrim -->
+ <color name="shade_panel">@android:color/system_accent1_800</color>
+
<!-- The color of the background in the separated list of the Global Actions menu -->
<color name="global_actions_separated_background">#F5F5F5</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 56aaf4c..d3ee63ba 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2504,14 +2504,14 @@
<!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_remove_tile_action">remove tile</string>
- <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to end" in screen readers [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_add_action">add tile to end</string>
+ <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to the last position" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_add_action">add tile to the last position</string>
<!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_tile_start_move">Move tile</string>
- <!-- Accessibility action for context menu to add QS tile [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_start_add">Add tile</string>
+ <!-- Accessibility action for context menu to add QS tile to a position [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_start_add">Add tile to desired position</string>
<!-- Accessibility description when QS tile is to be moved, indicating the destination position [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_tile_move_to_position">Move to <xliff:g id="position" example="5">%1$d</xliff:g></string>
@@ -2564,7 +2564,7 @@
<string name="accessibility_quick_settings_open_settings">Open <xliff:g name="page" example="Bluetooth">%s</xliff:g> settings.</string>
<!-- accessibility label for button to edit quick settings [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_edit">Edit order of settings.</string>
+ <string name="accessibility_quick_settings_edit">Edit order of Quick Settings.</string>
<!-- accessibility label for button to open power menu [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_power_menu">Power menu</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3156a50..f6c1ecea 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -559,15 +559,18 @@
<style name="SystemUI.Material3.Slider.Volume">
<item name="trackHeight">40dp</item>
<item name="thumbHeight">52dp</item>
+ <item name="trackCornerSize">12dp</item>
+ <item name="trackInsideCornerSize">2dp</item>
+ <item name="trackStopIndicatorSize">6dp</item>
</style>
<style name="SystemUI.Material3.Slider" parent="@style/Widget.Material3.Slider">
<item name="labelStyle">@style/Widget.Material3.Slider.Label</item>
- <item name="thumbColor">@color/slider_thumb_color</item>
- <item name="tickColorActive">@color/slider_inactive_track_color</item>
- <item name="tickColorInactive">@color/slider_active_track_color</item>
- <item name="trackColorActive">@color/slider_active_track_color</item>
- <item name="trackColorInactive">@color/slider_inactive_track_color</item>
+ <item name="thumbColor">@androidprv:color/materialColorPrimary</item>
+ <item name="tickColorActive">@androidprv:color/materialColorSurfaceContainerHighest</item>
+ <item name="tickColorInactive">@androidprv:color/materialColorPrimary</item>
+ <item name="trackColorActive">@androidprv:color/materialColorPrimary</item>
+ <item name="trackColorInactive">@androidprv:color/materialColorSurfaceContainerHighest</item>
</style>
<style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 5af80cb..71b622a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -43,7 +43,6 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags.REGION_SAMPLING
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -85,8 +84,8 @@
import kotlinx.coroutines.flow.merge
/**
- * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
- * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
+ * Controller for a Clock provided by the registry and used on the keyguard. Functionality is forked
+ * from [AnimatableClockController].
*/
open class ClockEventController
@Inject
@@ -348,14 +347,6 @@
object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible
- if (!MigrateClocksToBlueprint.isEnabled) {
- if (!isKeyguardVisible) {
- clock?.run {
- smallClock.animations.doze(if (isDozing) 1f else 0f)
- largeClock.animations.doze(if (isDozing) 1f else 0f)
- }
- }
- }
if (visible) {
refreshTime()
@@ -388,10 +379,6 @@
}
private fun refreshTime() {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
clock?.smallClock?.events?.onTimeTick()
clock?.largeClock?.events?.onTimeTick()
}
@@ -483,14 +470,10 @@
if (ModesUi.isEnabled) {
listenForDnd(this)
}
- if (MigrateClocksToBlueprint.isEnabled) {
- listenForDozeAmountTransition(this)
- listenForAnyStateToAodTransition(this)
- listenForAnyStateToLockscreenTransition(this)
- listenForAnyStateToDozingTransition(this)
- } else {
- listenForDozeAmount(this)
- }
+ listenForDozeAmountTransition(this)
+ listenForAnyStateToAodTransition(this)
+ listenForAnyStateToLockscreenTransition(this)
+ listenForAnyStateToDozingTransition(this)
}
}
smallTimeListener?.update(shouldTimeListenerRun)
@@ -596,11 +579,6 @@
}
@VisibleForTesting
- internal fun listenForDozeAmount(scope: CoroutineScope): Job {
- return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
- }
-
- @VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
merge(
@@ -695,8 +673,7 @@
isRunning = true
when (clockFace.config.tickRate) {
ClockTickRate.PER_MINUTE -> {
- // Handled by KeyguardClockSwitchController and
- // by KeyguardUpdateMonitorCallback#onTimeChanged.
+ // Handled by KeyguardUpdateMonitorCallback#onTimeChanged.
}
ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
ClockTickRate.PER_FRAME -> {
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index df77a58..3f332f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -23,14 +23,11 @@
import android.os.Bundle
import android.view.Display
import android.view.Gravity
-import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams
-import com.android.keyguard.dagger.KeyguardStatusViewComponent
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.res.R
@@ -45,7 +42,6 @@
constructor(
@Assisted display: Display,
context: Context,
- private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
private val clockRegistry: ClockRegistry,
private val clockEventController: ClockEventController,
) :
@@ -53,12 +49,11 @@
context,
display,
R.style.Theme_SystemUI_KeyguardPresentation,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
) {
private lateinit var rootView: FrameLayout
private var clock: View? = null
- private lateinit var keyguardStatusViewController: KeyguardStatusViewController
private lateinit var faceController: ClockFaceController
private lateinit var clockFrame: FrameLayout
@@ -82,7 +77,7 @@
oldLeft: Int,
oldTop: Int,
oldRight: Int,
- oldBottom: Int
+ oldBottom: Int,
) {
clock?.let {
faceController.events.onTargetRegionChanged(
@@ -95,11 +90,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- if (MigrateClocksToBlueprint.isEnabled) {
- onCreateV2()
- } else {
- onCreate()
- }
+ onCreateV2()
}
fun onCreateV2() {
@@ -112,39 +103,15 @@
setClock(clockRegistry.createCurrentClock())
}
- fun onCreate() {
- setContentView(
- LayoutInflater.from(context)
- .inflate(R.layout.keyguard_clock_presentation, /* root= */ null)
- )
-
- setFullscreen()
-
- clock = requireViewById(R.id.clock)
- keyguardStatusViewController =
- keyguardStatusViewComponentFactory
- .build(clock as KeyguardStatusView, display)
- .keyguardStatusViewController
- .apply {
- setDisplayedOnSecondaryDisplay()
- init()
- }
- }
-
override fun onAttachedToWindow() {
- if (MigrateClocksToBlueprint.isEnabled) {
- clockRegistry.registerClockChangeListener(clockChangedListener)
- clockEventController.registerListeners(clock!!)
-
- faceController.animations.enter()
- }
+ clockRegistry.registerClockChangeListener(clockChangedListener)
+ clockEventController.registerListeners(clock!!)
+ faceController.animations.enter()
}
override fun onDetachedFromWindow() {
- if (MigrateClocksToBlueprint.isEnabled) {
- clockEventController.unregisterListeners()
- clockRegistry.unregisterClockChangeListener(clockChangedListener)
- }
+ clockEventController.unregisterListeners()
+ clockRegistry.unregisterClockChangeListener(clockChangedListener)
super.onDetachedFromWindow()
}
@@ -166,7 +133,7 @@
context.resources.getDimensionPixelSize(R.dimen.keyguard_presentation_width),
WRAP_CONTENT,
Gravity.CENTER,
- )
+ ),
)
clockEventController.clock = clockController
@@ -190,8 +157,6 @@
@AssistedFactory
interface Factory {
/** Creates a new [Presentation] for the given [display]. */
- fun create(
- display: Display,
- ): ConnectedDisplayKeyguardPresentation
+ fun create(display: Display): ConnectedDisplayKeyguardPresentation
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 0305b5e..e76f38c 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -26,7 +26,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.res.R;
@@ -70,7 +69,7 @@
context,
layoutInflater,
resources,
- MigrateClocksToBlueprint.isEnabled(),
+
com.android.systemui.Flags.clockReactiveVariants()
),
context.getString(R.string.lockscreen_clock_id_fallback),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 4c2dc41..d8c628f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -155,6 +155,7 @@
override fun onAnimationEnd(animation: Animator) {
drawDwell = false
resetDwellAlpha()
+ invalidate()
}
})
start()
@@ -191,6 +192,7 @@
override fun onAnimationEnd(animation: Animator) {
drawDwell = false
resetDwellAlpha()
+ invalidate()
}
})
start()
@@ -248,6 +250,7 @@
override fun onAnimationEnd(animation: Animator) {
drawDwell = false
+ invalidate()
}
})
start()
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index 9cfb5be..b294dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -41,6 +41,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.time.SystemClock
import dagger.assisted.Assisted
@@ -68,6 +69,7 @@
private val uiEventLogger: UiEventLogger,
private val logger: BluetoothTileDialogLogger,
private val systemuiDialogFactory: SystemUIDialog.Factory,
+ private val shadeDialogContextInteractor: ShadeDialogContextInteractor,
) : SystemUIDialog.Delegate {
private val mutableBluetoothStateToggle: MutableStateFlow<Boolean?> = MutableStateFlow(null)
@@ -105,7 +107,7 @@
}
override fun createDialog(): SystemUIDialog {
- return systemuiDialogFactory.create(this)
+ return systemuiDialogFactory.create(this, shadeDialogContextInteractor.context)
}
override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
@@ -405,10 +407,11 @@
}
// updating icon colors
- val tintColor = context.getColor(
- if (item.isActive) InternalR.color.materialColorOnPrimaryContainer
- else InternalR.color.materialColorOnSurface
- )
+ val tintColor =
+ context.getColor(
+ if (item.isActive) InternalR.color.materialColorOnPrimaryContainer
+ else InternalR.color.materialColorOnSurface
+ )
// update icons
iconView.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 26abb48..73c0179 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -55,6 +55,8 @@
/** A [CommunalEnabledState] for the specified user. */
fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+ fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean>
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
@@ -138,6 +140,20 @@
.flowOn(bgDispatcher)
}
+ override fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.SCREENSAVER_ENABLED))
+ // Force an update
+ .onStart { emit(Unit) }
+ .map {
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ SCREENSAVER_ENABLED_SETTING_DEFAULT,
+ user.id,
+ ) == 1
+ }
+ .flowOn(bgDispatcher)
+
override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
broadcastDispatcher
.broadcastFlow(
@@ -182,6 +198,7 @@
companion object {
const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
private const val ENABLED_SETTING_DEFAULT = 1
+ private const val SCREENSAVER_ENABLED_SETTING_DEFAULT = 0
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 862b05b..c1f21e40 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -69,6 +69,12 @@
// Start this eagerly since the value is accessed synchronously in many places.
.stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+ /** Whether or not screensaver (dreams) is enabled for the currently selected user. */
+ val isScreensaverEnabled: Flow<Boolean> =
+ userInteractor.selectedUserInfo.flatMapLatest { user ->
+ repository.getScreensaverEnabledState(user)
+ }
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
index 7d5b196..c6f96e1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -18,10 +18,15 @@
import android.annotation.SuppressLint
import android.app.DreamManager
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.kotlin.isDevicePluggedIn
+import com.android.systemui.util.kotlin.sample
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlin.coroutines.CoroutineContext
@@ -31,7 +36,6 @@
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -41,6 +45,8 @@
constructor(
@Background private val backgroundContext: CoroutineContext,
batteryController: BatteryController,
+ private val settingsInteractor: CommunalSettingsInteractor,
+ private val activityStarter: ActivityStarter,
private val dreamManager: DreamManager,
) : ExclusiveActivatable() {
@@ -49,11 +55,7 @@
/** Whether we should show a button on hub to switch to dream. */
@SuppressLint("MissingPermission")
val shouldShowDreamButtonOnHub =
- batteryController
- .isDevicePluggedIn()
- .distinctUntilChanged()
- .map { isPluggedIn -> isPluggedIn && dreamManager.canStartDreaming(true) }
- .flowOn(backgroundContext)
+ batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(backgroundContext)
/** Handle a tap on the "show dream" button. */
fun onShowDreamButtonTap() {
@@ -63,9 +65,21 @@
@SuppressLint("MissingPermission")
override suspend fun onActivated(): Nothing = coroutineScope {
launch {
- _requests.receiveAsFlow().collectLatest {
- withContext(backgroundContext) { dreamManager.startDream() }
- }
+ _requests
+ .receiveAsFlow()
+ .sample(settingsInteractor.isScreensaverEnabled)
+ .collectLatest { enabled ->
+ withContext(backgroundContext) {
+ if (enabled) {
+ dreamManager.startDream()
+ } else {
+ activityStarter.postStartActivityDismissingKeyguard(
+ Intent(Settings.ACTION_DREAM_SETTINGS),
+ 0,
+ )
+ }
+ }
+ }
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index 160380b..57fe15d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -24,7 +24,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -50,9 +49,6 @@
}
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
if (emptyView.parent != null) {
// As emptyView is lazy, it might be already attached.
(emptyView.parent as? ViewGroup)?.removeView(emptyView)
@@ -68,17 +64,10 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
clockViewModel.burnInLayer = burnInLayer
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
constraintSet.apply {
// The empty view should not occupy any space
constrainHeight(R.id.burn_in_layer_empty_view, 1)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 70bf8bc..738fb73 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -32,7 +32,6 @@
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -82,9 +81,6 @@
override fun addViews(constraintLayout: ConstraintLayout) {}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
disposableHandle?.dispose()
disposableHandle =
KeyguardClockViewBinder.bind(
@@ -99,20 +95,12 @@
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
keyguardClockViewModel.currentClock.value?.let { clock ->
constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
}
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
disposableHandle?.dispose()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 3a791fd..4bfe5f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -24,7 +24,6 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.NotificationPanelView
@@ -54,32 +53,25 @@
sharedNotificationContainerBinder,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
constraintSet.apply {
val bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
- if (MigrateClocksToBlueprint.isEnabled) {
- val useLargeScreenHeader =
- context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)
- val marginTopLargeScreen =
- largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
- connect(
- R.id.nssl_placeholder,
- TOP,
- R.id.smart_space_barrier_bottom,
- BOTTOM,
- bottomMargin +
- if (useLargeScreenHeader) {
- marginTopLargeScreen
- } else {
- 0
- }
- )
- } else {
- connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, BOTTOM, bottomMargin)
- }
+ val useLargeScreenHeader =
+ context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)
+ val marginTopLargeScreen =
+ largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+ connect(
+ R.id.nssl_placeholder,
+ TOP,
+ R.id.smart_space_barrier_bottom,
+ BOTTOM,
+ bottomMargin +
+ if (useLargeScreenHeader) {
+ marginTopLargeScreen
+ } else {
+ 0
+ },
+ )
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index 620cc13..fc26d18 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -25,7 +25,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
@@ -62,9 +61,6 @@
}
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
// This moves the existing NSSL view to a different parent, as the controller is a
// singleton and recreating it has other bad side effects.
// In the SceneContainer, this is done by the NotificationSection composable.
@@ -78,10 +74,6 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
-
disposableHandle?.dispose()
disposableHandle =
sharedNotificationContainerBinder.bind(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 73e14b1..cd038d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -26,7 +26,6 @@
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -70,7 +69,6 @@
}
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
smartspaceView = smartspaceController.buildAndConnectView(constraintLayout)
weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout)
@@ -98,7 +96,6 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
disposableHandle?.dispose()
disposableHandle =
@@ -111,13 +108,11 @@
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
val dateWeatherPaddingStart = KeyguardSmartspaceViewModel.getDateWeatherStartMargin(context)
val smartspaceHorizontalPadding =
KeyguardSmartspaceViewModel.getSmartspaceHorizontalMargin(context)
constraintSet.apply {
- // migrate addDateWeatherView, addWeatherView from KeyguardClockSwitchController
constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
connect(
@@ -128,7 +123,6 @@
dateWeatherPaddingStart,
)
- // migrate addSmartspaceView from KeyguardClockSwitchController
constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT)
constrainWidth(sharedR.id.bc_smartspace_view, ConstraintSet.MATCH_CONSTRAINT)
connect(
@@ -182,7 +176,6 @@
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
listOf(smartspaceView, dateWeatherView).forEach {
it?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
new file mode 100644
index 0000000..dd2525f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.lottie
+
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieListener
+import com.airbnb.lottie.LottieTask
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Suspends until [LottieTask] is finished with a result or a failure.
+ *
+ * @return result of the [LottieTask] when it's successful
+ */
+suspend fun LottieTask<LottieComposition>.await() =
+ suspendCancellableCoroutine<LottieComposition> { continuation ->
+ val resultListener =
+ LottieListener<LottieComposition> { result ->
+ with(continuation) { if (!isCancelled && !isCompleted) resume(result) }
+ }
+ val failureListener =
+ LottieListener<Throwable> { throwable ->
+ with(continuation) {
+ if (!isCancelled && !isCompleted) resumeWithException(throwable)
+ }
+ }
+ addListener(resultListener)
+ addFailureListener(failureListener)
+ continuation.invokeOnCancellation {
+ removeListener(resultListener)
+ removeFailureListener(failureListener)
+ }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 574ccee..ab998d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -366,7 +366,6 @@
/ (double) seekBar.getMax());
mVolumeValueText.setText(mContext.getResources().getString(
R.string.media_output_dialog_volume_percentage, percentage));
- mVolumeValueText.setVisibility(View.VISIBLE);
if (mStartFromMute) {
updateUnmutedVolumeIcon(device);
mStartFromMute = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 91a3120..1e608af1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -67,6 +67,7 @@
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.DeviceConfigProxy
@@ -141,7 +142,6 @@
class FgsManagerControllerImpl
@Inject
constructor(
- @ShadeDisplayAware private val context: Context,
@ShadeDisplayAware private val resources: Resources,
@Main private val mainExecutor: Executor,
@Background private val backgroundExecutor: Executor,
@@ -155,6 +155,7 @@
private val broadcastDispatcher: BroadcastDispatcher,
private val dumpManager: DumpManager,
private val systemUIDialogFactory: SystemUIDialog.Factory,
+ private val shadeDialogContextRepository: ShadeDialogContextInteractor,
) : Dumpable, FgsManagerController {
companion object {
@@ -388,7 +389,7 @@
override fun showDialog(expandable: Expandable?) {
synchronized(lock) {
if (dialog == null) {
- val dialog = systemUIDialogFactory.create(context)
+ val dialog = systemUIDialogFactory.create(shadeDialogContextRepository.context)
dialog.setTitle(R.string.fgs_manager_dialog_title)
dialog.setMessage(R.string.fgs_manager_dialog_message)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
index 790793e..3049a40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
@@ -17,16 +17,16 @@
package com.android.systemui.qs.composefragment.ui
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.drawscope.clipRect
-import androidx.compose.ui.graphics.layer.CompositingStrategy
-import androidx.compose.ui.graphics.layer.drawLayer
+import androidx.compose.ui.graphics.graphicsLayer
/**
* Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
@@ -34,16 +34,16 @@
* from the QS container.
*/
fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
- return this.drawWithCache {
+ return this.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
+ .drawWithContent {
+ drawContent()
val params = clipParams()
val left = -params.leftInset.toFloat()
val right = size.width + params.rightInset.toFloat()
val top = params.top.toFloat()
val bottom = params.bottom.toFloat()
- val graphicsLayer = obtainGraphicsLayer()
- graphicsLayer.compositingStrategy = CompositingStrategy.Offscreen
- graphicsLayer.record {
- drawContent()
+ val clipSize = Size(right - left, bottom - top)
+ if (!clipSize.isEmpty()) {
clipRect {
drawRoundRect(
color = Color.Black,
@@ -54,9 +54,6 @@
)
}
}
- onDrawWithContent {
- drawLayer(graphicsLayer)
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 873059e..e7fa271 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -675,17 +675,11 @@
}
private void add() {
- if (addFromPosition(getLayoutPosition())) {
- itemView.announceForAccessibility(
- itemView.getContext().getText(R.string.accessibility_qs_edit_tile_added));
- }
+ addFromPosition(getLayoutPosition());
}
private void remove() {
- if (removeFromPosition(getLayoutPosition())) {
- itemView.announceForAccessibility(
- itemView.getContext().getText(R.string.accessibility_qs_edit_tile_removed));
- }
+ removeFromPosition(getLayoutPosition());
}
boolean isCurrentTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
index 85db952..f3c06a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/EditModeButton.kt
@@ -54,7 +54,7 @@
) {
Icon(
imageVector = Icons.Default.Edit,
- contentDescription = stringResource(id = R.string.qs_edit),
+ contentDescription = stringResource(id = R.string.accessibility_quick_settings_edit),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 42a0cb1..b7ff63c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -41,6 +41,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -56,6 +57,7 @@
private final DataSaverController mDataSaverController;
private final DialogTransitionAnimator mDialogTransitionAnimator;
private final SystemUIDialog.Factory mSystemUIDialogFactory;
+ private final ShadeDialogContextInteractor mShadeDialogContextInteractor;
@Inject
public DataSaverTile(
@@ -70,13 +72,15 @@
QSLogger qsLogger,
DataSaverController dataSaverController,
DialogTransitionAnimator dialogTransitionAnimator,
- SystemUIDialog.Factory systemUIDialogFactory
+ SystemUIDialog.Factory systemUIDialogFactory,
+ ShadeDialogContextInteractor shadeDialogContextInteractor
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mDataSaverController = dataSaverController;
mDialogTransitionAnimator = dialogTransitionAnimator;
mSystemUIDialogFactory = systemUIDialogFactory;
+ mShadeDialogContextInteractor = shadeDialogContextInteractor;
mDataSaverController.observe(getLifecycle(), this);
}
@@ -102,7 +106,8 @@
// Show a dialog to confirm first. Dialogs shown by the DialogTransitionAnimator must be
// created and shown on the main thread, so we post it to the UI handler.
mUiHandler.post(() -> {
- SystemUIDialog dialog = mSystemUIDialogFactory.create(mContext);
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(
+ mShadeDialogContextInteractor.getContext());
dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
dialog.setMessage(com.android.internal.R.string.data_saver_description);
dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 19b45d5..7516ca0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -193,7 +193,7 @@
if (mJob == null) {
mJob = WifiUtils.checkWepAllowed(mContext, mCoroutineScope, wifiEntry.getSsid(),
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, intent -> {
- mInternetDialogController.startActivity(intent, view);
+ mInternetDialogController.startActivityForDialog(intent);
return null;
}, () -> {
wifiConnect(wifiEntry, view);
@@ -211,7 +211,7 @@
true /* connectForCaller */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- mContext.startActivity(intent);
+ mInternetDialogController.startActivityForDialog(intent);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index dbe1ae9..7036ef91 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -781,6 +781,10 @@
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller);
}
+ void startActivityForDialog(Intent intent) {
+ mActivityStarter.startActivity(intent, false /* dismissShade */);
+ }
+
void launchNetworkSetting(View view) {
startActivity(getSettingsIntent(), view);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 70c2a2a..5e9deec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -72,6 +72,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeDisplayAware;
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wifitrackerlib.WifiEntry;
@@ -104,9 +105,9 @@
private final Handler mHandler;
private final Executor mBackgroundExecutor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
- private final Context mContext;
private final boolean mAboveStatusBar;
private final SystemUIDialog.Factory mSystemUIDialogFactory;
+ private final ShadeDialogContextInteractor mShadeDialogContextInteractor;
@VisibleForTesting
protected InternetAdapter mAdapter;
@@ -204,10 +205,11 @@
@Main Handler handler,
@Background Executor executor,
KeyguardStateController keyguardStateController,
- SystemUIDialog.Factory systemUIDialogFactory) {
- mContext = context;
+ SystemUIDialog.Factory systemUIDialogFactory,
+ ShadeDialogContextInteractor shadeDialogContextInteractor) {
mAboveStatusBar = aboveStatusBar;
mSystemUIDialogFactory = systemUIDialogFactory;
+ mShadeDialogContextInteractor = shadeDialogContextInteractor;
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
}
@@ -230,7 +232,8 @@
@Override
public SystemUIDialog createDialog() {
- SystemUIDialog dialog = mSystemUIDialogFactory.create(this, mContext);
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(this,
+ mShadeDialogContextInteractor.getContext());
if (!mAboveStatusBar) {
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 8c54ab40..862dba1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.user
import android.app.Dialog
-import android.content.Context
import android.content.DialogInterface
import android.content.DialogInterface.BUTTON_NEUTRAL
import android.content.Intent
@@ -34,6 +33,7 @@
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.user.ui.dialog.DialogShowerImpl
import javax.inject.Inject
@@ -50,6 +50,7 @@
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val uiEventLogger: UiEventLogger,
private val dialogFactory: SystemUIDialog.Factory,
+ private val shadeDialogContextInteractor: ShadeDialogContextInteractor,
) {
companion object {
@@ -63,7 +64,8 @@
* Populate the dialog with information from and adapter obtained from
* [userDetailViewAdapterProvider] and show it as launched from [expandable].
*/
- fun showDialog(context: Context, expandable: Expandable) {
+ fun showDialog(expandable: Expandable) {
+ val context = shadeDialogContextInteractor.context
with(dialogFactory.create(context)) {
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index a7b51faa..10ac2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -16,6 +16,8 @@
package com.android.systemui.scrim;
+import static com.android.systemui.Flags.notificationShadeBlur;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -214,8 +216,7 @@
public void draw(@NonNull Canvas canvas) {
mPaint.setColor(mMainColor);
mPaint.setAlpha(mAlpha);
- if (WindowBlurFlag.isEnabled()) {
- // TODO(b/370555223): Match the alpha to the visual spec when it is finalized.
+ if (notificationShadeBlur() || WindowBlurFlag.isEnabled()) {
// TODO (b/381263600), wire this at ScrimController, move it to PrimaryBouncerTransition
mPaint.setAlpha((int) (0.5f * mAlpha));
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 4bfa61e..0f80e74 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -16,6 +16,8 @@
package com.android.systemui.scrim;
+import static com.android.systemui.Flags.notificationShadeBlur;
+
import static java.lang.Float.isNaN;
import android.annotation.NonNull;
@@ -39,13 +41,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.res.R;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.util.LargeScreenUtils;
import java.util.concurrent.Executor;
-import static com.android.systemui.Flags.notificationShadeBlur;
-
/**
* A view which can draw a scrim. This view maybe be used in multiple windows running on different
* threads, but is controlled by {@link com.android.systemui.statusbar.phone.ScrimController} so we
@@ -253,8 +254,11 @@
mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount);
}
if (notificationShadeBlur()) {
- // TODO(b/370555223): Fix color and transparency to match visual spec exactly
- mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), Color.GRAY, 0.5f);
+ int layerAbove = ColorUtils.setAlphaComponent(
+ getResources().getColor(R.color.shade_panel, null),
+ (int) (0.4f * 255));
+ int layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.1f * 255));
+ mainTinted = ColorUtils.compositeColors(layerAbove, layerBelow);
}
drawable.setColor(mainTinted, animated);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index e1631cc..bbb13d5 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -61,9 +61,18 @@
/** Callback for notifying of changes. */
@WeaklyReferencedCallback
interface Callback {
- /** Notifies that the current user will be changed. */
+ /**
+ * Same as {@link onBeforeUserSwitching(Int, Runnable)} but the callback will be called
+ * automatically after the completion of this method.
+ */
fun onBeforeUserSwitching(newUser: Int) {}
+ /** Notifies that the current user will be changed. */
+ fun onBeforeUserSwitching(newUser: Int, resultCallback: Runnable) {
+ onBeforeUserSwitching(newUser)
+ resultCallback.run()
+ }
+
/**
* Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be called
* automatically after the completion of this method.
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index b7a3aed..42d8363 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -196,8 +196,9 @@
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(
object : UserSwitchObserver() {
- override fun onBeforeUserSwitching(newUserId: Int) {
+ override fun onBeforeUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
handleBeforeUserSwitching(newUserId)
+ reply?.sendResult(null)
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
@@ -236,8 +237,7 @@
setUserIdInternal(newUserId)
notifySubscribers { callback, resultCallback ->
- callback.onBeforeUserSwitching(newUserId)
- resultCallback.run()
+ callback.onBeforeUserSwitching(newUserId, resultCallback)
}
.await()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index f2c3906..839d459 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -48,7 +48,6 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -367,9 +366,7 @@
mTouchActive = true;
mTouchCancelled = false;
mDownEvent = ev;
- if (MigrateClocksToBlueprint.isEnabled()) {
- mService.userActivity();
- }
+ mService.userActivity();
} else if (ev.getActionMasked() == MotionEvent.ACTION_UP
|| ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
mTouchActive = false;
@@ -443,8 +440,7 @@
float x = ev.getRawX();
float y = ev.getRawY();
if (mStatusBarViewController.touchIsWithinView(x, y)) {
- if (!(MigrateClocksToBlueprint.isEnabled()
- && mPrimaryBouncerInteractor.isBouncerShowing())) {
+ if (!mPrimaryBouncerInteractor.isBouncerShowing()) {
if (mStatusBarWindowStateController.windowIsShowing()) {
mIsTrackingBarGesture = true;
return logDownDispatch(ev, "sending touch to status bar",
@@ -453,7 +449,7 @@
return logDownDispatch(ev, "hidden or hiding", true);
}
} else {
- mShadeLogger.d("NSWVC: bouncer not showing");
+ mShadeLogger.d("NSWVC: bouncer showing");
}
} else {
mShadeLogger.d("NSWVC: touch not within view");
@@ -511,34 +507,24 @@
&& !bouncerShowing
&& !mStatusBarStateController.isDozing()) {
if (mDragDownHelper.isDragDownEnabled()) {
- if (MigrateClocksToBlueprint.isEnabled()) {
- // When on lockscreen, if the touch originates at the top of the screen
- // go directly to QS and not the shade
- if (mStatusBarStateController.getState() == KEYGUARD
- && mQuickSettingsController.shouldQuickSettingsIntercept(
- ev.getX(), ev.getY(), 0)) {
- mShadeLogger.d("NSWVC: QS intercepted");
- return true;
- }
+ // When on lockscreen, if the touch originates at the top of the screen go
+ // directly to QS and not the shade
+ if (mStatusBarStateController.getState() == KEYGUARD
+ && mQuickSettingsController.shouldQuickSettingsIntercept(
+ ev.getX(), ev.getY(), 0)) {
+ mShadeLogger.d("NSWVC: QS intercepted");
+ return true;
}
// This handles drag down over lockscreen
boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
- if (MigrateClocksToBlueprint.isEnabled()) {
- if (result) {
- mLastInterceptWasDragDownHelper = true;
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: drag down helper intercepted");
- }
- } else if (didNotificationPanelInterceptEvent(ev)) {
- return true;
+ if (result) {
+ mLastInterceptWasDragDownHelper = true;
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mShadeLogger.d("NSWVC: drag down helper intercepted");
}
- } else {
- if (result) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: drag down helper intercepted");
- }
- }
+ } else if (didNotificationPanelInterceptEvent(ev)) {
+ return true;
}
return result;
} else {
@@ -547,12 +533,10 @@
return true;
}
}
- } else if (MigrateClocksToBlueprint.isEnabled()) {
+ } else if (!bouncerShowing && didNotificationPanelInterceptEvent(ev)) {
// This final check handles swipes on HUNs and when Pulsing
- if (!bouncerShowing && didNotificationPanelInterceptEvent(ev)) {
- mShadeLogger.d("NSWVC: intercepted for HUN/PULSING");
- return true;
- }
+ mShadeLogger.d("NSWVC: intercepted for HUN/PULSING");
+ return true;
}
return false;
}
@@ -562,9 +546,6 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- if (!MigrateClocksToBlueprint.isEnabled()) {
- mShadeViewController.handleExternalInterceptTouch(cancellation);
- }
cancellation.recycle();
}
@@ -574,22 +555,12 @@
if (mStatusBarStateController.isDozing()) {
handled = !mDozeServiceHost.isPulsing();
}
- if (MigrateClocksToBlueprint.isEnabled()) {
- if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
- // we still want to finish our drag down gesture when locking the screen
- handled |= mDragDownHelper.onTouchEvent(ev) || handled;
- }
- if (!handled && mShadeViewController.handleExternalTouch(ev)) {
- return true;
- }
- } else {
- if (mDragDownHelper.isDragDownEnabled()
- || mDragDownHelper.isDraggingDown()) {
- // we still want to finish our drag down gesture when locking the screen
- return mDragDownHelper.onTouchEvent(ev) || handled;
- } else {
- return handled;
- }
+ if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
+ // we still want to finish our drag down gesture when locking the screen
+ handled |= mDragDownHelper.onTouchEvent(ev) || handled;
+ }
+ if (!handled && mShadeViewController.handleExternalTouch(ev)) {
+ return true;
}
return handled;
}
@@ -673,14 +644,12 @@
}
private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
- if (MigrateClocksToBlueprint.isEnabled()) {
- // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need
- // to also ask NotificationPanelViewController directly, in order to process swipe up
- // events originating from notifications
- if (mShadeViewController.handleExternalInterceptTouch(ev)) {
- mShadeLogger.d("NSWVC: NPVC intercepted");
- return true;
- }
+ // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need to
+ // also ask NotificationPanelViewController directly, in order to process swipe up events
+ // originating from notifications
+ if (mShadeViewController.handleExternalInterceptTouch(ev)) {
+ mShadeLogger.d("NSWVC: NPVC intercepted");
+ return true;
}
return false;
@@ -707,9 +676,7 @@
if (!SceneContainerFlag.isEnabled()) {
mAmbientState.setSwipingUp(false);
}
- if (MigrateClocksToBlueprint.isEnabled()) {
- mDragDownHelper.stopDragging();
- }
+ mDragDownHelper.stopDragging();
}
private void setBrightnessMirrorShowingForDepth(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 207439e..5811157 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -21,7 +21,6 @@
import android.view.WindowInsets
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
@@ -32,7 +31,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.fragments.FragmentService
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
@@ -275,7 +273,6 @@
constraintSet.clone(mView)
setKeyguardStatusViewConstraints(constraintSet)
setQsConstraints(constraintSet)
- setNotificationsConstraints(constraintSet)
setLargeScreenShadeHeaderConstraints(constraintSet)
mView.applyConstraints(constraintSet)
}
@@ -288,21 +285,6 @@
}
}
- private fun setNotificationsConstraints(constraintSet: ConstraintSet) {
- if (MigrateClocksToBlueprint.isEnabled) {
- return
- }
- val startConstraintId = if (splitShadeEnabled) R.id.qs_edge_guideline else PARENT_ID
- val nsslId = R.id.notification_stack_scroller
- constraintSet.apply {
- connect(nsslId, START, startConstraintId, START)
- setMargin(nsslId, START, if (splitShadeEnabled) 0 else panelMarginHorizontal)
- setMargin(nsslId, END, panelMarginHorizontal)
- setMargin(nsslId, TOP, topMargin)
- setMargin(nsslId, BOTTOM, notificationsBottomMargin)
- }
- }
-
private fun setQsConstraints(constraintSet: ConstraintSet) {
val endConstraintId = if (splitShadeEnabled) R.id.qs_edge_guideline else PARENT_ID
constraintSet.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 1333055..000a666 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -21,7 +21,6 @@
import android.app.Fragment;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -33,13 +32,10 @@
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import java.util.ArrayList;
-import java.util.Comparator;
import java.util.function.Consumer;
/**
@@ -50,11 +46,7 @@
private View mQsFrame;
private View mStackScroller;
- private View mKeyguardStatusBar;
- private final ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
- private final ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
- private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
@@ -80,7 +72,6 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQsFrame = findViewById(R.id.qs_frame);
- mKeyguardStatusBar = findViewById(R.id.keyguard_header);
}
void setStackScroller(View stackScroller) {
@@ -160,46 +151,11 @@
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- mDrawingOrderedChildren.clear();
- mLayoutDrawingOrder.clear();
- if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mKeyguardStatusBar);
- mLayoutDrawingOrder.add(mKeyguardStatusBar);
- }
- if (mQsFrame.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mQsFrame);
- mLayoutDrawingOrder.add(mQsFrame);
- }
- if (mStackScroller.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mStackScroller);
- mLayoutDrawingOrder.add(mStackScroller);
- }
-
- // Let's now find the order that the view has when drawing regularly by sorting
- mLayoutDrawingOrder.sort(mIndexComparator);
- super.dispatchDraw(canvas);
- }
-
- @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return TouchLogger.logDispatchTouch("NotificationsQuickSettingsContainer", ev,
super.dispatchTouchEvent(ev));
}
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (MigrateClocksToBlueprint.isEnabled()) {
- return super.drawChild(canvas, child, drawingTime);
- }
- int layoutIndex = mLayoutDrawingOrder.indexOf(child);
- if (layoutIndex >= 0) {
- return super.drawChild(canvas, mDrawingOrderedChildren.get(layoutIndex), drawingTime);
- } else {
- return super.drawChild(canvas, child, drawingTime);
- }
- }
-
public void applyConstraints(ConstraintSet constraintSet) {
constraintSet.applyTo(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 0df2299..4fb43fd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -68,7 +68,6 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
@@ -1828,16 +1827,6 @@
"onQsIntercept: down action, QS partially expanded/collapsed");
return true;
}
- // TODO (b/265193930): remove dependency on NPVC
- if (mPanelViewControllerLazy.get().isKeyguardShowing()
- && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
- // Dragging down on the lockscreen statusbar should prohibit other interactions
- // immediately, otherwise we'll wait on the touchslop. This is to allow
- // dragging down to expanded quick settings directly on the lockscreen.
- if (!MigrateClocksToBlueprint.isEnabled()) {
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
- }
- }
if (mExpansionAnimator != null) {
mInitialHeightOnTouch = mExpansionHeight;
mShadeLog.logMotionEvent(event,
@@ -1879,9 +1868,6 @@
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(
mInitialTouchX, mInitialTouchY, h)) {
- if (!MigrateClocksToBlueprint.isEnabled()) {
- mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
- }
mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
setTracking(true);
traceQsJank(true, false);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index d31868c..61b9f08 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -35,6 +35,8 @@
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractorImpl
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl
import com.android.systemui.shade.display.ShadeDisplayPolicyModule
@@ -216,6 +218,25 @@
}
@Provides
+ @SysUISingleton
+ fun provideShadeDialogContextInteractor(
+ impl: ShadeDialogContextInteractorImpl
+ ): ShadeDialogContextInteractor = impl
+
+ @Provides
+ @IntoMap
+ @ClassKey(ShadeDialogContextInteractor::class)
+ fun provideShadeDialogContextInteractorCoreStartable(
+ impl: Provider<ShadeDialogContextInteractorImpl>
+ ): CoreStartable {
+ return if (ShadeWindowGoesAround.isEnabled) {
+ impl.get()
+ } else {
+ CoreStartable.NOP
+ }
+ }
+
+ @Provides
@IntoMap
@ClassKey(ShadePrimaryDisplayCommand::class)
fun provideShadePrimaryDisplayCommand(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/FakeShadeDialogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/FakeShadeDialogContextInteractor.kt
new file mode 100644
index 0000000..455370c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/FakeShadeDialogContextInteractor.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.content.Context
+
+/** Fake context repository that always returns the same context. */
+class FakeShadeDialogContextInteractor(override val context: Context) :
+ ShadeDialogContextInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt
new file mode 100644
index 0000000..201dc03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.content.Context
+import android.util.Log
+import android.view.Display
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
+import com.android.app.tracing.coroutines.launchTraced
+import com.android.app.tracing.traceSection
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filter
+
+/** Provides the correct context to show dialogs on the shade window, whenever it moves. */
+interface ShadeDialogContextInteractor {
+ /** Context usable to create dialogs on the notification shade display. */
+ val context: Context
+}
+
+@SysUISingleton
+class ShadeDialogContextInteractorImpl
+@Inject
+constructor(
+ @Main private val defaultContext: Context,
+ private val displayWindowPropertyRepository: Provider<DisplayWindowPropertiesRepository>,
+ private val shadeDisplaysRepository: ShadeDisplaysRepository,
+ @Background private val bgScope: CoroutineScope,
+) : CoreStartable, ShadeDialogContextInteractor {
+
+ override fun start() {
+ if (ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()) return
+ bgScope.launchTraced(TAG) {
+ shadeDisplaysRepository.displayId
+ // No need for default display pre-warming.
+ .filter { it != Display.DEFAULT_DISPLAY }
+ .collectLatest { displayId ->
+ // Prewarms the context in the background every time the display changes.
+ // In this way, there will be no main thread delays when a dialog is shown.
+ getContextOrDefault(displayId)
+ }
+ }
+ }
+
+ override val context: Context
+ get() {
+ if (!ShadeWindowGoesAround.isEnabled) {
+ return defaultContext
+ }
+ val displayId = shadeDisplaysRepository.displayId.value
+ return getContextOrDefault(displayId)
+ }
+
+ private fun getContextOrDefault(displayId: Int): Context {
+ return try {
+ traceSection({ "Getting dialog context for displayId=$displayId" }) {
+ displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE).context
+ }
+ } catch (e: Exception) {
+ // This can happen if the display was disconnected in the meantime.
+ Log.e(
+ TAG,
+ "Couldn't get dialog context for displayId=$displayId. Returning default one",
+ e,
+ )
+ defaultContext
+ }
+ }
+
+ private companion object {
+ const val TAG = "ShadeDialogContextRepo"
+ const val DIALOG_WINDOW_TYPE = TYPE_STATUS_BAR_SUB_PANEL
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index b2ca33a..a7ad462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -44,13 +44,11 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -136,7 +134,6 @@
private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
// These views are used by InteractionJankMonitor to get callback from HWUI.
private View mView;
- private KeyguardClockSwitch mClockSwitchView;
/**
* If any of the system bars is hidden.
@@ -426,7 +423,6 @@
if ((mView == null || !mView.isAttachedToWindow())
&& (view != null && view.isAttachedToWindow())) {
mView = view;
- mClockSwitchView = view.findViewById(R.id.keyguard_clock_container);
}
mDozeAmountTarget = dozeAmount;
if (animated) {
@@ -511,16 +507,7 @@
/** Returns the id of the currently rendering clock */
public String getClockId() {
- if (MigrateClocksToBlueprint.isEnabled()) {
- return mKeyguardClockInteractorLazy.get().getRenderedClockId();
- }
-
- if (mClockSwitchView == null) {
- Log.e(TAG, "Clock container was missing");
- return KeyguardClockSwitch.MISSING_CLOCK_ID;
- }
-
- return mClockSwitchView.getClockId();
+ return mKeyguardClockInteractorLazy.get().getRenderedClockId();
}
private void beginInteractionJankMonitor() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 8a1371f..aa010cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.notification.domain.NotificationDomainLayerModule;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor;
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModelModule;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.icon.ConversationIconManager;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
@@ -78,8 +79,7 @@
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.logging.dagger.NotificationsLogModule;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
@@ -92,7 +92,6 @@
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModesCleanupStartable;
import dagger.Binds;
@@ -105,8 +104,6 @@
import kotlinx.coroutines.CoroutineScope;
-import java.util.Optional;
-
import javax.inject.Provider;
/**
@@ -315,21 +312,17 @@
@ClassKey(ZenModesCleanupStartable.class)
CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup);
- /**
- * Provides {@link
- * com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor} if
- * one of the relevant feature flags is enabled.
- */
+ /** Provides the default implementation of {@link PromotedNotificationContentExtractor} if at
+ * least one of the relevant feature flags is enabled, or an implementation that always returns
+ * null if none are enabled. */
@Provides
@SysUISingleton
- static Optional<PromotedNotificationContentExtractor>
- providePromotedNotificationContentExtractor(
- PromotedNotificationsProvider provider, Context context,
- PromotedNotificationLogger logger) {
+ static PromotedNotificationContentExtractor providesPromotedNotificationContentExtractor(
+ Provider<PromotedNotificationContentExtractorImpl> implProvider) {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- return Optional.of(new PromotedNotificationContentExtractor(provider, context, logger));
+ return implProvider.get();
} else {
- return Optional.empty();
+ return (entry, recoveredBuilder) -> null;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
index 6756077..d02e17c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java
@@ -1299,7 +1299,6 @@
}
private NotificationEntry requireEntry() {
- /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode();
return Objects.requireNonNull(mEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index 863c665..4e9e333 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -34,15 +34,22 @@
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
import javax.inject.Inject
+interface PromotedNotificationContentExtractor {
+ fun extractContent(
+ entry: NotificationEntry,
+ recoveredBuilder: Notification.Builder,
+ ): PromotedNotificationContentModel?
+}
+
@SysUISingleton
-class PromotedNotificationContentExtractor
+class PromotedNotificationContentExtractorImpl
@Inject
constructor(
private val promotedNotificationsProvider: PromotedNotificationsProvider,
@ShadeDisplayAware private val context: Context,
private val logger: PromotedNotificationLogger,
-) {
- fun extractContent(
+) : PromotedNotificationContentExtractor {
+ override fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
): PromotedNotificationContentModel? {
@@ -169,5 +176,5 @@
private fun ProgressStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) {
// TODO: Create NotificationProgressModel.toSkeleton, or something similar.
- contentBuilder.progress = createProgressModel(0xffffffff.toInt(), 0x00000000)
+ contentBuilder.progress = createProgressModel(0xffffffff.toInt(), 0xff000000.toInt())
}
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 6e05e8e..6eeb80d 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
@@ -921,7 +921,7 @@
logger.logAsyncTaskProgress(entry, "finishing");
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.setPromotedNotificationContentModel(result.mExtractedPromotedNotificationContent);
+ entry.setPromotedNotificationContentModel(result.mPromotedContent);
}
boolean setRepliesAndActions = true;
@@ -1292,10 +1292,13 @@
if (PromotedNotificationContentModel.featureFlagEnabled()) {
mLogger.logAsyncTaskProgress(mEntry, "extracting promoted notification content");
- result.mExtractedPromotedNotificationContent = mPromotedNotificationContentExtractor
- .extractContent(mEntry, recoveredBuilder);
+ final PromotedNotificationContentModel promotedContent =
+ mPromotedNotificationContentExtractor.extractContent(mEntry,
+ recoveredBuilder);
mLogger.logAsyncTaskProgress(mEntry, "extracted promoted notification content: "
- + result.mExtractedPromotedNotificationContent);
+ + promotedContent);
+
+ result.mPromotedContent = promotedContent;
}
mLogger.logAsyncTaskProgress(mEntry,
@@ -1399,7 +1402,7 @@
@VisibleForTesting
static class InflationProgress {
- PromotedNotificationContentModel mExtractedPromotedNotificationContent;
+ PromotedNotificationContentModel mPromotedContent;
private RemoteViews newContentView;
private RemoteViews newHeadsUpView;
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 c7d80e9..7dcb2de 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
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row
import android.annotation.SuppressLint
-import android.app.Flags
import android.app.Notification
import android.content.Context
import android.content.ContextWrapper
@@ -591,7 +590,7 @@
@VisibleForTesting val packageContext: Context,
val remoteViews: NewRemoteViews,
val contentModel: NotificationContentModel,
- val extractedPromotedNotificationContentModel: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModel?,
) {
var inflatedContentView: View? = null
@@ -683,16 +682,15 @@
promotedNotificationContentExtractor: PromotedNotificationContentExtractor,
logger: NotificationRowContentBinderLogger,
): InflationProgress {
- val promoted =
+ val promotedContent =
if (PromotedNotificationContentModel.featureFlagEnabled()) {
logger.logAsyncTaskProgress(entry, "extracting promoted notification content")
- val extracted =
- promotedNotificationContentExtractor.extractContent(entry, builder)
- logger.logAsyncTaskProgress(
- entry,
- "extracted promoted notification content: {extracted}",
- )
- extracted
+ promotedNotificationContentExtractor.extractContent(entry, builder).also {
+ logger.logAsyncTaskProgress(
+ entry,
+ "extracted promoted notification content: $it",
+ )
+ }
} else {
null
}
@@ -759,7 +757,7 @@
packageContext = packageContext,
remoteViews = remoteViews,
contentModel = contentModel,
- extractedPromotedNotificationContentModel = promoted,
+ promotedContent = promotedContent,
)
}
@@ -1420,8 +1418,7 @@
entry.setContentModel(result.contentModel)
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.promotedNotificationContentModel =
- result.extractedPromotedNotificationContentModel
+ entry.promotedNotificationContentModel = result.promotedContent
}
result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
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 7c9d850..38a7035 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
@@ -3729,14 +3729,6 @@
// Only when scene container is enabled, mark that we are being dragged so that we start
// dispatching the rest of the gesture to scene container.
- void startOverscrollAfterExpanding() {
- if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
- getExpandHelper().finishExpanding();
- setIsBeingDragged(true);
- }
-
- // Only when scene container is enabled, mark that we are being dragged so that we start
- // dispatching the rest of the gesture to scene container.
void startDraggingOnHun() {
if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
setIsBeingDragged(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 245b1d2..a33a9ed 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
@@ -2203,11 +2203,10 @@
expandingNotification = mView.isExpandingNotification();
if (mView.getExpandedInThisMotion() && !expandingNotification && wasExpandingBefore
&& !mView.getDisallowScrollingInThisMotion()) {
- // We need to dispatch the overscroll differently when Scene Container is on,
- // since NSSL no longer controls its own scroll.
+ // Finish expansion here, as this gesture will be marked to be sent to
+ // scene container
if (SceneContainerFlag.isEnabled() && !isCancelOrUp) {
- mView.startOverscrollAfterExpanding();
- return true;
+ expandHelper.finishExpanding();
} else {
mView.dispatchDownEventToScroller(ev);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 7bea480..6dc25aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -21,9 +21,10 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
-import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
@@ -207,6 +208,7 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -221,7 +223,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
@@ -2514,12 +2515,15 @@
* should update only the status bar components.
*/
private void setBouncerShowingForStatusBarComponents(boolean bouncerShowing) {
- int importance = bouncerShowing
- ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
if (!StatusBarConnectedDisplays.isEnabled() && mPhoneStatusBarViewController != null) {
+ int importance = bouncerShowing
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
mPhoneStatusBarViewController.setImportantForAccessibility(importance);
}
+ int importance = bouncerShowing
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_NO;
mShadeSurface.setImportantForAccessibility(importance);
mShadeSurface.setBouncerShowing(bouncerShowing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 78954de..8b60ee5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -192,7 +192,7 @@
mUiEventLogger.log(
LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP);
- mUserSwitchDialogController.showDialog(mUserAvatarViewWithBackground.getContext(),
+ mUserSwitchDialogController.showDialog(
Expandable.fromView(mUserAvatarViewWithBackground));
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
index a1d5cbe..9ff0d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
@@ -44,6 +44,7 @@
import com.android.systemui.dialog.ui.composable.AlertDialogContent
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -67,6 +68,7 @@
private val viewModel: Provider<ModesDialogViewModel>,
private val dialogEventLogger: ModesDialogEventLogger,
@Main private val mainCoroutineContext: CoroutineContext,
+ private val shadeDisplayContextRepository: ShadeDialogContextInteractor,
) : SystemUIDialog.Delegate {
// NOTE: This should only be accessed/written from the main thread.
@VisibleForTesting var currentDialog: ComponentSystemUIDialog? = null
@@ -78,7 +80,10 @@
currentDialog?.dismiss()
}
- currentDialog = sysuiDialogFactory.create { ModesDialogContent(it) }
+ currentDialog =
+ sysuiDialogFactory.create(context = shadeDisplayContextRepository.context) {
+ ModesDialogContent(it)
+ }
currentDialog
?.lifecycle
?.addObserver(
@@ -106,9 +111,8 @@
modifier =
Modifier.semantics {
testTagsAsResourceId = true
- paneTitle = dialog.context.getString(
- R.string.accessibility_desc_quick_settings
- )
+ paneTitle =
+ dialog.context.getString(R.string.accessibility_desc_quick_settings)
},
title = {
Text(
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 102fcc0..e4b2dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -18,7 +18,6 @@
package com.android.systemui.user.ui.dialog
import android.app.Dialog
-import android.content.Context
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.users.UserCreatingDialog
@@ -32,6 +31,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.user.UserSwitchFullscreenDialog
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.domain.model.ShowDialogRequestModel
@@ -48,7 +48,6 @@
class UserSwitcherDialogCoordinator
@Inject
constructor(
- @Application private val context: Lazy<Context>,
@Application private val applicationScope: Lazy<CoroutineScope>,
private val falsingManager: Lazy<FalsingManager>,
private val broadcastSender: Lazy<BroadcastSender>,
@@ -59,6 +58,7 @@
private val activityStarter: Lazy<ActivityStarter>,
private val falsingCollector: Lazy<FalsingCollector>,
private val userSwitcherViewModel: Lazy<UserSwitcherViewModel>,
+ private val shadeDialogContextInteractor: Lazy<ShadeDialogContextInteractor>,
) : CoreStartable {
private var currentDialog: Dialog? = null
@@ -71,12 +71,13 @@
private fun startHandlingDialogShowRequests() {
applicationScope.get().launch {
interactor.get().dialogShowRequests.filterNotNull().collect { request ->
+ val context = shadeDialogContextInteractor.get().context
val (dialog, dialogCuj) =
when (request) {
is ShowDialogRequestModel.ShowAddUserDialog ->
Pair(
AddUserDialog(
- context = context.get(),
+ context = context,
userHandle = request.userHandle,
isKeyguardShowing = request.isKeyguardShowing,
showEphemeralMessage = request.showEphemeralMessage,
@@ -92,7 +93,7 @@
is ShowDialogRequestModel.ShowUserCreationDialog ->
Pair(
UserCreatingDialog(
- context.get(),
+ context,
request.isGuest,
),
null,
@@ -100,7 +101,7 @@
is ShowDialogRequestModel.ShowExitGuestDialog ->
Pair(
ExitGuestDialog(
- context = context.get(),
+ context = context,
guestUserId = request.guestUserId,
isGuestEphemeral = request.isGuestEphemeral,
targetUserId = request.targetUserId,
@@ -117,7 +118,7 @@
is ShowDialogRequestModel.ShowUserSwitcherDialog ->
Pair(
UserSwitchDialog(
- context = context.get(),
+ context = context,
adapter = userDetailAdapterProvider.get(),
uiEventLogger = eventLogger.get(),
falsingManager = falsingManager.get(),
@@ -132,7 +133,7 @@
is ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog ->
Pair(
UserSwitchFullscreenDialog(
- context = context.get(),
+ context = context,
falsingCollector = falsingCollector.get(),
userSwitcherViewModel = userSwitcherViewModel.get(),
),
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
index 2e1f82d..70e342f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -17,45 +17,28 @@
package com.android.systemui.volume.dialog.settings.ui.binder
import android.view.View
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
-import com.android.systemui.lifecycle.viewModel
+import android.widget.ImageButton
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.settings.ui.viewmodel.VolumeDialogSettingsButtonViewModel
import javax.inject.Inject
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@VolumeDialogScope
class VolumeDialogSettingsButtonViewBinder
@Inject
-constructor(private val viewModelFactory: VolumeDialogSettingsButtonViewModel.Factory) {
+constructor(private val viewModel: VolumeDialogSettingsButtonViewModel) {
- fun bind(view: View) {
- with(view) {
- val button = requireViewById<View>(R.id.volume_dialog_settings)
- repeatWhenAttached {
- viewModel(
- traceName = "VolumeDialogViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
- ) { viewModel ->
- setSnapshotBinding {
- viewModel.isVisible
- .onEach { isVisible ->
- visibility = if (isVisible) View.VISIBLE else View.GONE
- }
- .launchIn(this)
+ fun CoroutineScope.bind(view: View) {
+ val button = view.requireViewById<ImageButton>(R.id.volume_dialog_settings)
+ viewModel.isVisible
+ .onEach { isVisible -> button.visibility = if (isVisible) View.VISIBLE else View.GONE }
+ .launchIn(this)
- button.setOnClickListener { viewModel.onButtonClicked() }
- }
+ viewModel.icon.onEach { button.setImageDrawable(it) }.launchIn(this)
- awaitCancellation()
- }
- }
- }
+ button.setOnClickListener { viewModel.onButtonClicked() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
index 015d773..03442db 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -14,27 +14,206 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.volume.dialog.settings.ui.viewmodel
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.ColorFilter
+import android.graphics.drawable.Drawable
+import android.media.session.PlaybackState
+import androidx.annotation.ColorInt
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.SimpleColorFilter
+import com.airbnb.lottie.model.KeyPath
+import com.airbnb.lottie.value.LottieValueCallback
+import com.android.internal.R as internalR
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lottie.await
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.settings.domain.VolumeDialogSettingsButtonInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.shared.model.filterData
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.runningFold
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
+import kotlinx.coroutines.suspendCancellableCoroutine
class VolumeDialogSettingsButtonViewModel
-@AssistedInject
-constructor(private val interactor: VolumeDialogSettingsButtonInteractor) {
+@Inject
+constructor(
+ @Application private val context: Context,
+ @UiBackground private val uiBgCoroutineContext: CoroutineContext,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ mediaOutputInteractor: MediaOutputInteractor,
+ private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+ private val interactor: VolumeDialogSettingsButtonInteractor,
+) {
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ private val drawables: Flow<Drawables> =
+ flow {
+ val color = context.getColor(internalR.color.materialColorPrimary)
+ emit(
+ Drawables(
+ start =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_in)
+ .await()
+ .toDrawable { setColor(color) },
+ playing =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_playing)
+ .await()
+ .toDrawable {
+ repeatCount = LottieDrawable.INFINITE
+ repeatMode = LottieDrawable.RESTART
+ setColor(color)
+ },
+ stop =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_out)
+ .await()
+ .toDrawable { setColor(color) },
+ idle = context.getDrawable(R.drawable.audio_bars_idle)!!,
+ )
+ )
+ }
+ .buffer()
+ .flowOn(uiBgCoroutineContext)
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
val isVisible = interactor.isVisible
+ val icon: Flow<Drawable> =
+ mediaOutputInteractor.defaultActiveMediaSession
+ .filterData()
+ .flatMapLatest { session ->
+ if (session == null) {
+ flowOf(null)
+ } else {
+ mediaDeviceSessionInteractor.playbackState(session)
+ }
+ }
+ .runningFold(null) { playbackStates: PlaybackStates?, playbackState: PlaybackState? ->
+ val isCurrentActive = playbackState?.isActive ?: false
+ if (playbackStates != null && isCurrentActive == playbackState?.isActive) {
+ return@runningFold playbackStates
+ }
+ playbackStates?.copy(
+ isPreviousActive = playbackStates.isCurrentActive,
+ isCurrentActive = isCurrentActive,
+ ) ?: PlaybackStates(isPreviousActive = null, isCurrentActive = isCurrentActive)
+ }
+ .filterNotNull()
+ // only apply the most recent state if we wait for the animation.
+ .buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ // distinct again because the changed state might've been dropped by the buffer
+ .distinctUntilChangedBy { it.isCurrentActive }
+ .transform { emitDrawables(it) }
+ .runningFold(null) { previous: Drawable?, current: Drawable ->
+ // wait for the previous animation to finish before starting the new one
+ // this also waits for the current loop of the playing animation to finish
+ (previous as? LottieDrawable)?.awaitFinish()
+ (current as? LottieDrawable)?.start()
+ current
+ }
+ .filterNotNull()
+
+ private suspend fun FlowCollector<Drawable>.emitDrawables(playbackStates: PlaybackStates) {
+ val animations = drawables.first()
+ val stateChanged =
+ playbackStates.isPreviousActive != null &&
+ playbackStates.isPreviousActive != playbackStates.isCurrentActive
+ if (playbackStates.isCurrentActive) {
+ if (stateChanged) {
+ emit(animations.start)
+ }
+ emit(animations.playing)
+ } else {
+ if (stateChanged) {
+ emit(animations.stop)
+ }
+ emit(animations.idle)
+ }
+ }
fun onButtonClicked() {
interactor.onButtonClicked()
}
- @VolumeDialogScope
- @AssistedFactory
- interface Factory {
+ private data class PlaybackStates(val isPreviousActive: Boolean?, val isCurrentActive: Boolean)
- fun create(): VolumeDialogSettingsButtonViewModel
+ private data class Drawables(
+ val start: LottieDrawable,
+ val playing: LottieDrawable,
+ val stop: LottieDrawable,
+ val idle: Drawable,
+ )
+}
+
+private fun LottieComposition.toDrawable(setup: LottieDrawable.() -> Unit = {}): LottieDrawable =
+ LottieDrawable().also { drawable ->
+ drawable.composition = this
+ drawable.setup()
}
+
+/** Suspends until current loop of the repeating animation is finished */
+private suspend fun LottieDrawable.awaitFinish() = suspendCancellableCoroutine { continuation ->
+ if (!isRunning) {
+ continuation.resume(Unit)
+ return@suspendCancellableCoroutine
+ }
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationRepeat(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+ }
+ addAnimatorListener(listener)
+ continuation.invokeOnCancellation { removeAnimatorListener(listener) }
+}
+
+/**
+ * Overrides colors of the [LottieDrawable] to a specified [color]
+ *
+ * @see com.airbnb.lottie.LottieAnimationView
+ */
+private fun LottieDrawable.setColor(@ColorInt color: Int) {
+ val callback = LottieValueCallback<ColorFilter>(SimpleColorFilter(color))
+ addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER, callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index f305246..faf06b94 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -26,7 +26,7 @@
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
-import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
import javax.inject.Inject
@@ -84,5 +84,5 @@
interpolator = DecelerateInterpolator()
addListener(jankListener)
}
- .awaitAnimation<Float> { value = it }
+ .suspendAnimate<Float> { value = it }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
index 10cf615..5f12480 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
@@ -22,6 +22,7 @@
import android.view.ViewPropertyAnimator
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringAnimation
+import com.airbnb.lottie.LottieDrawable
import kotlin.coroutines.resume
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -66,7 +67,7 @@
* is cancelled.
*/
@Suppress("UNCHECKED_CAST")
-suspend fun <T> ValueAnimator.awaitAnimation(onValueChanged: (T) -> Unit) {
+suspend fun <T> ValueAnimator.suspendAnimate(onValueChanged: (T) -> Unit) {
suspendCancellableCoroutine { continuation ->
addListener(
object : AnimatorListenerAdapter() {
@@ -103,6 +104,29 @@
}
}
+/**
+ * Starts the animation and suspends until it's finished. Cancels the animation if the running
+ * coroutine is cancelled.
+ */
+suspend fun LottieDrawable.suspendAnimate() = suspendCancellableCoroutine { continuation ->
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+ }
+ addAnimatorListener(listener)
+ start()
+ continuation.invokeOnCancellation {
+ removeAnimatorListener(listener)
+ stop()
+ }
+}
+
private fun <T> CancellableContinuation<T>.resumeIfCan(value: T) {
if (!isCancelled && !isCompleted) {
resume(value)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index 6e1ebc8..12e624ca 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -19,10 +19,10 @@
import android.media.session.MediaController
import android.media.session.PlaybackState
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -38,7 +38,7 @@
/** Allows to observe and change [MediaDeviceSession] state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaDeviceSessionInteractor
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index b3848a6..2973e11 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -24,12 +24,13 @@
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.shared.model.Result
import com.android.systemui.volume.panel.shared.model.filterData
import com.android.systemui.volume.panel.shared.model.wrapInResult
@@ -54,13 +55,13 @@
/** Provides observable models about the current media session state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaOutputInteractor
@Inject
constructor(
private val localMediaRepositoryFactory: LocalMediaRepositoryFactory,
private val packageManager: PackageManager,
- @VolumePanelScope private val coroutineScope: CoroutineScope,
+ @Application private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
mediaControllerRepository: MediaControllerRepository,
private val mediaControllerInteractor: MediaControllerInteractor,
@@ -77,7 +78,7 @@
.onStart { emit(activeSessions) }
}
.map { getMediaControllers(it) }
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaControllers(null, null))
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), MediaControllers(null, null))
/** [MediaDeviceSessions] that contains currently active sessions. */
val activeMediaDeviceSessions: Flow<MediaDeviceSessions> =
@@ -89,7 +90,11 @@
)
}
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSessions(null, null))
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ MediaDeviceSessions(null, null),
+ )
/** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */
val defaultActiveMediaSession: StateFlow<Result<MediaDeviceSession?>> =
@@ -104,7 +109,7 @@
}
.wrapInResult()
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), Result.Loading())
private val localMediaRepository: Flow<LocalMediaRepository> =
defaultActiveMediaSession
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index a41725f..4abbbac 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -25,7 +25,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_INACTIVE
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.Flags
@@ -299,20 +298,6 @@
}
@Test
- @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun keyguardCallback_visibilityChanged_clockDozeCalled() =
- runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
-
- captor.value.onKeyguardVisibilityChanged(true)
- verify(animations, never()).doze(0f)
-
- captor.value.onKeyguardVisibilityChanged(false)
- verify(animations, times(2)).doze(0f)
- }
-
- @Test
fun keyguardCallback_timeFormat_clockNotified() =
runBlocking(IMMEDIATE) {
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
@@ -344,19 +329,6 @@
}
@Test
- fun keyguardCallback_verifyKeyguardChanged() =
- runBlocking(IMMEDIATE) {
- val job = underTest.listenForDozeAmount(this)
- repository.setDozeAmount(0.4f)
-
- yield()
-
- verify(animations, times(2)).doze(0.4f)
-
- job.cancel()
- }
-
- @Test
fun listenForDozeAmountTransition_updatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index fb0fd23..6bfd080 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -34,8 +34,10 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.model.SysUiState
import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.shadeDialogContextInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
@@ -82,7 +84,7 @@
private val uiProperties =
BluetoothTileDialogViewModel.UiProperties.build(
isBluetoothEnabled = ENABLED,
- isAutoOnToggleFeatureAvailable = ENABLED
+ isAutoOnToggleFeatureAvailable = ENABLED,
)
@Mock private lateinit var sysuiDialogFactory: SystemUIDialog.Factory
@Mock private lateinit var dialogManager: SystemUIDialogManager
@@ -98,6 +100,8 @@
private lateinit var mBluetoothTileDialogDelegate: BluetoothTileDialogDelegate
private lateinit var deviceItem: DeviceItem
+ private val kosmos = testKosmos()
+
@Before
fun setUp() {
scheduler = TestCoroutineScheduler()
@@ -116,10 +120,16 @@
fakeSystemClock,
uiEventLogger,
logger,
- sysuiDialogFactory
+ sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
- whenever(sysuiDialogFactory.create(any(SystemUIDialog.Delegate::class.java))).thenAnswer {
+ whenever(
+ sysuiDialogFactory.create(
+ any(SystemUIDialog.Delegate::class.java),
+ any()
+ )
+ ).thenAnswer {
SystemUIDialog(
mContext,
0,
@@ -128,7 +138,7 @@
sysuiState,
fakeBroadcastDispatcher,
dialogTransitionAnimator,
- it.getArgument(0)
+ it.getArgument(0),
)
}
@@ -140,7 +150,7 @@
deviceName = DEVICE_NAME,
connectionSummary = DEVICE_CONNECTION_SUMMARY,
iconWithDescription = icon,
- background = null
+ background = null,
)
`when`(cachedBluetoothDevice.isBusy).thenReturn(false)
}
@@ -169,7 +179,7 @@
dialog,
listOf(deviceItem),
showSeeAll = false,
- showPairNewDevice = false
+ showPairNewDevice = false,
)
val recyclerView = dialog.requireViewById<RecyclerView>(R.id.device_list)
@@ -217,6 +227,7 @@
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.Adapter(bluetoothTileDialogCallback)
.DeviceItemViewHolder(view)
@@ -238,7 +249,7 @@
dialog,
listOf(deviceItem),
showSeeAll = false,
- showPairNewDevice = true
+ showPairNewDevice = true,
)
val seeAllButton = dialog.requireViewById<View>(R.id.see_all_button)
@@ -272,6 +283,7 @@
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.createDialog()
dialog.show()
@@ -295,6 +307,7 @@
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.createDialog()
dialog.show()
@@ -318,6 +331,7 @@
uiEventLogger,
logger,
sysuiDialogFactory,
+ kosmos.shadeDialogContextInteractor,
)
.createDialog()
dialog.show()
@@ -339,7 +353,7 @@
dialog,
visibility = VISIBLE,
label = null,
- isActive = true
+ isActive = true,
)
val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
@@ -361,7 +375,7 @@
dialog,
visibility = VISIBLE,
label = null,
- isActive = false
+ isActive = false,
)
val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
@@ -383,7 +397,7 @@
dialog,
visibility = GONE,
label = null,
- isActive = false
+ isActive = false,
)
val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch
rename to packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index c2c94a8..1cabf20 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -77,6 +77,8 @@
INITIATION_WIDTH,
mKosmos.getCommunalInteractor(),
mKosmos.getConfigurationInteractor(),
+ mKosmos.getSceneInteractor(),
+ Optional.of(mKosmos.getMockWindowRootViewProvider()),
mLifecycle
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index 1184a76..eb19a9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -81,7 +81,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@FlakyTest(
bugId = 292574995,
- detail = "on certain architectures all permutations with startActivity=true is causing failures"
+ detail = "on certain architectures all permutations with startActivity=true is causing failures",
)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@@ -93,11 +93,7 @@
private val DRAWABLE =
mock<Icon> {
whenever(this.contentDescription)
- .thenReturn(
- ContentDescription.Resource(
- res = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- )
+ .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
}
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
@@ -273,13 +269,7 @@
context = context,
userFileManager =
mock<UserFileManager>().apply {
- whenever(
- getSharedPreferences(
- anyString(),
- anyInt(),
- anyInt(),
- )
- )
+ whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
@@ -316,9 +306,7 @@
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractorFactory.create(
- featureFlags = featureFlags,
- )
+ KeyguardInteractorFactory.create(featureFlags = featureFlags)
.keyguardInteractor,
shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
@@ -350,9 +338,7 @@
homeControls.setState(
lockScreenState =
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon = DRAWABLE,
- )
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE)
)
homeControls.onTriggeredResult =
if (startActivity) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index b26f0a6..782b248 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -557,6 +557,13 @@
}
@Test
+ public void startActivityForDialog_always_startActivityWithoutDismissShade() {
+ mInternetDialogController.startActivityForDialog(mock(Intent.class));
+
+ verify(mActivityStarter).startActivity(any(Intent.class), eq(false) /* dismissShade */);
+ }
+
+ @Test
public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() {
mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index 300c9b8..8560b67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -149,7 +150,8 @@
mHandler,
mBgExecutor,
mKeyguard,
- mSystemUIDialogFactory);
+ mSystemUIDialogFactory,
+ new FakeShadeDialogContextInteractor(mContext));
mInternetDialogDelegate.createDialog();
mInternetDialogDelegate.onCreate(mSystemUIDialog, null);
mInternetDialogDelegate.mAdapter = mInternetAdapter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index a0ecb80..f695c13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -76,6 +76,8 @@
@Mock private lateinit var iActivityManager: IActivityManager
+ @Mock private lateinit var beforeUserSwitchingReply: IRemoteCallback
+
@Mock private lateinit var userSwitchingReply: IRemoteCallback
@Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
@@ -199,9 +201,10 @@
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
+ captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
captor.value.onUserSwitching(newID, userSwitchingReply)
runCurrent()
+ verify(beforeUserSwitchingReply).sendResult(any())
verify(userSwitchingReply).sendResult(any())
verify(userManager).getProfiles(newID)
@@ -341,10 +344,11 @@
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
+ captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
captor.value.onUserSwitching(newID, userSwitchingReply)
runCurrent()
+ verify(beforeUserSwitchingReply).sendResult(any())
verify(userSwitchingReply).sendResult(any())
assertThat(callback.calledOnUserChanging).isEqualTo(1)
assertThat(callback.lastUser).isEqualTo(newID)
@@ -395,7 +399,7 @@
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
+ captor.value.onBeforeUserSwitching(newID, any())
captor.value.onUserSwitchComplete(newID)
runCurrent()
@@ -453,8 +457,10 @@
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
captor.value.onUserSwitching(newID, userSwitchingReply)
runCurrent()
+ verify(beforeUserSwitchingReply).sendResult(any())
verify(userSwitchingReply).sendResult(any())
captor.value.onUserSwitchComplete(newID)
@@ -488,6 +494,7 @@
}
private class TestCallback : UserTracker.Callback {
+ var calledOnBeforeUserChanging = 0
var calledOnUserChanging = 0
var calledOnUserChanged = 0
var calledOnProfilesChanged = 0
@@ -495,6 +502,11 @@
var lastUserContext: Context? = null
var lastUserProfiles = emptyList<UserInfo>()
+ override fun onBeforeUserSwitching(newUser: Int) {
+ calledOnBeforeUserChanging++
+ lastUser = newUser
+ }
+
override fun onUserChanging(newUser: Int, userContext: Context) {
calledOnUserChanging++
lastUser = newUser
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 041d1a6..4b11e2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade
import android.content.Context
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.flag.junit.FlagsParameterization
@@ -32,7 +31,6 @@
import com.android.keyguard.KeyguardSecurityContainerController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
@@ -406,18 +404,6 @@
}
@Test
- @DisableSceneContainer
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun handleDispatchTouchEvent_nsslMigrationOff_userActivity_not_called() {
- underTest.setStatusBarViewController(phoneStatusBarViewController)
-
- interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
-
- verify(centralSurfaces, times(0)).userActivity()
- }
-
- @Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun handleDispatchTouchEvent_nsslMigrationOn_userActivity() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
@@ -438,7 +424,6 @@
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
@@ -451,7 +436,6 @@
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
@@ -464,7 +448,6 @@
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun shouldInterceptTouchEvent_dozingAndPulsing_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
@@ -609,7 +592,6 @@
}
@Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun cancelCurrentTouch_callsDragDownHelper() {
underTest.cancelCurrentTouch()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 13bc82f..a04ca03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade
-import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -27,7 +26,6 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
@@ -65,10 +63,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-/**
- * Uses Flags.KEYGUARD_STATUS_VIEW_MIGRATE_NSSL set to false. If all goes well, this set of tests
- * will be deleted.
- */
+/** NotificationsQSContainerController tests */
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -122,7 +117,7 @@
delayableExecutor,
notificationStackScrollLayoutController,
ResourcesSplitShadeStateController(),
- largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
+ largeScreenHeaderHelperLazy = { largeScreenHeaderHelper },
)
overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -209,23 +204,23 @@
given(
taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
given(
taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = STABLE_INSET_BOTTOM,
expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
}
@@ -237,22 +232,22 @@
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
given(
taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
- expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
}
@@ -263,22 +258,22 @@
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withCutout()
+ insets = windowInsets().withCutout(),
)
then(
expectedContainerPadding = CUTOUT_HEIGHT,
- expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
given(
taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withCutout().withStableBottom()
+ insets = windowInsets().withCutout().withStableBottom(),
)
then(
expectedContainerPadding = 0,
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
- expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET
+ expectedQsPadding = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN - QS_PADDING_OFFSET,
)
}
@@ -289,18 +284,18 @@
given(
taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM)
given(
taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = STABLE_INSET_BOTTOM,
- expectedQsPadding = STABLE_INSET_BOTTOM
+ expectedQsPadding = STABLE_INSET_BOTTOM,
)
}
@@ -314,19 +309,19 @@
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withCutout().withStableBottom()
+ insets = windowInsets().withCutout().withStableBottom(),
)
then(expectedContainerPadding = CUTOUT_HEIGHT, expectedQsPadding = STABLE_INSET_BOTTOM)
given(
taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(
expectedContainerPadding = 0,
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
- expectedQsPadding = STABLE_INSET_BOTTOM
+ expectedQsPadding = STABLE_INSET_BOTTOM,
)
}
@@ -339,7 +334,7 @@
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(expectedContainerPadding = 0, expectedNotificationsMargin = 0)
@@ -355,7 +350,7 @@
given(
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom()
+ insets = windowInsets().withStableBottom(),
)
then(expectedContainerPadding = 0)
@@ -376,43 +371,6 @@
}
@Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSplitShadeLayout_isAlignedToGuideline() {
- enableSplitShade()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd).isEqualTo(R.id.qs_edge_guideline)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart)
- .isEqualTo(R.id.qs_edge_guideline)
- }
-
- @Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSinglePaneLayout_childrenHaveEqualMargins() {
- disableSplitShade()
- underTest.updateResources()
- val qsStartMargin = getConstraintSetLayout(R.id.qs_frame).startMargin
- val qsEndMargin = getConstraintSetLayout(R.id.qs_frame).endMargin
- val notifStartMargin = getConstraintSetLayout(R.id.notification_stack_scroller).startMargin
- val notifEndMargin = getConstraintSetLayout(R.id.notification_stack_scroller).endMargin
- assertThat(
- qsStartMargin == qsEndMargin &&
- notifStartMargin == notifEndMargin &&
- qsStartMargin == notifStartMargin
- )
- .isTrue()
- }
-
- @Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
- enableSplitShade()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startMargin)
- .isEqualTo(0)
- }
-
- @Test
fun testSplitShadeLayout_qsFrameHasHorizontalMarginsOfZero() {
enableSplitShade()
underTest.updateResources()
@@ -421,37 +379,6 @@
}
@Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testLargeScreenLayout_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
- setLargeScreen()
- val largeScreenHeaderResourceHeight = 100
- val largeScreenHeaderHelperHeight = 200
- whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
- .thenReturn(largeScreenHeaderHelperHeight)
- overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderResourceHeight)
-
- // ensure the estimated height (would be 30 here) wouldn't impact this test case
- overrideResource(R.dimen.large_screen_shade_header_min_height, 10)
- overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 10)
-
- underTest.updateResources()
-
- assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
- .isEqualTo(largeScreenHeaderHelperHeight)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin)
- .isEqualTo(largeScreenHeaderHelperHeight)
- }
-
- @Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
- setSmallScreen()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin).isEqualTo(0)
- }
-
- @Test
fun testSinglePaneShadeLayout_qsFrameHasHorizontalMarginsSetToCorrectValue() {
disableSplitShade()
underTest.updateResources()
@@ -464,17 +391,6 @@
}
@Test
- @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun testSinglePaneShadeLayout_isAlignedToParent() {
- disableSplitShade()
- underTest.updateResources()
- assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
- .isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart)
- .isEqualTo(ConstraintSet.PARENT_ID)
- }
-
- @Test
fun testAllChildrenOfNotificationContainer_haveIds() {
// set dimen to 0 to avoid triggering updating bottom spacing
overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, 0)
@@ -493,7 +409,7 @@
delayableExecutor,
notificationStackScrollLayoutController,
ResourcesSplitShadeStateController(),
- largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }
+ largeScreenHeaderHelperLazy = { largeScreenHeaderHelper },
)
controller.updateConstraints()
@@ -509,7 +425,7 @@
taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = emptyInsets(),
- applyImmediately = false
+ applyImmediately = false,
)
fakeSystemClock.advanceTime(INSET_DEBOUNCE_MILLIS / 2)
windowInsetsCallback.accept(windowInsets().withStableBottom())
@@ -576,7 +492,7 @@
taskbarVisible: Boolean,
navigationMode: Int,
insets: WindowInsets,
- applyImmediately: Boolean = true
+ applyImmediately: Boolean = true,
) {
Mockito.clearInvocations(view)
taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false)
@@ -591,7 +507,7 @@
fun then(
expectedContainerPadding: Int,
expectedNotificationsMargin: Int = NOTIFICATIONS_MARGIN,
- expectedQsPadding: Int = 0
+ expectedQsPadding: Int = 0,
) {
verify(view).setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding))
verify(view).setNotificationsMarginBottom(expectedNotificationsMargin)
@@ -623,7 +539,7 @@
val layoutParams =
ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
// required as cloning ConstraintSet fails if view doesn't have layout params
view.layoutParams = layoutParams
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a91fb45..e1a8916 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -1544,14 +1544,6 @@
assertFalse(mStackScroller.mHeadsUpAnimatingAway);
}
- @Test
- @EnableSceneContainer
- public void finishExpanding_sceneContainerEnabled() {
- mStackScroller.startOverscrollAfterExpanding();
- verify(mStackScroller.getExpandHelper()).finishExpanding();
- assertTrue(mStackScroller.getIsBeingDragged());
- }
-
private MotionEvent captureTouchSentToSceneFramework() {
ArgumentCaptor<MotionEvent> captor = ArgumentCaptor.forClass(MotionEvent.class);
verify(mStackScrollLayoutController).sendTouchToSceneFramework(captor.capture());
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
index 76fc611..25d1c37 100644
--- a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -433,6 +433,8 @@
override fun showRearDisplayDialog(currentBaseState: Int) {}
+ override fun unbundleNotification(key: String) {}
+
companion object {
const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
const val SECONDARY_DISPLAY_ID = 2
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index ad92b31..bfc4248 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -86,12 +86,8 @@
}
suspend fun Kosmos.setCommunalV2Enabled(enabled: Boolean) {
- setCommunalV2ConfigEnabled(true)
- if (enabled) {
- fakeUserRepository.asMainUser()
- } else {
- fakeUserRepository.asDefaultUser()
- }
+ setCommunalV2ConfigEnabled(enabled)
+ setCommunalEnabled(enabled)
}
suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
index b407b1b..e3cfb80 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.communal.ui.viewmodel
import android.service.dream.dreamManager
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.policy.batteryController
val Kosmos.communalToDreamButtonViewModel by
@@ -26,6 +28,8 @@
CommunalToDreamButtonViewModel(
backgroundContext = testDispatcher,
batteryController = batteryController,
+ settingsInteractor = communalSettingsInteractor,
+ activityStarter = activityStarter,
dreamManager = dreamManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
index 534ded5..9012393 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
@@ -58,4 +58,26 @@
fun insert(instance: DisplayWindowProperties) {
properties.put(instance.displayId, instance.windowType, instance)
}
+
+ /** inserts an entry, mocking everything except the context. */
+ fun insertForContext(displayId: Int, windowType: Int, context: Context) {
+ properties.put(
+ displayId,
+ windowType,
+ DisplayWindowProperties(
+ displayId = displayId,
+ windowType = windowType,
+ context = context,
+ windowManager = mock(),
+ layoutInflater = mock(),
+ ),
+ )
+ }
+
+ /** Whether the repository contains an entry already. */
+ fun contains(displayId: Int, windowType: Int): Boolean =
+ properties.contains(displayId, windowType)
+
+ /** Removes all the entries. */
+ fun clear() = properties.clear()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index d941fb0..4383560 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -17,6 +17,7 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.mockito.kotlin.verify
var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
@@ -82,6 +83,32 @@
}
/** Retrieve the current value of this [StateFlow] safely. See `currentValue(TestScope)`. */
+fun <T> Kosmos.currentValue(fn: () -> T) = testScope.currentValue(fn)
+
+/**
+ * Retrieve the result of [fn] after running all pending tasks. Do not use to retrieve the value of
+ * a flow directly; for that, use either `currentValue(StateFlow)` or [collectLastValue]
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T> TestScope.currentValue(fn: () -> T): T {
+ runCurrent()
+ return fn()
+}
+
+/** Retrieve the result of [fn] after running all pending tasks. See `TestScope.currentValue(fn)` */
fun <T> Kosmos.currentValue(stateFlow: StateFlow<T>): T {
return testScope.currentValue(stateFlow)
}
+
+/** Safely verify that a mock has been called after the test scope has caught up */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T> TestScope.verifyCurrent(mock: T): T {
+ runCurrent()
+ return verify(mock)
+}
+
+/**
+ * Safely verify that a mock has been called after the test scope has caught up. See
+ * `TestScope.verifyCurrent`
+ */
+fun <T> Kosmos.verifyCurrent(mock: T) = testScope.verifyCurrent(mock)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 41cfcea..39f1ad4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -63,6 +63,7 @@
import com.android.systemui.scene.domain.startable.scrimStartable
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.shared.model.sceneDataSource
+import com.android.systemui.scene.ui.view.mockWindowRootViewProvider
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -191,4 +192,5 @@
}
val disableFlagsInteractor by lazy { kosmos.disableFlagsInteractor }
val fakeDisableFlagsRepository by lazy { kosmos.fakeDisableFlagsRepository }
+ val mockWindowRootViewProvider by lazy { kosmos.mockWindowRootViewProvider }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
index f52572a..f5eebb4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
@@ -19,13 +19,13 @@
import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
+import com.android.systemui.kosmos.currentValue
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.TestScope
-class FakeSceneDataSource(
- initialSceneKey: SceneKey,
-) : SceneDataSource {
+class FakeSceneDataSource(initialSceneKey: SceneKey, val testScope: TestScope) : SceneDataSource {
private val _currentScene = MutableStateFlow(initialSceneKey)
override val currentScene: StateFlow<SceneKey> = _currentScene.asStateFlow()
@@ -33,18 +33,20 @@
private val _currentOverlays = MutableStateFlow<Set<OverlayKey>>(emptySet())
override val currentOverlays: StateFlow<Set<OverlayKey>> = _currentOverlays.asStateFlow()
- var isPaused = false
- private set
+ private var _isPaused = false
+ val isPaused
+ get() = testScope.currentValue { _isPaused }
- var pendingScene: SceneKey? = null
- private set
+ private var _pendingScene: SceneKey? = null
+ val pendingScene
+ get() = testScope.currentValue { _pendingScene }
var pendingOverlays: Set<OverlayKey>? = null
private set
override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
- if (isPaused) {
- pendingScene = toScene
+ if (_isPaused) {
+ _pendingScene = toScene
} else {
_currentScene.value = toScene
}
@@ -55,7 +57,7 @@
}
override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
- if (isPaused) {
+ if (_isPaused) {
pendingOverlays = (pendingOverlays ?: currentOverlays.value) + overlay
} else {
_currentOverlays.value += overlay
@@ -63,7 +65,7 @@
}
override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
- if (isPaused) {
+ if (_isPaused) {
pendingOverlays = (pendingOverlays ?: currentOverlays.value) - overlay
} else {
_currentOverlays.value -= overlay
@@ -82,9 +84,9 @@
* last one will be remembered.
*/
fun pause() {
- check(!isPaused) { "Can't pause what's already paused!" }
+ check(!_isPaused) { "Can't pause what's already paused!" }
- isPaused = true
+ _isPaused = true
}
/**
@@ -100,15 +102,12 @@
*
* If [expectedScene] is provided, will assert that it's indeed the latest called.
*/
- fun unpause(
- force: Boolean = false,
- expectedScene: SceneKey? = null,
- ) {
- check(force || isPaused) { "Can't unpause what's already not paused!" }
+ fun unpause(force: Boolean = false, expectedScene: SceneKey? = null) {
+ check(force || _isPaused) { "Can't unpause what's already not paused!" }
- isPaused = false
- pendingScene?.let { _currentScene.value = it }
- pendingScene = null
+ _isPaused = false
+ _pendingScene?.let { _currentScene.value = it }
+ _pendingScene = null
pendingOverlays?.let { _currentOverlays.value = it }
pendingOverlays = null
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt
index f519686..7eebfc3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneDataSourceKosmos.kt
@@ -19,13 +19,12 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.initialSceneKey
import com.android.systemui.scene.sceneContainerConfig
val Kosmos.fakeSceneDataSource by Fixture {
- FakeSceneDataSource(
- initialSceneKey = initialSceneKey,
- )
+ FakeSceneDataSource(initialSceneKey = initialSceneKey, testScope = testScope)
}
val Kosmos.sceneDataSourceDelegator by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt
index 5c91dc8..e6ba9a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/WindowRootViewKosmos.kt
@@ -17,6 +17,9 @@
package com.android.systemui.scene.ui.view
import com.android.systemui.kosmos.Kosmos
+import javax.inject.Provider
import org.mockito.kotlin.mock
val Kosmos.mockShadeRootView by Kosmos.Fixture { mock<WindowRootView>() }
+val Kosmos.mockWindowRootViewProvider by
+ Kosmos.Fixture { Provider<WindowRootView> { mock<WindowRootView>() } }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt
index 4dcd220..3ed7302 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeAnimationRepositoryKosmos.kt
@@ -16,6 +16,14 @@
package com.android.systemui.shade.data.repository
+import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
val Kosmos.shadeAnimationRepository by Kosmos.Fixture { ShadeAnimationRepository() }
+val Kosmos.shadeDialogContextInteractor by
+ Kosmos.Fixture {
+ mock<ShadeDialogContextInteractor> { on { context } doReturn applicationContext }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
new file mode 100644
index 0000000..680e0de
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted
+
+import android.app.Notification
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import org.junit.Assert
+
+class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtractor {
+ @JvmField
+ val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModel?>()
+ @JvmField val extractCalls = mutableListOf<Pair<NotificationEntry, Notification.Builder>>()
+
+ override fun extractContent(
+ entry: NotificationEntry,
+ recoveredBuilder: Notification.Builder,
+ ): PromotedNotificationContentModel? {
+ extractCalls.add(entry to recoveredBuilder)
+
+ if (contentForEntry.isEmpty()) {
+ // If *no* entries are set, just return null for everything.
+ return null
+ } else {
+ // If entries *are* set, fail on unexpected ones.
+ Assert.assertTrue(contentForEntry.containsKey(entry))
+ return contentForEntry.get(entry)
+ }
+ }
+
+ fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModel?) {
+ contentForEntry.clear()
+ contentForEntry.put(entry, content)
+ extractCalls.clear()
+ }
+
+ fun verifyZeroExtractCalls() {
+ Assert.assertTrue(extractCalls.isEmpty())
+ }
+
+ fun verifyOneExtractCall() {
+ Assert.assertEquals(1, extractCalls.size)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
index 88caf6e..ea7b41d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
@@ -17,11 +17,20 @@
package com.android.systemui.statusbar.notification.promoted
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import org.junit.Assert
class FakePromotedNotificationsProvider : PromotedNotificationsProvider {
val promotedEntries = mutableSetOf<NotificationEntry>()
+ val shouldPromoteForEntry = mutableMapOf<NotificationEntry, Boolean>()
override fun shouldPromote(entry: NotificationEntry): Boolean {
- return promotedEntries.contains(entry)
+ if (shouldPromoteForEntry.isEmpty()) {
+ // If *no* entries are set, just return false for everything.
+ return false
+ } else {
+ // If entries *are* set, fail on unexpected ones.
+ Assert.assertTrue(shouldPromoteForEntry.containsKey(entry))
+ return shouldPromoteForEntry[entry] ?: false
+ }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
index 5e9f12b..52c17c82f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
@@ -21,7 +21,7 @@
var Kosmos.promotedNotificationContentExtractor by
Kosmos.Fixture {
- PromotedNotificationContentExtractor(
+ PromotedNotificationContentExtractorImpl(
promotedNotificationsProvider,
applicationContext,
promotedNotificationLogger,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 7126933..e739e82 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -65,7 +65,7 @@
import com.android.systemui.statusbar.notification.icon.IconBuilder
import com.android.systemui.statusbar.notification.icon.IconManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProviderImpl
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.CoordinateOnClickListener
@@ -222,16 +222,11 @@
Mockito.mock(LauncherApps::class.java, STUB_ONLY),
Mockito.mock(ConversationNotificationManager::class.java, STUB_ONLY),
)
-
- val promotedNotificationsProvider = PromotedNotificationsProviderImpl()
- val promotedNotificationLog = logcatLogBuffer("PromotedNotifLog")
- val promotedNotificationLogger = PromotedNotificationLogger(promotedNotificationLog)
-
val promotedNotificationContentExtractor =
- PromotedNotificationContentExtractor(
- promotedNotificationsProvider,
+ PromotedNotificationContentExtractorImpl(
+ PromotedNotificationsProviderImpl(),
context,
- promotedNotificationLogger,
+ PromotedNotificationLogger(logcatLogBuffer("PromotedNotifLog")),
)
mContentBinder =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
index 932e768..6c98d19 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.mainCoroutineContext
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shade.data.repository.shadeDialogContextInteractor
import com.android.systemui.statusbar.phone.systemUIDialogFactory
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
import com.android.systemui.util.mockito.mock
@@ -35,5 +36,6 @@
{ modesDialogViewModel },
modesDialogEventLogger,
mainCoroutineContext,
+ shadeDialogContextInteractor,
)
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 3025e2e..549f8fa 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -217,6 +217,13 @@
+ mPowerManagerWakeLock.getTag()));
return;
}
+
+ if (!mPowerManagerWakeLock.isHeld()) {
+ Slog.w(TAG, addUserIdToLogMessage(mUserId,
+ "Wakelock not held: " + mPowerManagerWakeLock.getTag()));
+ return;
+ }
+
mPowerManagerWakeLock.release();
Slog.v(
TAG,
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index fd18fa8..abfb826 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -55,6 +55,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
+import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -102,6 +103,7 @@
private final PackageManagerInternal mPackageManager;
private final WindowManagerInternal mWmInternal;
private final DevicePolicyManagerInternal mDpmInternal;
+ private final AudioManager mAudioManager;
private final Object mLock = new Object();
private final AssistDataRequester mAssistDataRequester;
@@ -163,6 +165,8 @@
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
mPackageManager = LocalServices.getService(PackageManagerInternal.class);
+ mAudioManager = context.getSystemService(AudioManager.class);
+
mWmInternal = Objects.requireNonNull(LocalServices.getService(WindowManagerInternal.class));
mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mAssistDataRequester = new AssistDataRequester(
@@ -306,6 +310,10 @@
SystemClock.uptimeMillis());
launchIntent.putExtra(ContextualSearchManager.EXTRA_ENTRYPOINT, entrypoint);
launchIntent.putExtra(ContextualSearchManager.EXTRA_TOKEN, mToken);
+ if (Flags.includeAudioPlayingStatus()) {
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_AUDIO_PLAYING,
+ mAudioManager.isMusicActive());
+ }
boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
final List<IBinder> activityTokens = new ArrayList<>(records.size());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a184e90..50b6990 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19396,9 +19396,6 @@
creatorPackage);
if (creatorToken != null) {
extraIntent.setCreatorToken(creatorToken);
- // TODO remove Slog.wtf once proven FrameworkStatsLog works. b/375396329
- Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
- + creatorPackage + "; intent: " + extraIntent);
FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, false);
}
});
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 87f87c7..c82933c 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -158,6 +158,7 @@
"aoc",
"app_widgets",
"arc_next",
+ "art_cloud",
"art_mainline",
"art_performance",
"attack_tools",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c31b9ef..70f2a8e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -160,6 +160,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -176,6 +177,9 @@
class UserController implements Handler.Callback {
private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
+ // Amount of time we wait for observers to handle onBeforeUserSwitching, before crashing system.
+ static final int DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS = 20 * 1000;
+
// Amount of time we wait for observers to handle a user switch before
// giving up on them and dismissing the user switching dialog.
static final int DEFAULT_USER_SWITCH_TIMEOUT_MS = 3 * 1000;
@@ -1920,8 +1924,14 @@
return false;
}
- mHandler.post(() -> startUserInternalOnHandler(userId, oldUserId, userStartMode,
- unlockListener, callingUid, callingPid));
+ final Runnable continueStartUserInternal = () -> continueStartUserInternal(userInfo,
+ oldUserId, userStartMode, unlockListener, callingUid, callingPid);
+ if (foreground) {
+ mHandler.post(() -> dispatchOnBeforeUserSwitching(userId, () ->
+ mHandler.post(continueStartUserInternal)));
+ } else {
+ continueStartUserInternal.run();
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1929,11 +1939,11 @@
return true;
}
- private void startUserInternalOnHandler(int userId, int oldUserId, int userStartMode,
+ private void continueStartUserInternal(UserInfo userInfo, int oldUserId, int userStartMode,
IProgressListener unlockListener, int callingUid, int callingPid) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
final boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
- final UserInfo userInfo = getUserInfo(userId);
+ final int userId = userInfo.id;
boolean needStart = false;
boolean updateUmState = false;
@@ -1995,7 +2005,6 @@
// it should be moved outside, but for now it's not as there are many calls to
// external components here afterwards
updateProfileRelatedCaches();
- dispatchOnBeforeUserSwitching(userId);
mInjector.getWindowManager().setCurrentUser(userId);
mInjector.reportCurWakefulnessUsageEvent();
// Once the internal notion of the active user has switched, we lock the device
@@ -2296,25 +2305,40 @@
mUserSwitchObservers.finishBroadcast();
}
- private void dispatchOnBeforeUserSwitching(@UserIdInt int newUserId) {
+ private void dispatchOnBeforeUserSwitching(@UserIdInt int newUserId, Runnable onComplete) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("dispatchOnBeforeUserSwitching-" + newUserId);
- final int observerCount = mUserSwitchObservers.beginBroadcast();
- for (int i = 0; i < observerCount; i++) {
- final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
- t.traceBegin("onBeforeUserSwitching-" + name);
+ final AtomicBoolean isSuccessful = new AtomicBoolean(false);
+ startTimeoutForOnBeforeUserSwitching(isSuccessful);
+ informUserSwitchObservers((observer, callback) -> {
try {
- mUserSwitchObservers.getBroadcastItem(i).onBeforeUserSwitching(newUserId);
+ observer.onBeforeUserSwitching(newUserId, callback);
} catch (RemoteException e) {
- // Ignore
- } finally {
- t.traceEnd();
+ // ignore
}
- }
- mUserSwitchObservers.finishBroadcast();
+ }, () -> {
+ isSuccessful.set(true);
+ onComplete.run();
+ }, "onBeforeUserSwitching");
t.traceEnd();
}
+ private void startTimeoutForOnBeforeUserSwitching(AtomicBoolean isSuccessful) {
+ mHandler.postDelayed(() -> {
+ if (isSuccessful.get()) {
+ return;
+ }
+ String unresponsiveObservers;
+ synchronized (mLock) {
+ unresponsiveObservers = String.join(", ", mCurWaitingUserSwitchCallbacks);
+ }
+ throw new RuntimeException("Timeout on dispatchOnBeforeUserSwitching. "
+ + "These UserSwitchObservers did not respond in "
+ + DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS + "ms: " + unresponsiveObservers + ".");
+ }, DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS);
+ }
+
+
/** Called on handler thread */
@VisibleForTesting
void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) {
@@ -2527,70 +2551,76 @@
t.traceBegin("dispatchUserSwitch-" + oldUserId + "-to-" + newUserId);
EventLog.writeEvent(EventLogTags.UC_DISPATCH_USER_SWITCH, oldUserId, newUserId);
-
- final int observerCount = mUserSwitchObservers.beginBroadcast();
- if (observerCount > 0) {
- final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
- synchronized (mLock) {
- uss.switching = true;
- mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
+ uss.switching = true;
+ informUserSwitchObservers((observer, callback) -> {
+ try {
+ observer.onUserSwitching(newUserId, callback);
+ } catch (RemoteException e) {
+ // ignore
}
- final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
- final long userSwitchTimeoutMs = getUserSwitchTimeoutMs();
- final long dispatchStartedTime = SystemClock.elapsedRealtime();
- for (int i = 0; i < observerCount; i++) {
- final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
- try {
- // Prepend with unique prefix to guarantee that keys are unique
- final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
- synchronized (mLock) {
- curWaitingUserSwitchCallbacks.add(name);
- }
- final IRemoteCallback callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- asyncTraceEnd("onUserSwitching-" + name, newUserId);
- synchronized (mLock) {
- long delayForObserver = SystemClock.elapsedRealtime()
- - dispatchStartedTimeForObserver;
- if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
- Slogf.w(TAG, "User switch slowed down by observer " + name
- + ": result took " + delayForObserver
- + " ms to process.");
- }
-
- long totalDelay = SystemClock.elapsedRealtime()
- - dispatchStartedTime;
- if (totalDelay > userSwitchTimeoutMs) {
- Slogf.e(TAG, "User switch timeout: observer " + name
- + "'s result was received " + totalDelay
- + " ms after dispatchUserSwitch.");
- }
-
- curWaitingUserSwitchCallbacks.remove(name);
- // Continue switching if all callbacks have been notified and
- // user switching session is still valid
- if (waitingCallbacksCount.decrementAndGet() == 0
- && (curWaitingUserSwitchCallbacks
- == mCurWaitingUserSwitchCallbacks)) {
- sendContinueUserSwitchLU(uss, oldUserId, newUserId);
- }
- }
- }
- };
- asyncTraceBegin("onUserSwitching-" + name, newUserId);
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
- } catch (RemoteException e) {
- // Ignore
- }
- }
- } else {
+ }, () -> {
synchronized (mLock) {
sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
+ }, "onUserSwitching");
+ t.traceEnd();
+ }
+
+ void informUserSwitchObservers(BiConsumer<IUserSwitchObserver, IRemoteCallback> consumer,
+ final Runnable onComplete, String trace) {
+ final int observerCount = mUserSwitchObservers.beginBroadcast();
+ if (observerCount == 0) {
+ onComplete.run();
+ mUserSwitchObservers.finishBroadcast();
+ return;
+ }
+ final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
+ synchronized (mLock) {
+ mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
+ }
+ final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
+ final long userSwitchTimeoutMs = getUserSwitchTimeoutMs();
+ final long dispatchStartedTime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < observerCount; i++) {
+ final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
+ // Prepend with unique prefix to guarantee that keys are unique
+ final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
+ synchronized (mLock) {
+ curWaitingUserSwitchCallbacks.add(name);
+ }
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ asyncTraceEnd(trace + "-" + name, 0);
+ synchronized (mLock) {
+ long delayForObserver = SystemClock.elapsedRealtime()
+ - dispatchStartedTimeForObserver;
+ if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
+ Slogf.w(TAG, "User switch slowed down by observer " + name
+ + ": result took " + delayForObserver
+ + " ms to process. " + trace);
+ }
+ long totalDelay = SystemClock.elapsedRealtime() - dispatchStartedTime;
+ if (totalDelay > userSwitchTimeoutMs) {
+ Slogf.e(TAG, "User switch timeout: observer " + name
+ + "'s result was received " + totalDelay
+ + " ms after dispatchUserSwitch. " + trace);
+ }
+ curWaitingUserSwitchCallbacks.remove(name);
+ // Continue switching if all callbacks have been notified and
+ // user switching session is still valid
+ if (waitingCallbacksCount.decrementAndGet() == 0
+ && (curWaitingUserSwitchCallbacks
+ == mCurWaitingUserSwitchCallbacks)) {
+ onComplete.run();
+ }
+ }
+ }
+ };
+ asyncTraceBegin(trace + "-" + name, 0);
+ consumer.accept(mUserSwitchObservers.getBroadcastItem(i), callback);
}
mUserSwitchObservers.finishBroadcast();
- t.traceEnd(); // end dispatchUserSwitch-
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index bad5b8b..5740e16 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2397,9 +2397,13 @@
// We don't bother invalidating the display info caches here because any changes to the
// display info will trigger a cache invalidation inside of LogicalDisplay before we hit
// this point.
- sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
applyDisplayChangedLocked(display);
+
+ if (mDisplayTopologyCoordinator != null) {
+ mDisplayTopologyCoordinator.onDisplayChanged(display.getDisplayInfoLocked());
+ }
}
private void applyDisplayChangedLocked(@NonNull LogicalDisplay display) {
@@ -2643,7 +2647,8 @@
private void updateCanHostTasksIfNeededLocked(LogicalDisplay display) {
if (display.setCanHostTasksLocked(!mMirrorBuiltInDisplay)) {
- sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ sendDisplayEventIfEnabledLocked(display,
+ DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
}
}
@@ -3474,7 +3479,7 @@
private void sendDisplayEventFrameRateOverrideLocked(int displayId) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
- displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
mHandler.sendMessage(msg);
}
@@ -4061,7 +4066,7 @@
handleLogicalDisplayAddedLocked(display);
break;
- case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED:
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_BASIC_CHANGED:
handleLogicalDisplayChangedLocked(display);
break;
@@ -4286,8 +4291,9 @@
switch (event) {
case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED) != 0;
- case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
- return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED) != 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED:
+ return (mask & DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED)
+ != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
return (mask
& DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED)
@@ -4542,7 +4548,8 @@
public void registerCallback(IDisplayManagerCallback callback) {
registerCallbackWithEventMask(callback,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED);
}
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 5b78726..461a9f3 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -85,13 +85,26 @@
}
/**
+ * Update the topology with display changes.
+ * @param info The new display info
+ */
+ void onDisplayChanged(DisplayInfo info) {
+ synchronized (mSyncRoot) {
+ if (mTopology.updateDisplay(info.displayId, getWidth(info), getHeight(info))) {
+ sendTopologyUpdateLocked();
+ }
+ }
+ }
+
+ /**
* Remove a display from the topology.
* @param displayId The logical display ID
*/
void onDisplayRemoved(int displayId) {
synchronized (mSyncRoot) {
- mTopology.removeDisplay(displayId);
- sendTopologyUpdateLocked();
+ if (mTopology.removeDisplay(displayId)) {
+ sendTopologyUpdateLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 79592a65..0069215 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -81,7 +81,7 @@
public static final int LOGICAL_DISPLAY_EVENT_BASE = 0;
public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1 << 0;
- public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 1 << 1;
+ public static final int LOGICAL_DISPLAY_EVENT_BASIC_CHANGED = 1 << 1;
public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 1 << 2;
public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 1 << 3;
public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 1 << 4;
@@ -172,9 +172,7 @@
/**
* Has an entry for every logical display that the rest of the system has been notified about.
- * Any entry in here requires us to send a {@link LOGICAL_DISPLAY_EVENT_REMOVED} event when it
- * is deleted or {@link LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed. The values are any
- * of the {@code UPDATE_STATE_*} constant types.
+ * The values are any of the {@code UPDATE_STATE_*} constant types.
*/
private final SparseIntArray mUpdatedLogicalDisplays = new SparseIntArray();
@@ -811,7 +809,8 @@
final boolean isCurrentlyEnabled = display.isEnabledLocked();
int logicalDisplayEventMask = mLogicalDisplaysToUpdate
.get(displayId, LOGICAL_DISPLAY_EVENT_BASE);
-
+ boolean hasBasicInfoChanged =
+ !mTempDisplayInfo.equals(newDisplayInfo, /* compareRefreshRate */ false);
// The display is no longer valid and needs to be removed.
if (!display.isValidLocked()) {
// Remove from group
@@ -863,19 +862,28 @@
int event = isCurrentlyEnabled ? LOGICAL_DISPLAY_EVENT_ADDED :
LOGICAL_DISPLAY_EVENT_REMOVED;
logicalDisplayEventMask |= event;
- } else if (wasDirty || !mTempDisplayInfo.equals(newDisplayInfo)) {
+ } else if (wasDirty) {
// If only the hdr/sdr ratio changed, then send just the event for that case
if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED;
} else {
- logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CHANGED;
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED
+ | LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED
+ | LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
}
+ } else if (hasBasicInfoChanged
+ || mTempDisplayInfo.getRefreshRate() != newDisplayInfo.getRefreshRate()) {
+ // If only the hdr/sdr ratio changed, then send just the event for that case
+ if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED;
+ } else {
- if (mFlags.isDisplayListenerPerformanceImprovementsEnabled()) {
+ if (hasBasicInfoChanged) {
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED;
+ }
logicalDisplayEventMask
|= updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo);
}
-
// The display is involved in a display layout transition
} else if (updateState == UPDATE_STATE_TRANSITION) {
logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION;
@@ -891,7 +899,8 @@
// things like display cutouts.
display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
- logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CHANGED;
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED
+ | LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
}
}
mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
@@ -930,7 +939,7 @@
if (mFlags.isConnectedDisplayManagementEnabled()) {
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DISCONNECTED);
}
- sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_BASIC_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_STATE_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
@@ -962,7 +971,8 @@
mask |= LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
}
- if (mTempDisplayInfo.state != newDisplayInfo.state) {
+ if (mFlags.isDisplayListenerPerformanceImprovementsEnabled()
+ && mTempDisplayInfo.state != newDisplayInfo.state) {
mask |= LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
}
return mask;
@@ -1357,8 +1367,6 @@
return "added";
case LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION:
return "transition";
- case LOGICAL_DISPLAY_EVENT_CHANGED:
- return "changed";
case LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED:
return "framerate_override";
case LOGICAL_DISPLAY_EVENT_SWAPPED:
@@ -1375,6 +1383,8 @@
return "state_changed";
case LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED:
return "refresh_rate_changed";
+ case LOGICAL_DISPLAY_EVENT_BASIC_CHANGED:
+ return "basic_changed";
}
return null;
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 58c8450..0d6e502 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.MediaRoute2ProviderService.Reason;
import android.media.MediaRouter2;
import android.media.MediaRouter2Utils;
import android.media.RouteDiscoveryPreference;
@@ -123,6 +124,13 @@
}
}
+ /** Calls {@link Callback#onRequestFailed} with the given id and reason. */
+ protected void notifyRequestFailed(long requestId, @Reason int reason) {
+ if (mCallback != null) {
+ mCallback.onRequestFailed(/* provider= */ this, requestId, reason);
+ }
+ }
+
void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo) {
setProviderState(providerInfo);
notifyProviderState();
@@ -171,11 +179,34 @@
void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
void onSessionCreated(@NonNull MediaRoute2Provider provider,
long requestId, @Nullable RoutingSessionInfo sessionInfo);
- void onSessionUpdated(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo);
+
+ /**
+ * Called when there's a session info change.
+ *
+ * <p>If the provided {@code sessionInfo} has a null {@link
+ * RoutingSessionInfo#getClientPackageName()}, that means that it's applicable to all
+ * packages. We call this type of routing session "global". This is typically used for
+ * system provided {@link RoutingSessionInfo}. However, some applications may be exempted
+ * from the global routing sessions, because their media is being routed using a session
+ * different from the global routing session.
+ *
+ * @param provider The provider that owns the session that changed.
+ * @param sessionInfo The new {@link RoutingSessionInfo}.
+ * @param packageNamesWithRoutingSessionOverrides The names of packages that are not
+ * affected by global session changes. This set may only be non-empty when the {@code
+ * sessionInfo} is for the global session, and therefore has no {@link
+ * RoutingSessionInfo#getClientPackageName()}.
+ */
+ void onSessionUpdated(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides);
+
void onSessionReleased(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo);
- void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId, int reason);
+
+ void onRequestFailed(
+ @NonNull MediaRoute2Provider provider, long requestId, @Reason int reason);
}
/**
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index f09be2c..d6f7d3b 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -31,6 +32,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.MediaRoute2ProviderService.Reason;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -41,6 +43,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Slog;
@@ -89,6 +92,12 @@
mRequestIdToSessionCreationRequest;
@GuardedBy("mLock")
+ private final Map<String, SystemMediaSessionCallback> mSystemSessionCallbacks;
+
+ @GuardedBy("mLock")
+ private final LongSparseArray<SystemMediaSessionCallback> mRequestIdToSystemSessionRequest;
+
+ @GuardedBy("mLock")
private final Map<String, SessionCreationOrTransferRequest> mSessionOriginalIdToTransferRequest;
MediaRoute2ProviderServiceProxy(
@@ -102,6 +111,8 @@
mContext = Objects.requireNonNull(context, "Context must not be null.");
mRequestIdToSessionCreationRequest = new LongSparseArray<>();
mSessionOriginalIdToTransferRequest = new HashMap<>();
+ mRequestIdToSystemSessionRequest = new LongSparseArray<>();
+ mSystemSessionCallbacks = new ArrayMap<>();
mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
mSupportsSystemMediaRouting = supportsSystemMediaRouting;
mUserId = userId;
@@ -236,6 +247,48 @@
}
}
+ /**
+ * Requests the creation of a system media routing session.
+ *
+ * @param requestId The id of the request.
+ * @param uid The uid of the package whose media to route, or {@link
+ * android.os.Process#INVALID_UID} if not applicable (for example, if all the system's media
+ * must be routed).
+ * @param packageName The package name to populate {@link
+ * RoutingSessionInfo#getClientPackageName()}.
+ * @param routeId The id of the route to be initially {@link
+ * RoutingSessionInfo#getSelectedRoutes()}.
+ * @param sessionHints An optional bundle with paramets.
+ * @param callback A {@link SystemMediaSessionCallback} to notify of session events.
+ * @see MediaRoute2ProviderService#onCreateSystemRoutingSession
+ */
+ public void requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String routeId,
+ @Nullable Bundle sessionHints,
+ @NonNull SystemMediaSessionCallback callback) {
+ if (!Flags.enableMirroringInMediaRouter2()) {
+ throw new IllegalStateException(
+ "Unexpected call to requestCreateSystemMediaSession. Governing flag is"
+ + " disabled.");
+ }
+ if (mConnectionReady) {
+ boolean binderRequestSucceeded =
+ mActiveConnection.requestCreateSystemMediaSession(
+ requestId, uid, packageName, routeId, sessionHints);
+ if (!binderRequestSucceeded) {
+ // notify failure.
+ return;
+ }
+ updateBinding();
+ synchronized (mLock) {
+ mRequestIdToSystemSessionRequest.put(requestId, callback);
+ }
+ }
+ }
+
public boolean hasComponentName(String packageName, String className) {
return mComponentName.getPackageName().equals(packageName)
&& mComponentName.getClassName().equals(className);
@@ -292,7 +345,14 @@
mLastDiscoveryPreference != null
&& mLastDiscoveryPreference.shouldPerformActiveScan()
&& mSupportsSystemMediaRouting;
+ boolean bindDueToOngoingSystemMediaRoutingSessions = false;
+ if (Flags.enableMirroringInMediaRouter2()) {
+ synchronized (mLock) {
+ bindDueToOngoingSystemMediaRoutingSessions = !mSystemSessionCallbacks.isEmpty();
+ }
+ }
if (!getSessionInfos().isEmpty()
+ || bindDueToOngoingSystemMediaRoutingSessions
|| bindDueToManagerScan
|| bindDueToSystemMediaRoutingSupport) {
return true;
@@ -438,6 +498,14 @@
String newSessionId = newSession.getId();
synchronized (mLock) {
+ var systemMediaSessionCallback = mRequestIdToSystemSessionRequest.get(requestId);
+ if (systemMediaSessionCallback != null) {
+ mRequestIdToSystemSessionRequest.remove(requestId);
+ mSystemSessionCallbacks.put(newSession.getOriginalId(), systemMediaSessionCallback);
+ systemMediaSessionCallback.onSessionUpdate(newSession);
+ return;
+ }
+
if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
newSession =
createSessionWithPopulatedTransferInitiationDataLocked(
@@ -569,6 +637,12 @@
boolean found = false;
synchronized (mLock) {
+ var sessionCallback = mSystemSessionCallbacks.get(releasedSession.getOriginalId());
+ if (sessionCallback != null) {
+ sessionCallback.onSessionReleased();
+ return;
+ }
+
mSessionOriginalIdToTransferRequest.remove(releasedSession.getId());
for (RoutingSessionInfo session : mSessionInfos) {
if (TextUtils.equals(session.getId(), releasedSession.getId())) {
@@ -602,7 +676,11 @@
private void dispatchSessionUpdated(RoutingSessionInfo session) {
mHandler.sendMessage(
- obtainMessage(mCallback::onSessionUpdated, this, session));
+ obtainMessage(
+ mCallback::onSessionUpdated,
+ this,
+ session,
+ /* packageNamesWithRoutingSessionOverrides= */ Set.of()));
}
private void dispatchSessionReleased(RoutingSessionInfo session) {
@@ -645,6 +723,19 @@
for (RoutingSessionInfo sessionInfo : mSessionInfos) {
mCallback.onSessionReleased(this, sessionInfo);
}
+ if (Flags.enableMirroringInMediaRouter2()) {
+ for (var callback : mSystemSessionCallbacks.values()) {
+ callback.onSessionReleased();
+ }
+ mSystemSessionCallbacks.clear();
+ int requestsSize = mRequestIdToSystemSessionRequest.size();
+ for (int i = 0; i < requestsSize; i++) {
+ var callback = mRequestIdToSystemSessionRequest.valueAt(i);
+ var requestId = mRequestIdToSystemSessionRequest.keyAt(i);
+ callback.onRequestFailed(requestId, REASON_REJECTED);
+ }
+ mSystemSessionCallbacks.clear();
+ }
mSessionInfos.clear();
mReleasingSessions.clear();
mRequestIdToSessionCreationRequest.clear();
@@ -673,6 +764,26 @@
pendingTransferCount);
}
+ /**
+ * Callback for events related to system media sessions.
+ *
+ * @see MediaRoute2ProviderService#onCreateSystemRoutingSession
+ */
+ public interface SystemMediaSessionCallback {
+
+ /**
+ * Called when the corresponding session's {@link RoutingSessionInfo}, or upon the creation
+ * of the given session info.
+ */
+ void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo);
+
+ /** Called when the request with the given id fails for the given reason. */
+ void onRequestFailed(long requestId, @Reason int reason);
+
+ /** Called when the corresponding session is released. */
+ void onSessionReleased();
+ }
+
// All methods in this class are called on the main thread.
private final class ServiceConnectionImpl implements ServiceConnection {
@@ -739,6 +850,28 @@
}
}
+ /**
+ * Sends a system media session creation request to the provider service, and returns
+ * whether the request transaction succeeded.
+ *
+ * <p>The transaction might fail, for example, if the recipient process has died.
+ */
+ public boolean requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String routeId,
+ @Nullable Bundle sessionHints) {
+ try {
+ mService.requestCreateSystemMediaSession(
+ requestId, uid, packageName, routeId, sessionHints);
+ return true;
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "requestCreateSystemMediaSession: Failed to deliver request.");
+ }
+ return false;
+ }
+
public void releaseSession(long requestId, String sessionId) {
try {
mService.releaseSession(requestId, sessionId);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 58deffc..5e6737a4 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -846,33 +846,29 @@
try {
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- List<RoutingSessionInfo> sessionInfos;
+ SystemMediaRoute2Provider systemProvider = userRecord.mHandler.getSystemProvider();
if (hasSystemRoutingPermissions) {
- if (setDeviceRouteSelected && !Flags.enableMirroringInMediaRouter2()) {
+ if (!Flags.enableMirroringInMediaRouter2() && setDeviceRouteSelected) {
// Return a fake system session that shows the device route as selected and
// available bluetooth routes as transferable.
- return userRecord.mHandler.getSystemProvider()
- .generateDeviceRouteSelectedSessionInfo(targetPackageName);
+ return systemProvider.generateDeviceRouteSelectedSessionInfo(
+ targetPackageName);
} else {
- sessionInfos = userRecord.mHandler.getSystemProvider().getSessionInfos();
- if (!sessionInfos.isEmpty()) {
- // Return a copy of the current system session with no modification,
- // except setting the client package name.
- return new RoutingSessionInfo.Builder(sessionInfos.get(0))
- .setClientPackageName(targetPackageName)
- .build();
+ RoutingSessionInfo session =
+ systemProvider.getSessionForPackage(targetPackageName);
+ if (session != null) {
+ return session;
} else {
Slog.w(TAG, "System provider does not have any session info.");
+ return null;
}
}
} else {
- return new RoutingSessionInfo.Builder(
- userRecord.mHandler.getSystemProvider().getDefaultSessionInfo())
+ return new RoutingSessionInfo.Builder(systemProvider.getDefaultSessionInfo())
.setClientPackageName(targetPackageName)
.build();
}
}
- return null;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2638,10 +2634,17 @@
}
@Override
- public void onSessionUpdated(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo) {
- sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
- this, provider, sessionInfo));
+ public void onSessionUpdated(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides) {
+ sendMessage(
+ PooledLambda.obtainMessage(
+ UserHandler::onSessionInfoChangedOnHandler,
+ this,
+ provider,
+ sessionInfo,
+ packageNamesWithRoutingSessionOverrides));
}
@Override
@@ -3152,10 +3155,31 @@
toOriginalRequestId(uniqueRequestId), sessionInfo);
}
- private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
- @NonNull RoutingSessionInfo sessionInfo) {
+ /**
+ * Implementation of {@link MediaRoute2Provider.Callback#onSessionUpdated}.
+ *
+ * <p>Must run on the thread that corresponds to this {@link UserHandler}.
+ */
+ private void onSessionInfoChangedOnHandler(
+ @NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo,
+ Set<String> packageNamesWithRoutingSessionOverrides) {
List<ManagerRecord> managers = getManagerRecords();
for (ManagerRecord manager : managers) {
+ if (Flags.enableMirroringInMediaRouter2()) {
+ String targetPackageName = manager.mTargetPackageName;
+ boolean skipDueToOverride =
+ targetPackageName != null
+ && packageNamesWithRoutingSessionOverrides.contains(
+ targetPackageName);
+ boolean sessionIsForTargetPackage =
+ TextUtils.isEmpty(sessionInfo.getClientPackageName()) // is global.
+ || TextUtils.equals(
+ targetPackageName, sessionInfo.getClientPackageName());
+ if (skipDueToOverride || !sessionIsForTargetPackage) {
+ continue;
+ }
+ }
manager.notifySessionUpdated(sessionInfo);
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index b93846b..60fced1 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -62,7 +62,7 @@
static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
private final AudioManager mAudioManager;
- private final Handler mHandler;
+ protected final Handler mHandler;
private final Context mContext;
private final UserHandle mUser;
@@ -116,7 +116,7 @@
() -> {
publishProviderState();
if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
});
@@ -129,7 +129,7 @@
() -> {
publishProviderState();
if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
}));
}
@@ -161,7 +161,7 @@
public void setCallback(Callback callback) {
super.setCallback(callback);
notifyProviderState();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
@Override
@@ -296,7 +296,7 @@
if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
&& updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
}
@@ -327,6 +327,23 @@
}
/**
+ * Returns the {@link RoutingSessionInfo} that corresponds to the package with the given name.
+ */
+ public RoutingSessionInfo getSessionForPackage(String targetPackageName) {
+ synchronized (mLock) {
+ if (!mSessionInfos.isEmpty()) {
+ // Return a copy of the current system session with no modification,
+ // except setting the client package name.
+ return new RoutingSessionInfo.Builder(mSessionInfos.get(0))
+ .setClientPackageName(targetPackageName)
+ .build();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
* Builds a system {@link RoutingSessionInfo} with the selected route set to the currently
* selected <b>device</b> route (wired or built-in, but not bluetooth) and transferable routes
* set to the currently available (connected) bluetooth routes.
@@ -626,20 +643,21 @@
notifyProviderState();
}
- void notifySessionInfoUpdated() {
+ void notifyGlobalSessionInfoUpdated() {
if (mCallback == null) {
return;
}
RoutingSessionInfo sessionInfo;
synchronized (mLock) {
- sessionInfo = mSessionInfos.get(0);
- if (sessionInfo == null) {
+ if (mSessionInfos.isEmpty()) {
return;
}
+ sessionInfo = mSessionInfos.get(0);
}
- mCallback.onSessionUpdated(this, sessionInfo);
+ mCallback.onSessionUpdated(
+ this, sessionInfo, /* packageNamesWithRoutingSessionOverrides= */ Set.of());
}
@Override
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
index 7dc30ab..8931e3a 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider2.java
@@ -18,23 +18,33 @@
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.media.MediaRoute2ProviderService.Reason;
+import android.media.MediaRouter2Utils;
import android.media.RoutingSessionInfo;
+import android.os.Binder;
import android.os.Looper;
+import android.os.Process;
import android.os.UserHandle;
-import android.util.ArraySet;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.LongSparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.media.MediaRoute2ProviderServiceProxy.SystemMediaSessionCallback;
-import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Stream;
/**
@@ -48,11 +58,33 @@
private static final String ROUTE_ID_PREFIX_SYSTEM = "SYSTEM";
private static final String ROUTE_ID_SYSTEM_SEPARATOR = ".";
+ private final PackageManager mPackageManager;
+
@GuardedBy("mLock")
private MediaRoute2ProviderInfo mLastSystemProviderInfo;
@GuardedBy("mLock")
- private final Map<String, ProviderProxyRecord> mProxyRecords = new HashMap<>();
+ private final Map<String, ProviderProxyRecord> mProxyRecords = new ArrayMap<>();
+
+ /**
+ * Maps package names to corresponding sessions maintained by {@link MediaRoute2ProviderService
+ * provider services}.
+ */
+ @GuardedBy("mLock")
+ private final Map<String, SystemMediaSessionRecord> mPackageNameToSessionRecord =
+ new ArrayMap<>();
+
+ /**
+ * Maps route {@link MediaRoute2Info#getOriginalId original ids} to the id of the {@link
+ * MediaRoute2ProviderService provider service} that manages the corresponding route.
+ */
+ @GuardedBy("mLock")
+ private final Map<String, String> mOriginalRouteIdToProviderId = new ArrayMap<>();
+
+ /** Maps request ids to pending session creation callbacks. */
+ @GuardedBy("mLock")
+ private final LongSparseArray<SystemMediaSessionCallbackImpl> mPendingSessionCreations =
+ new LongSparseArray<>();
private static final ComponentName COMPONENT_NAME =
new ComponentName(
@@ -69,6 +101,128 @@
private SystemMediaRoute2Provider2(Context context, UserHandle user, Looper looper) {
super(context, COMPONENT_NAME, user, looper);
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public void transferToRoute(
+ long requestId,
+ @NonNull UserHandle clientUserHandle,
+ @NonNull String clientPackageName,
+ String sessionOriginalId,
+ String routeOriginalId,
+ int transferReason) {
+ synchronized (mLock) {
+ var targetProviderProxyId = mOriginalRouteIdToProviderId.get(routeOriginalId);
+ var targetProviderProxyRecord = mProxyRecords.get(targetProviderProxyId);
+ // Holds the target route, if it's managed by a provider service. Holds null otherwise.
+ var serviceTargetRoute =
+ targetProviderProxyRecord != null
+ ? targetProviderProxyRecord.getRouteByOriginalId(routeOriginalId)
+ : null;
+ var existingSessionRecord = mPackageNameToSessionRecord.get(clientPackageName);
+ if (existingSessionRecord != null) {
+ var existingSession = existingSessionRecord.mSourceSessionInfo;
+ if (targetProviderProxyId != null
+ && TextUtils.equals(
+ targetProviderProxyId, existingSession.getProviderId())) {
+ // The currently selected route and target route both belong to the same
+ // provider. We tell the provider to handle the transfer.
+ targetProviderProxyRecord.requestTransfer(
+ existingSession.getOriginalId(), serviceTargetRoute);
+ } else {
+ // The target route is handled by a provider other than the target one. We need
+ // to release the existing session.
+ var currentProxyRecord = existingSessionRecord.getProxyRecord();
+ if (currentProxyRecord != null) {
+ currentProxyRecord.releaseSession(
+ requestId, existingSession.getOriginalId());
+ existingSessionRecord.removeSelfFromSessionMap();
+ }
+ }
+ }
+
+ if (serviceTargetRoute != null) {
+ boolean isGlobalSession = TextUtils.isEmpty(clientPackageName);
+ int uid;
+ if (isGlobalSession) {
+ uid = Process.INVALID_UID;
+ } else {
+ uid = fetchUid(clientPackageName, clientUserHandle);
+ if (uid == Process.INVALID_UID) {
+ throw new IllegalArgumentException(
+ "Cannot resolve transfer for "
+ + clientPackageName
+ + " and "
+ + clientUserHandle);
+ }
+ }
+ var pendingCreationCallback =
+ new SystemMediaSessionCallbackImpl(
+ targetProviderProxyId, requestId, clientPackageName);
+ mPendingSessionCreations.put(requestId, pendingCreationCallback);
+ targetProviderProxyRecord.requestCreateSystemMediaSession(
+ requestId,
+ uid,
+ clientPackageName,
+ routeOriginalId,
+ pendingCreationCallback);
+ } else {
+ // The target route is not provided by any of the services. Assume it's a system
+ // provided route.
+ super.transferToRoute(
+ requestId,
+ clientUserHandle,
+ clientPackageName,
+ sessionOriginalId,
+ routeOriginalId,
+ transferReason);
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public RoutingSessionInfo getSessionForPackage(String packageName) {
+ synchronized (mLock) {
+ var systemSession = super.getSessionForPackage(packageName);
+ if (systemSession == null) {
+ return null;
+ }
+ var overridingSession = mPackageNameToSessionRecord.get(packageName);
+ if (overridingSession != null) {
+ var builder =
+ new RoutingSessionInfo.Builder(overridingSession.mTranslatedSessionInfo)
+ .setProviderId(mUniqueId)
+ .setSystemSession(true);
+ for (var systemRoute : mLastSystemProviderInfo.getRoutes()) {
+ builder.addTransferableRoute(systemRoute.getOriginalId());
+ }
+ return builder.build();
+ } else {
+ return systemSession;
+ }
+ }
+ }
+
+ /**
+ * Returns the uid that corresponds to the given name and user handle, or {@link
+ * Process#INVALID_UID} if a uid couldn't be found.
+ */
+ @SuppressLint("MissingPermission")
+ // We clear the calling identity before calling the package manager, and we are running on the
+ // system_server.
+ private int fetchUid(String clientPackageName, UserHandle clientUserHandle) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mPackageManager.getApplicationInfoAsUser(
+ clientPackageName, /* flags= */ 0, clientUserHandle)
+ .uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ return Process.INVALID_UID;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
@@ -85,21 +239,21 @@
} else {
mProxyRecords.put(serviceProxy.mUniqueId, proxyRecord);
}
- setProviderState(buildProviderInfo());
+ updateProviderInfo();
}
updateSessionInfo();
notifyProviderState();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
@Override
public void onSystemProviderRoutesChanged(MediaRoute2ProviderInfo providerInfo) {
synchronized (mLock) {
mLastSystemProviderInfo = providerInfo;
- setProviderState(buildProviderInfo());
+ updateProviderInfo();
}
updateSessionInfo();
- notifySessionInfoUpdated();
+ notifyGlobalSessionInfoUpdated();
}
/**
@@ -116,10 +270,13 @@
var builder = new RoutingSessionInfo.Builder(systemSessionInfo);
mProxyRecords.values().stream()
.flatMap(ProviderProxyRecord::getRoutesStream)
- .map(MediaRoute2Info::getId)
+ .map(MediaRoute2Info::getOriginalId)
.forEach(builder::addTransferableRoute);
mSessionInfos.clear();
mSessionInfos.add(builder.build());
+ for (var sessionRecords : mPackageNameToSessionRecord.values()) {
+ mSessionInfos.add(sessionRecords.mTranslatedSessionInfo);
+ }
}
}
@@ -129,13 +286,84 @@
* provider services}.
*/
@GuardedBy("mLock")
- private MediaRoute2ProviderInfo buildProviderInfo() {
+ private void updateProviderInfo() {
MediaRoute2ProviderInfo.Builder builder =
new MediaRoute2ProviderInfo.Builder(mLastSystemProviderInfo);
- mProxyRecords.values().stream()
- .flatMap(ProviderProxyRecord::getRoutesStream)
- .forEach(builder::addRoute);
- return builder.build();
+ mOriginalRouteIdToProviderId.clear();
+ for (var proxyRecord : mProxyRecords.values()) {
+ String proxyId = proxyRecord.mProxy.mUniqueId;
+ proxyRecord
+ .getRoutesStream()
+ .forEach(
+ route -> {
+ builder.addRoute(route);
+ mOriginalRouteIdToProviderId.put(route.getOriginalId(), proxyId);
+ });
+ }
+ setProviderState(builder.build());
+ }
+
+ @Override
+ /* package */ void notifyGlobalSessionInfoUpdated() {
+ if (mCallback == null) {
+ return;
+ }
+
+ RoutingSessionInfo sessionInfo;
+ Set<String> packageNamesWithRoutingSessionOverrides;
+ synchronized (mLock) {
+ if (mSessionInfos.isEmpty()) {
+ return;
+ }
+ packageNamesWithRoutingSessionOverrides = mPackageNameToSessionRecord.keySet();
+ sessionInfo = mSessionInfos.getFirst();
+ }
+
+ mCallback.onSessionUpdated(this, sessionInfo, packageNamesWithRoutingSessionOverrides);
+ }
+
+ private void onSessionOverrideUpdated(RoutingSessionInfo sessionInfo) {
+ // TODO: b/362507305 - Consider adding routes from other provider services. This is not a
+ // trivial change because a provider1-route to provider2-route transfer has seemingly two
+ // possible approachies. Either we first release the current session and then create the new
+ // one, in which case the audio is briefly going to leak through the system route. On the
+ // other hand, if we first create the provider2 session, then there will be a period during
+ // which there will be two overlapping routing policies asking for the exact same media
+ // stream.
+ var builder = new RoutingSessionInfo.Builder(sessionInfo);
+ mLastSystemProviderInfo.getRoutes().stream()
+ .map(MediaRoute2Info::getOriginalId)
+ .forEach(builder::addTransferableRoute);
+ mCallback.onSessionUpdated(
+ /* provider= */ this,
+ builder.build(),
+ /* packageNamesWithRoutingSessionOverrides= */ Set.of());
+ }
+
+ /**
+ * Equivalent to {@link #asSystemRouteId}, except it takes a unique route id instead of a
+ * original id.
+ */
+ private static String uniqueIdAsSystemRouteId(String providerId, String uniqueRouteId) {
+ return asSystemRouteId(providerId, MediaRouter2Utils.getOriginalId(uniqueRouteId));
+ }
+
+ /**
+ * Returns a unique {@link MediaRoute2Info#getOriginalId() original id} for this provider to
+ * publish system media routes from {@link MediaRoute2ProviderService provider services}.
+ *
+ * <p>This provider will publish system media routes as part of the system routing session.
+ * However, said routes may also support {@link MediaRoute2Info#FLAG_ROUTING_TYPE_REMOTE remote
+ * routing}, meaning we cannot use the same id, or there would be an id collision. As a result,
+ * we derive a {@link MediaRoute2Info#getOriginalId original id} that is unique among all
+ * original route ids used by this provider.
+ */
+ private static String asSystemRouteId(String providerId, String originalRouteId) {
+ return ROUTE_ID_PREFIX_SYSTEM
+ + ROUTE_ID_SYSTEM_SEPARATOR
+ + providerId
+ + ROUTE_ID_SYSTEM_SEPARATOR
+ + originalRouteId;
}
/**
@@ -145,14 +373,69 @@
* @param mProxy The corresponding {@link MediaRoute2ProviderServiceProxy}.
* @param mSystemMediaRoutes The last snapshot of routes from the service that support system
* media routing, as defined by {@link MediaRoute2Info#supportsSystemMediaRouting()}.
+ * @param mNewOriginalIdToSourceOriginalIdMap Maps the {@link #mSystemMediaRoutes} ids to the
+ * original ids of corresponding {@link MediaRoute2ProviderService service} route.
*/
private record ProviderProxyRecord(
MediaRoute2ProviderServiceProxy mProxy,
- Collection<MediaRoute2Info> mSystemMediaRoutes) {
+ Map<String, MediaRoute2Info> mSystemMediaRoutes,
+ Map<String, String> mNewOriginalIdToSourceOriginalIdMap) {
/** Returns a stream representation of the {@link #mSystemMediaRoutes}. */
public Stream<MediaRoute2Info> getRoutesStream() {
- return mSystemMediaRoutes.stream();
+ return mSystemMediaRoutes.values().stream();
+ }
+
+ @Nullable
+ public MediaRoute2Info getRouteByOriginalId(String routeOriginalId) {
+ return mSystemMediaRoutes.get(routeOriginalId);
+ }
+
+ /**
+ * Requests the creation of a system media routing session.
+ *
+ * @param requestId The request id.
+ * @param uid The uid of the package whose media to route, or {@link Process#INVALID_UID} if
+ * not applicable.
+ * @param packageName The name of the package whose media to route.
+ * @param originalRouteId The {@link MediaRoute2Info#getOriginalId() original route id} of
+ * the route that should be initially selected.
+ * @param callback A {@link MediaRoute2ProviderServiceProxy.SystemMediaSessionCallback} for
+ * events.
+ * @see MediaRoute2ProviderService#onCreateSystemRoutingSession
+ */
+ public void requestCreateSystemMediaSession(
+ long requestId,
+ int uid,
+ String packageName,
+ String originalRouteId,
+ SystemMediaSessionCallback callback) {
+ var targetRouteId = mNewOriginalIdToSourceOriginalIdMap.get(originalRouteId);
+ if (targetRouteId == null) {
+ Log.w(
+ TAG,
+ "Failed system media session creation due to lack of mapping for id: "
+ + originalRouteId);
+ callback.onRequestFailed(
+ requestId, MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE);
+ } else {
+ mProxy.requestCreateSystemMediaSession(
+ requestId,
+ uid,
+ packageName,
+ targetRouteId,
+ /* sessionHints= */ null,
+ callback);
+ }
+ }
+
+ public void requestTransfer(String sessionId, MediaRoute2Info targetRoute) {
+ // TODO: Map the target route to the source route original id.
+ throw new UnsupportedOperationException("TODO Implement");
+ }
+
+ public void releaseSession(long requestId, String originalSessionId) {
+ mProxy.releaseSession(requestId, originalSessionId);
}
/**
@@ -165,22 +448,177 @@
if (providerInfo == null) {
return null;
}
- ArraySet<MediaRoute2Info> routes = new ArraySet<>();
- providerInfo.getRoutes().stream()
- .filter(MediaRoute2Info::supportsSystemMediaRouting)
- .forEach(
- route -> {
- String id =
- ROUTE_ID_PREFIX_SYSTEM
- + route.getProviderId()
- + ROUTE_ID_SYSTEM_SEPARATOR
- + route.getOriginalId();
- routes.add(
- new MediaRoute2Info.Builder(id, route.getName())
- .addFeature(FEATURE_LIVE_AUDIO)
- .build());
- });
- return new ProviderProxyRecord(serviceProxy, Collections.unmodifiableSet(routes));
+ Map<String, MediaRoute2Info> routesMap = new ArrayMap<>();
+ Map<String, String> idMap = new ArrayMap<>();
+ for (MediaRoute2Info sourceRoute : providerInfo.getRoutes()) {
+ if (!sourceRoute.supportsSystemMediaRouting()) {
+ continue;
+ }
+ String id =
+ asSystemRouteId(providerInfo.getUniqueId(), sourceRoute.getOriginalId());
+ var newRoute =
+ new MediaRoute2Info.Builder(id, sourceRoute.getName())
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .build();
+ routesMap.put(id, newRoute);
+ idMap.put(id, sourceRoute.getOriginalId());
+ }
+ return new ProviderProxyRecord(
+ serviceProxy,
+ Collections.unmodifiableMap(routesMap),
+ Collections.unmodifiableMap(idMap));
+ }
+ }
+
+ private class SystemMediaSessionCallbackImpl implements SystemMediaSessionCallback {
+
+ private final String mProviderId;
+ private final long mRequestId;
+ private final String mClientPackageName;
+ // Accessed only on mHandler.
+ @Nullable private SystemMediaSessionRecord mSessionRecord;
+
+ private SystemMediaSessionCallbackImpl(
+ String providerId, long requestId, String clientPackageName) {
+ mProviderId = providerId;
+ mRequestId = requestId;
+ mClientPackageName = clientPackageName;
+ }
+
+ @Override
+ public void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo) {
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onSessionUpdate(sessionInfo);
+ }
+ SystemMediaSessionRecord systemMediaSessionRecord =
+ new SystemMediaSessionRecord(mProviderId, sessionInfo);
+ RoutingSessionInfo translatedSession;
+ synchronized (mLock) {
+ mSessionRecord = systemMediaSessionRecord;
+ mPackageNameToSessionRecord.put(
+ mClientPackageName, systemMediaSessionRecord);
+ mPendingSessionCreations.remove(mRequestId);
+ translatedSession = systemMediaSessionRecord.mTranslatedSessionInfo;
+ }
+ onSessionOverrideUpdated(translatedSession);
+ });
+ }
+
+ @Override
+ public void onRequestFailed(long requestId, @Reason int reason) {
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onRequestFailed(requestId, reason);
+ }
+ synchronized (mLock) {
+ mPendingSessionCreations.remove(mRequestId);
+ }
+ notifyRequestFailed(requestId, reason);
+ });
+ }
+
+ @Override
+ public void onSessionReleased() {
+ mHandler.post(
+ () -> {
+ if (mSessionRecord != null) {
+ mSessionRecord.onSessionReleased();
+ } else {
+ // Should never happen. The session hasn't yet been created.
+ throw new IllegalStateException();
+ }
+ });
+ }
+ }
+
+ private class SystemMediaSessionRecord implements SystemMediaSessionCallback {
+
+ private final String mProviderId;
+
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ @NonNull
+ private RoutingSessionInfo mSourceSessionInfo;
+
+ /**
+ * The same as {@link #mSourceSessionInfo}, except ids are {@link #asSystemRouteId system
+ * provider ids}.
+ */
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ @NonNull
+ private RoutingSessionInfo mTranslatedSessionInfo;
+
+ SystemMediaSessionRecord(
+ @NonNull String providerId, @NonNull RoutingSessionInfo sessionInfo) {
+ mProviderId = providerId;
+ mSourceSessionInfo = sessionInfo;
+ mTranslatedSessionInfo = asSystemProviderSession(sessionInfo);
+ }
+
+ @Override
+ public void onSessionUpdate(@NonNull RoutingSessionInfo sessionInfo) {
+ RoutingSessionInfo translatedSessionInfo = mTranslatedSessionInfo;
+ synchronized (mLock) {
+ mSourceSessionInfo = sessionInfo;
+ mTranslatedSessionInfo = asSystemProviderSession(sessionInfo);
+ }
+ onSessionOverrideUpdated(translatedSessionInfo);
+ }
+
+ @Override
+ public void onRequestFailed(long requestId, @Reason int reason) {
+ notifyRequestFailed(requestId, reason);
+ }
+
+ @Override
+ public void onSessionReleased() {
+ synchronized (mLock) {
+ removeSelfFromSessionMap();
+ }
+ notifyGlobalSessionInfoUpdated();
+ }
+
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ @Nullable
+ public ProviderProxyRecord getProxyRecord() {
+ ProviderProxyRecord provider = mProxyRecords.get(mProviderId);
+ if (provider == null) {
+ // Unexpected condition where the proxy is no longer available while there's an
+ // ongoing session. Could happen due to a crash in the provider process.
+ removeSelfFromSessionMap();
+ }
+ return provider;
+ }
+
+ @GuardedBy("SystemMediaRoute2Provider2.this.mLock")
+ private void removeSelfFromSessionMap() {
+ mPackageNameToSessionRecord.remove(mSourceSessionInfo.getClientPackageName());
+ }
+
+ private RoutingSessionInfo asSystemProviderSession(RoutingSessionInfo session) {
+ var builder =
+ new RoutingSessionInfo.Builder(session)
+ .setProviderId(mUniqueId)
+ .setSystemSession(true)
+ .clearSelectedRoutes()
+ .clearSelectableRoutes()
+ .clearDeselectableRoutes()
+ .clearTransferableRoutes();
+ session.getSelectedRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addSelectedRoute);
+ session.getSelectableRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addSelectableRoute);
+ session.getDeselectableRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addDeselectableRoute);
+ session.getTransferableRoutes().stream()
+ .map(it -> uniqueIdAsSystemRouteId(session.getProviderId(), it))
+ .forEach(builder::addTransferableRoute);
+ return builder.build();
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 89902f7..7cbbe29 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -101,4 +101,10 @@
void onNotificationFeedbackReceived(String key, Bundle feedback);
void prepareForPossibleShutdown();
+
+ /**
+ * Called when the notification should be unbundled.
+ * @param key the notification key
+ */
+ void unbundleNotification(String key);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c6d7fc7..7375a68 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -112,6 +112,7 @@
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.notificationClassification;
import static android.service.notification.Flags.notificationForceGrouping;
+import static android.service.notification.Flags.notificationRegroupOnClassification;
import static android.service.notification.Flags.redactSensitiveNotificationsBigTextStyle;
import static android.service.notification.Flags.redactSensitiveNotificationsFromUntrustedListeners;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -1851,6 +1852,42 @@
}
}
+ @Override
+ public void unbundleNotification(String key) {
+ if (!(notificationClassification() && notificationRegroupOnClassification())) {
+ return;
+ }
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) {
+ return;
+ }
+
+ if (DBG) {
+ Slog.v(TAG, "unbundleNotification: " + r);
+ }
+
+ boolean hasOriginalSummary = false;
+ if (r.getSbn().isAppGroup() && r.getNotification().isGroupChild()) {
+ final String oldGroupKey = GroupHelper.getFullAggregateGroupKey(
+ r.getSbn().getPackageName(), r.getOriginalGroupKey(), r.getUserId());
+ NotificationRecord groupSummary = mSummaryByGroupKey.get(oldGroupKey);
+ // We only care about app-provided valid groups
+ hasOriginalSummary = (groupSummary != null
+ && !GroupHelper.isAggregatedGroup(groupSummary));
+ }
+
+ // Only NotificationRecord's mChannel is updated when bundled, the Notification
+ // mChannelId will always be the original channel.
+ String origChannelId = r.getNotification().getChannelId();
+ NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
+ r.getSbn().getPackageName(), r.getUid(), origChannelId, false);
+ if (originalChannel != null && !origChannelId.equals(r.getChannel().getId())) {
+ r.updateNotificationChannel(originalChannel);
+ mGroupHelper.onNotificationUnbundled(r, hasOriginalSummary);
+ }
+ }
+ }
};
NotificationManagerPrivate mNotificationManagerPrivate = new NotificationManagerPrivate() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 749952e..15377d6 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -559,7 +559,7 @@
if (r.uid == UNKNOWN_UID) {
if (Flags.persistIncompleteRestoreData()) {
- r.userId = userId;
+ r.userIdWhenUidUnknown = userId;
}
mRestoredWithoutUids.put(unrestoredPackageKey(pkg, userId), r);
} else {
@@ -756,7 +756,7 @@
if (Flags.persistIncompleteRestoreData() && r.uid == UNKNOWN_UID) {
out.attributeLong(null, ATT_CREATION_TIME, r.creationTime);
- out.attributeInt(null, ATT_USERID, r.userId);
+ out.attributeInt(null, ATT_USERID, r.userIdWhenUidUnknown);
}
if (!forBackup) {
@@ -1959,7 +1959,7 @@
ArrayList<ZenBypassingApp> bypassing = new ArrayList<>();
synchronized (mLock) {
for (PackagePreferences p : mPackagePreferences.values()) {
- if (p.userId != userId) {
+ if (UserHandle.getUserId(p.uid) != userId) {
continue;
}
int totalChannelCount = p.channels.size();
@@ -3189,7 +3189,7 @@
// Until we enable the UI, we should return false.
boolean canHavePromotedNotifs = android.app.Flags.uiRichOngoing();
- @UserIdInt int userId;
+ @UserIdInt int userIdWhenUidUnknown;
Delegate delegate = null;
ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c4d1cc7..ec0f251 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4068,7 +4068,7 @@
@Nullable IBinder focusedToken) {
boolean handled = PhoneWindowManager.this.handleKeyGestureEvent(event,
focusedToken);
- if (handled && Arrays.stream(event.getKeycodes()).anyMatch(
+ if (handled && !event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch(
(keycode) -> keycode == KeyEvent.KEYCODE_POWER)) {
mPowerKeyHandled = true;
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 0c3c46c..271c818 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -503,6 +503,31 @@
}
}
+ @VisibleForTesting
+ int getWakelockMonitorTypeForLogging(int flags) {
+ switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+ case PowerManager.FULL_WAKE_LOCK, PowerManager.SCREEN_DIM_WAKE_LOCK,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ return PowerManager.FULL_WAKE_LOCK;
+ case PowerManager.DRAW_WAKE_LOCK:
+ return PowerManager.DRAW_WAKE_LOCK;
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ if (mSuspendWhenScreenOffDueToProximityConfig) {
+ return -1;
+ }
+ return PowerManager.PARTIAL_WAKE_LOCK;
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ return PowerManager.PARTIAL_WAKE_LOCK;
+ case PowerManager.DOZE_WAKE_LOCK:
+ // Doze wake locks are an internal implementation detail of the
+ // communication between dream manager service and power manager
+ // service. They have no additive battery impact.
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
/**
* Notifies that the device is changing wakefulness.
* This function may be called even if the previous change hasn't finished in
@@ -1288,7 +1313,7 @@
if (mBatteryStatsInternal == null) {
return;
}
- final int type = flags & PowerManager.WAKE_LOCK_LEVEL_MASK;
+ final int type = getWakelockMonitorTypeForLogging(flags);
if (workSource == null || workSource.isEmpty()) {
final int mappedUid = mBatteryStatsInternal.getOwnerUid(ownerUid);
mFrameworkStatsLogger.wakelockStateChanged(mappedUid, tag, type, eventType);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f8877ad..c18918f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -2175,6 +2175,19 @@
}
}
+ /**
+ * Called when the notification should be unbundled.
+ * @param key the notification key
+ */
+ @Override
+ public void unbundleNotification(@Nullable String key) {
+ enforceStatusBarService();
+ enforceValidCallingUser();
+ Binder.withCleanCallingIdentity(() -> {
+ mNotificationDelegate.unbundleNotification(key);
+ });
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index dad3a78..bb163ef 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -369,10 +369,9 @@
tagName = parser.getName();
if (TAG_QUOTA.equals(tagName)) {
CacheQuotaHint request = getRequestFromXml(parser);
- if (request == null) {
- continue;
+ if (request != null) {
+ quotas.add(request);
}
- quotas.add(request);
}
}
eventType = parser.next();
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 0369a0f..9f88bc9 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -164,15 +164,13 @@
appCompatTaskInfo.setIsFromLetterboxDoubleTap(reachabilityOverrides.isFromDoubleTap());
+ appCompatTaskInfo.topActivityAppBounds.set(getAppBounds(top));
final boolean isTopActivityLetterboxed = top.areBoundsLetterboxed();
appCompatTaskInfo.setTopActivityLetterboxed(isTopActivityLetterboxed);
if (isTopActivityLetterboxed) {
final Rect bounds = top.getBounds();
- final Rect appBounds = getAppBounds(top);
appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
- appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
- appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
// TODO(b/379824541) Remove duplicate information.
appCompatTaskInfo.topActivityLetterboxBounds = bounds;
// We need to consider if letterboxed or pillarboxed.
@@ -281,8 +279,7 @@
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppWidth = TaskInfo.PROPERTY_VALUE_UNSET;
+ info.topActivityAppBounds.setEmpty();
info.topActivityLetterboxBounds = null;
info.cameraCompatTaskInfo.freeformCameraCompatMode =
CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED;
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index dd1af0a..6b6f011 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -29,9 +29,6 @@
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-
-import com.android.internal.R;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -687,11 +684,12 @@
@Override
public Animation getFadeInAnimation() {
+ final Animation anim = super.getFadeInAnimation();
if (mHasScreenRotationAnimation) {
// Use a shorter animation so it is easier to align with screen rotation animation.
- return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
+ anim.setDuration(getScaledDuration(SHORT_DURATION_MS));
}
- return super.getFadeInAnimation();
+ return anim;
}
@Override
diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java
index 7af67e6..c60d367 100644
--- a/services/core/java/com/android/server/wm/FadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeAnimationController.java
@@ -20,41 +20,51 @@
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
import android.annotation.NonNull;
-import android.content.Context;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
-import com.android.internal.R;
-
import java.io.PrintWriter;
/**
* An animation controller to fade-in/out for a window token.
*/
public class FadeAnimationController {
+ static final int SHORT_DURATION_MS = 200;
+ static final int MEDIUM_DURATION_MS = 350;
+
protected final DisplayContent mDisplayContent;
- protected final Context mContext;
public FadeAnimationController(DisplayContent displayContent) {
mDisplayContent = displayContent;
- mContext = displayContent.mWmService.mContext;
}
/**
* @return a fade-in Animation.
*/
public Animation getFadeInAnimation() {
- return AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
+ final AlphaAnimation anim = new AlphaAnimation(0f, 1f);
+ anim.setDuration(getScaledDuration(MEDIUM_DURATION_MS));
+ anim.setInterpolator(new DecelerateInterpolator());
+ return anim;
}
/**
* @return a fade-out Animation.
*/
public Animation getFadeOutAnimation() {
- return AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
+ final AlphaAnimation anim = new AlphaAnimation(1f, 0f);
+ anim.setDuration(getScaledDuration(SHORT_DURATION_MS));
+ anim.setInterpolator(new AccelerateInterpolator());
+ return anim;
+ }
+
+ long getScaledDuration(int durationMs) {
+ return (long) (durationMs * mDisplayContent.mWmService.getWindowAnimationScaleLocked());
}
/** Run the fade in/out animation for a window token. */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9d9c53d..db62ceb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2945,7 +2945,7 @@
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
if (callingPid != MY_PID) {
- throw new WindowManager.InvalidDisplayException(
+ throw new IllegalArgumentException(
"attachWindowContextToDisplayContent: trying to attach to a"
+ " non-existing display:" + displayId);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index f96294ed..b7b4f04 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -214,7 +214,8 @@
private static final String PACKAGE_NAME = "com.android.frameworks.displayservicetests";
private static final long STANDARD_DISPLAY_EVENTS =
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
private static final long STANDARD_AND_CONNECTION_DISPLAY_EVENTS =
STANDARD_DISPLAY_EVENTS
@@ -222,7 +223,7 @@
private static final String EVENT_DISPLAY_ADDED = "EVENT_DISPLAY_ADDED";
private static final String EVENT_DISPLAY_REMOVED = "EVENT_DISPLAY_REMOVED";
- private static final String EVENT_DISPLAY_CHANGED = "EVENT_DISPLAY_CHANGED";
+ private static final String EVENT_DISPLAY_BASIC_CHANGED = "EVENT_DISPLAY_BASIC_CHANGED";
private static final String EVENT_DISPLAY_BRIGHTNESS_CHANGED =
"EVENT_DISPLAY_BRIGHTNESS_CHANGED";
private static final String EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED =
@@ -889,7 +890,6 @@
FakeDisplayManagerCallback callback = registerDisplayListenerCallback(
displayManager, bs, displayDevice);
-
// Simulate DisplayDevice change
DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo();
displayDeviceInfo2.copyFrom(displayDeviceInfo);
@@ -900,7 +900,8 @@
Handler handler = displayManager.getDisplayHandler();
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_BASIC_CHANGED,
+ EVENT_DISPLAY_REFRESH_RATE_CHANGED);
}
/**
@@ -2145,7 +2146,7 @@
new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -2154,7 +2155,7 @@
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_BASIC_CHANGED);
updateFrameRateOverride(displayManager, displayDevice,
new DisplayEventReceiver.FrameRateOverride[]{
@@ -2163,7 +2164,7 @@
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -2172,7 +2173,7 @@
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -2180,7 +2181,7 @@
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_BASIC_CHANGED);
}
/**
@@ -2303,16 +2304,16 @@
updateRenderFrameRate(displayManager, displayDevice, 30f);
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_REFRESH_RATE_CHANGED);
callback.clear();
updateRenderFrameRate(displayManager, displayDevice, 30f);
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_REFRESH_RATE_CHANGED);
updateRenderFrameRate(displayManager, displayDevice, 20f);
waitForIdleHandler(displayManager.getDisplayHandler());
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_REFRESH_RATE_CHANGED);
callback.clear();
}
@@ -3888,7 +3889,7 @@
observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_BASIC_CHANGED);
}
@Test
@@ -3919,7 +3920,7 @@
observer.onChange(false, Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY));
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_BASIC_CHANGED);
}
private void initDisplayPowerController(DisplayManagerInternal localService) {
@@ -4389,8 +4390,8 @@
return EVENT_DISPLAY_ADDED;
case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
return EVENT_DISPLAY_REMOVED;
- case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
- return EVENT_DISPLAY_CHANGED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED:
+ return EVENT_DISPLAY_BASIC_CHANGED;
case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
return EVENT_DISPLAY_BRIGHTNESS_CHANGED;
case DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 5d42713..c65024f8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -26,6 +26,7 @@
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -43,7 +44,7 @@
@Before
fun setUp() {
- displayInfo.displayId = 2
+ displayInfo.displayId = Display.DEFAULT_DISPLAY
displayInfo.logicalWidth = 300
displayInfo.logicalHeight = 200
displayInfo.logicalDensityDpi = 100
@@ -90,6 +91,44 @@
}
@Test
+ fun updateDisplay() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(true)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun updateDisplay_notChanged() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(false)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
+ fun removeDisplay() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(true)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun removeDisplay_notChanged() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(false)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
fun getTopology_copy() {
assertThat(coordinator.topology).isEqualTo(mockTopologyCopy)
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index ad30f22..0dbb6ba 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -36,9 +36,9 @@
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CONNECTED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DISCONNECTED;
-import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN;
import static com.android.server.utils.FoldSettingProvider.SETTING_VALUE_SELECTIVE_STAY_AWAKE;
@@ -1170,17 +1170,19 @@
@Test
public void updateAndGetMaskForDisplayPropertyChanges_getsPropertyChangedFlags() {
- // Change the display state
+ // Change the refresh rate override
DisplayInfo newDisplayInfo = new DisplayInfo();
+ newDisplayInfo.refreshRateOverride = 30;
+ assertEquals(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED,
+ mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
+
+ // Change the display state
+ when(mFlagsMock.isDisplayListenerPerformanceImprovementsEnabled()).thenReturn(true);
+ newDisplayInfo = new DisplayInfo();
newDisplayInfo.state = STATE_OFF;
assertEquals(LOGICAL_DISPLAY_EVENT_STATE_CHANGED,
mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
- // Change the refresh rate override
- newDisplayInfo = new DisplayInfo();
- newDisplayInfo.refreshRateOverride = 30;
- assertEquals(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED,
- mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
// Change multiple properties
newDisplayInfo = new DisplayInfo();
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 96741e0..469bd66 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -889,6 +890,32 @@
"my.package.name", false, null, null);
}
+ @Test
+ public void getWakelockMonitorTypeForLogging_evaluatesWakelockLevel() {
+ createNotifier();
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.SCREEN_DIM_WAKE_LOCK),
+ PowerManager.FULL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK), PowerManager.FULL_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.DRAW_WAKE_LOCK),
+ PowerManager.DRAW_WAKE_LOCK);
+ assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
+ PowerManager.PARTIAL_WAKE_LOCK);
+ 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);
+ }
+
private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 2fe6918..7dbbff2 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -33,6 +33,7 @@
import static com.android.server.am.UserController.CLEAR_USER_JOURNEY_SESSION_MSG;
import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG;
import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS;
import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
@@ -94,6 +95,7 @@
import android.os.Message;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
@@ -181,14 +183,12 @@
Intent.ACTION_USER_STARTING);
private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
- 0, // for startUserInternalOnHandler
REPORT_USER_SWITCH_MSG,
USER_SWITCH_TIMEOUT_MSG,
USER_START_MSG,
USER_CURRENT_MSG);
private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
- 0, // for startUserInternalOnHandler
USER_START_MSG,
REPORT_LOCKED_BOOT_COMPLETE_MSG);
@@ -376,7 +376,7 @@
// and the cascade effect goes on...). In fact, a better approach would to not assert the
// binder calls, but their side effects (in this case, that the user is stopped right away)
assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
- .containsExactly(/* for startUserInternalOnHandler */ 0, USER_START_MSG);
+ .containsExactly(USER_START_MSG);
}
private void startUserAssertions(
@@ -419,17 +419,12 @@
@Test
public void testDispatchUserSwitch() throws RemoteException {
// Prepare mock observer and register it
- IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
- when(observer.asBinder()).thenReturn(new Binder());
- doAnswer(invocation -> {
- IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
- callback.sendResult(null);
- return null;
- }).when(observer).onUserSwitching(anyInt(), any());
- mUserController.registerUserSwitchObserver(observer, "mock");
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ true,
+ /* replyToOnUserSwitchingCallback= */ true);
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
- verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID));
+ verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
UserState userState = (UserState) reportMsg.obj;
@@ -453,14 +448,26 @@
}
@Test
+ public void testShouldCrashWhenOnBeforeUserSwitchingTimeouts() throws RemoteException {
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ false,
+ /* replyToOnUserSwitchingCallback= */ true);
+ mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
+ verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
+ assertThrows("Should have crashed when observers don't reply to onBeforeUserSwitching in "
+ + DEFAULT_BEFORE_USER_SWITCH_TIMEOUT_MS + " ms", RuntimeException.class,
+ mInjector.mHandler::runPendingCallbacks);
+ }
+
+ @Test
public void testDispatchUserSwitchBadReceiver() throws RemoteException {
- // Prepare mock observer which doesn't notify the callback and register it
- IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
- when(observer.asBinder()).thenReturn(new Binder());
- mUserController.registerUserSwitchObserver(observer, "mock");
+ // Prepare mock observer which doesn't notify the onUserSwitching callback and register it
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ true,
+ /* replyToOnUserSwitchingCallback= */ false);
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
- verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID));
+ verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
UserState userState = (UserState) reportMsg.obj;
@@ -551,7 +558,6 @@
expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
if (backgroundUserStopping) {
expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG);
- expectedCodes.add(0); // this is for directly posting in stopping.
}
if (expectScheduleBackgroundUserStopping) {
expectedCodes.add(SCHEDULED_STOP_BACKGROUND_USER_MSG);
@@ -567,9 +573,9 @@
@Test
public void testDispatchUserSwitchComplete() throws RemoteException {
// Prepare mock observer and register it
- IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
- when(observer.asBinder()).thenReturn(new Binder());
- mUserController.registerUserSwitchObserver(observer, "mock");
+ IUserSwitchObserver observer = registerUserSwitchObserver(
+ /* replyToOnBeforeUserSwitchingCallback= */ true,
+ /* replyToOnUserSwitchingCallback= */ true);
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -1752,6 +1758,29 @@
verify(mInjector, never()).onSystemUserVisibilityChanged(anyBoolean());
}
+ private IUserSwitchObserver registerUserSwitchObserver(
+ boolean replyToOnBeforeUserSwitchingCallback, boolean replyToOnUserSwitchingCallback)
+ throws RemoteException {
+ IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
+ when(observer.asBinder()).thenReturn(new Binder());
+ if (replyToOnBeforeUserSwitchingCallback) {
+ doAnswer(invocation -> {
+ IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
+ callback.sendResult(null);
+ return null;
+ }).when(observer).onBeforeUserSwitching(anyInt(), any());
+ }
+ if (replyToOnUserSwitchingCallback) {
+ doAnswer(invocation -> {
+ IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
+ callback.sendResult(null);
+ return null;
+ }).when(observer).onUserSwitching(anyInt(), any());
+ }
+ mUserController.registerUserSwitchObserver(observer, "mock");
+ return observer;
+ }
+
// Should be public to allow mocking
private static class TestInjector extends UserController.Injector {
public final TestHandler mHandler;
@@ -1957,6 +1986,7 @@
* fix this, but in the meantime, this is your warning.
*/
private final List<Message> mMessages = new ArrayList<>();
+ private final List<Runnable> mPendingCallbacks = new ArrayList<>();
TestHandler(Looper looper) {
super(looper);
@@ -1989,14 +2019,24 @@
@Override
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- Message copy = new Message();
- copy.copyFrom(msg);
- mMessages.add(copy);
- if (msg.getCallback() != null) {
- msg.getCallback().run();
+ if (msg.getCallback() == null) {
+ Message copy = new Message();
+ copy.copyFrom(msg);
+ mMessages.add(copy);
+ } else {
+ if (SystemClock.uptimeMillis() >= uptimeMillis) {
+ msg.getCallback().run();
+ } else {
+ mPendingCallbacks.add(msg.getCallback());
+ }
msg.setCallback(null);
}
return super.sendMessageAtTime(msg, uptimeMillis);
}
+
+ private void runPendingCallbacks() {
+ mPendingCallbacks.forEach(Runnable::run);
+ mPendingCallbacks.clear();
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
index 9c61d95..9528a05 100644
--- a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
@@ -23,13 +23,13 @@
import android.util.Pair;
import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
import com.android.modules.utils.TypedXmlSerializer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
@@ -123,8 +123,24 @@
buildCacheQuotaHint("uuid2", 10, 250));
}
+ @Test
+ public void testReadInvalidInput() throws Exception {
+ String input = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
+ "<cache-info previousBytes=\"1000\">\n"
+ + "<quota/>\n"
+ + "</cache-info>\n";
+
+ try {
+ CacheQuotaStrategy.readFromXml(new ByteArrayInputStream(
+ input.getBytes("UTF-8")));
+ fail("Expected XML parsing exception");
+ } catch (XmlPullParserException e) {
+ // Expected XmlPullParserException exception
+ }
+ }
+
private CacheQuotaHint buildCacheQuotaHint(String volumeUuid, int uid, long quota) {
return new CacheQuotaHint.Builder()
.setVolumeUuid(volumeUuid).setUid(uid).setQuota(quota).build();
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 20f4bb6..601023f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -17683,4 +17683,145 @@
assertThat(mService.mNotificationList).isEmpty();
}
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testUnbundleNotification_ungrouped_restoresOriginalChannel() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+
+ // Post a single notification
+ final boolean hasOriginalSummary = false;
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ final String keyToUnbundle = r.getKey();
+ mService.addNotification(r);
+
+ // Classify notification into the NEWS bundle
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ // Check that the NotificationRecord channel is updated
+ assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+ // Check that the Notification mChannelId is not updated
+ assertThat(r.getNotification().getChannelId()).isEqualTo(TEST_CHANNEL_ID);
+
+ // Unbundle the notification
+ mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+
+ // Check that the original channel was restored
+ assertThat(r.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+ verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r), eq(hasOriginalSummary));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testUnbundleNotification_grouped_restoresOriginalChannel() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+
+ // Post grouped notifications
+ final String originalGroupName = "originalGroup";
+ final int summaryId = 0;
+ final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 1, originalGroupName, false);
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 2, originalGroupName, false);
+ mService.addNotification(r2);
+ final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
+ summaryId, originalGroupName, true);
+ mService.addNotification(summary);
+ final String originalGroupKey = summary.getGroupKey();
+ assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary);
+
+ // Classify a child notification into the NEWS bundle
+ final String keyToUnbundle = r1.getKey();
+ final boolean hasOriginalSummary = true;
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(r1.getSbn().getPackageName(), r1.getKey(), signals,
+ "", r1.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r1.applyAdjustments();
+ assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
+
+ // Unbundle the notification
+ mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+
+ // Check that the original channel was restored
+ assertThat(r1.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+ verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r1), eq(hasOriginalSummary));
+ }
+
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testUnbundleNotification_groupedSummaryCanceled_restoresOriginalChannel()
+ throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+
+ // Post grouped notifications
+ final String originalGroupName = "originalGroup";
+ final int summaryId = 0;
+ final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 1, originalGroupName, false);
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
+ summaryId + 2, originalGroupName, false);
+ mService.addNotification(r2);
+ final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
+ summaryId, originalGroupName, true);
+ mService.addNotification(summary);
+ final String originalGroupKey = summary.getGroupKey();
+ assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary);
+
+ // Classify a child notification into the NEWS bundle
+ final String keyToUnbundle = r1.getKey();
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(r1.getSbn().getPackageName(), r1.getKey(), signals,
+ "", r1.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r1.applyAdjustments();
+ assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
+
+ // Cancel original summary
+ final boolean hasOriginalSummary = false;
+ mService.mSummaryByGroupKey.remove(summary.getGroupKey());
+
+ // Unbundle the notification
+ mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+
+ // Check that the original channel was restored
+ assertThat(r1.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+ verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r1), eq(hasOriginalSummary));
+ }
+
}
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 fbd53f7..8e79514 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -66,7 +66,6 @@
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
-import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
import static com.android.server.notification.Flags.FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI;
import static com.android.server.notification.Flags.FLAG_PERSIST_INCOMPLETE_RESTORE_DATA;
import static com.android.server.notification.NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER;
@@ -155,7 +154,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
@@ -167,9 +165,6 @@
import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -204,6 +199,9 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@EnableFlags(FLAG_PERSIST_INCOMPLETE_RESTORE_DATA)
@@ -2640,6 +2638,35 @@
}
@Test
+ public void getPackagesBypassingDnd_multipleUsers() {
+ int uidUser1 = UserHandle.getUid(1, UID_P);
+ NotificationChannel channelUser1Bypass = new NotificationChannel("id11", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channelUser1Bypass.setBypassDnd(true);
+ NotificationChannel channelUser1NoBypass = new NotificationChannel("id12", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ channelUser1NoBypass.setBypassDnd(false);
+
+ int uidUser2 = UserHandle.getUid(2, UID_P);
+ NotificationChannel channelUser2Bypass = new NotificationChannel("id21", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channelUser2Bypass.setBypassDnd(true);
+
+ mHelper.createNotificationChannel(PKG_P, uidUser1, channelUser1Bypass, true,
+ /* hasDndAccess= */ true, uidUser1, false);
+ mHelper.createNotificationChannel(PKG_P, uidUser1, channelUser1NoBypass, true,
+ /* hasDndAccess= */ true, uidUser1, false);
+ mHelper.createNotificationChannel(PKG_P, uidUser2, channelUser2Bypass, true,
+ /* hasDndAccess= */ true, uidUser2, false);
+
+ assertThat(mHelper.getPackagesBypassingDnd(0)).isEmpty();
+ assertThat(mHelper.getPackagesBypassingDnd(1))
+ .containsExactly(new ZenBypassingApp(PKG_P, false));
+ assertThat(mHelper.getPackagesBypassingDnd(2))
+ .containsExactly(new ZenBypassingApp(PKG_P, true));
+ }
+
+ @Test
public void getPackagesBypassingDnd_oneChannelBypassing_groupBlocked() {
int uid = UID_N_MR1;
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
diff --git a/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java b/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
index 34f0c19..fe9f636 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/IntegrationTests.java
@@ -76,6 +76,7 @@
private ActivityTestRule<EmptyActivity> mEmptyActivityRule =
new ActivityTestRule<>(EmptyActivity.class, false , true);
+
@Before
public void setUp() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -163,7 +164,7 @@
// of that state.
for (int i = 0; i < uiStates.size(); i++) {
StateTracker.StateData stateData = uiStates.get(i);
- if (stateData.mWidgetCategory.equals(AppJankStats.ANIMATION)) {
+ if (stateData.mWidgetCategory.equals(AppJankStats.WIDGET_CATEGORY_ANIMATION)) {
assertNotEquals(Long.MAX_VALUE, stateData.mVsyncIdEnd);
}
}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
index 30c568b..c905957 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java
@@ -215,7 +215,8 @@
assertEquals(jankStats.getJankyFrameCount() * 2, pendingStat.getJankyFrames());
assertEquals(jankStats.getTotalFrameCount() * 2, pendingStat.getTotalFrames());
- int[] originalHistogramBuckets = jankStats.getFrameOverrunHistogram().getBucketCounters();
+ int[] originalHistogramBuckets =
+ jankStats.getRelativeFrameTimeHistogram().getBucketCounters();
int[] frameOverrunBuckets = pendingStat.getFrameOverrunBuckets();
for (int i = 0; i < frameOverrunBuckets.length; i++) {
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
index 0b4d97e..df92898 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
@@ -17,7 +17,7 @@
package android.app.jank.tests;
import android.app.jank.AppJankStats;
-import android.app.jank.FrameOverrunHistogram;
+import android.app.jank.RelativeFrameTimeHistogram;
public class JankUtils {
private static final int APP_ID = 25;
@@ -29,8 +29,8 @@
AppJankStats jankStats = new AppJankStats(
/*App Uid*/APP_ID,
/*Widget Id*/"test widget id",
- /*Widget Category*/AppJankStats.SCROLL,
- /*Widget State*/AppJankStats.SCROLLING,
+ /*Widget Category*/AppJankStats.WIDGET_CATEGORY_SCROLL,
+ /*Widget State*/AppJankStats.WIDGET_STATE_SCROLLING,
/*Total Frames*/100,
/*Janky Frames*/25,
getOverrunHistogram()
@@ -41,12 +41,12 @@
/**
* Returns a mock histogram to be used with an AppJankStats object.
*/
- public static FrameOverrunHistogram getOverrunHistogram() {
- FrameOverrunHistogram overrunHistogram = new FrameOverrunHistogram();
- overrunHistogram.addFrameOverrunMillis(-2);
- overrunHistogram.addFrameOverrunMillis(1);
- overrunHistogram.addFrameOverrunMillis(5);
- overrunHistogram.addFrameOverrunMillis(25);
+ public static RelativeFrameTimeHistogram getOverrunHistogram() {
+ RelativeFrameTimeHistogram overrunHistogram = new RelativeFrameTimeHistogram();
+ overrunHistogram.addRelativeFrameTimeMillis(-2);
+ overrunHistogram.addRelativeFrameTimeMillis(1);
+ overrunHistogram.addRelativeFrameTimeMillis(5);
+ overrunHistogram.addRelativeFrameTimeMillis(25);
return overrunHistogram;
}
}
diff --git a/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java b/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
index 5fff460..71796d6 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/TestWidget.java
@@ -45,8 +45,8 @@
*/
public void simulateAnimationStarting() {
if (jankTrackerCreated()) {
- mJankTracker.addUiState(AppJankStats.ANIMATION,
- Integer.toString(this.getId()), AppJankStats.ANIMATING);
+ mJankTracker.addUiState(AppJankStats.WIDGET_CATEGORY_ANIMATION,
+ Integer.toString(this.getId()), AppJankStats.WIDGET_STATE_ANIMATING);
}
}
@@ -55,8 +55,8 @@
*/
public void simulateAnimationEnding() {
if (jankTrackerCreated()) {
- mJankTracker.removeUiState(AppJankStats.ANIMATION,
- Integer.toString(this.getId()), AppJankStats.ANIMATING);
+ mJankTracker.removeUiState(AppJankStats.WIDGET_CATEGORY_ANIMATION,
+ Integer.toString(this.getId()), AppJankStats.WIDGET_STATE_ANIMATING);
}
}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 700856c..14c8de8 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -819,7 +819,7 @@
private List<Float> getExpectedFrameRateForCompatibility(int compatibility) {
assumeTrue("**** testSurfaceControlFrameRateCompatibility SKIPPED for compatibility "
+ compatibility,
- compatibility == Surface.FRAME_RATE_COMPATIBILITY_GTE);
+ compatibility == Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST);
Display display = getDisplay();
List<Float> expectedFrameRates = getRefreshRates(display.getMode(), display)
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
index 4d48276..f1d4dc6 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -85,7 +85,8 @@
@Test
public void testSurfaceControlFrameRateCompatibilityGte() throws InterruptedException {
GraphicsActivity activity = mActivityRule.getActivity();
- activity.testSurfaceControlFrameRateCompatibility(Surface.FRAME_RATE_COMPATIBILITY_GTE);
+ activity.testSurfaceControlFrameRateCompatibility(
+ Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST);
}
@Test
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 2e7b207..2db8b1e 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -29,6 +29,7 @@
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.wm.WindowingMode
import android.view.KeyEvent.KEYCODE_LEFT_BRACKET
+import android.view.KeyEvent.KEYCODE_MINUS
import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET
import android.view.KeyEvent.META_META_ON
import android.view.WindowInsets
@@ -160,10 +161,21 @@
?: error("Unable to find resource $MINIMIZE_BUTTON_VIEW\n")
}
- fun minimizeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice, isPip: Boolean = false) {
- val caption = getCaptionForTheApp(wmHelper, device)
- val minimizeButton = getMinimizeButtonForTheApp(caption)
- minimizeButton.click()
+ fun minimizeDesktopApp(
+ wmHelper: WindowManagerStateHelper,
+ device: UiDevice,
+ isPip: Boolean = false,
+ usingKeyboard: Boolean = false,
+ ) {
+ if (usingKeyboard) {
+ val keyEventHelper = KeyEventHelper(getInstrumentation())
+ keyEventHelper.press(KEYCODE_MINUS, META_META_ON)
+ } else {
+ val caption = getCaptionForTheApp(wmHelper, device)
+ val minimizeButton = getMinimizeButtonForTheApp(caption)
+ minimizeButton.click()
+ }
+
wmHelper
.StateSyncBuilder()
.withAppTransitionIdle()
@@ -226,8 +238,7 @@
toLeft: Boolean,
) {
val bracketKey = if (toLeft) KEYCODE_LEFT_BRACKET else KEYCODE_RIGHT_BRACKET
- keyEventHelper.actionDown(bracketKey, META_META_ON)
- keyEventHelper.actionUp(bracketKey, META_META_ON)
+ keyEventHelper.press(bracketKey, META_META_ON)
waitAndVerifySnapResize(wmHelper, context, toLeft)
}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
index ebd8cc3..55ed091 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
@@ -29,6 +29,10 @@
class KeyEventHelper(
private val instr: Instrumentation,
) {
+ fun press(keyCode: Int, metaState: Int = 0) {
+ actionDown(keyCode, metaState)
+ actionUp(keyCode, metaState)
+ }
fun actionDown(keyCode: Int, metaState: Int = 0, time: Long = SystemClock.uptimeMillis()) {
injectKeyEvent(ACTION_DOWN, keyCode, metaState, downTime = time, eventTime = time)
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 5e0f87f..60c4bf5 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -292,13 +292,12 @@
}
if (!hasData) {
const String8& srcName = file->getSourceFile();
- time_t fileModWhen;
- fileModWhen = getFileModDate(srcName.c_str());
- if (fileModWhen == (time_t) -1) { // file existence tested earlier,
- return false; // not expecting an error here
+ auto fileModWhen = getFileModDate(srcName.c_str());
+ if (fileModWhen == kInvalidModDate) { // file existence tested earlier,
+ return false; // not expecting an error here
}
-
- if (fileModWhen > entry->getModWhen()) {
+
+ if (toTimeT(fileModWhen) > entry->getModWhen()) {
// mark as deleted so add() will succeed
if (bundle->getVerbose()) {
printf(" (removing old '%s')\n", storageName.c_str());