diff options
543 files changed, 11371 insertions, 3842 deletions
diff --git a/Ravenwood.bp b/Ravenwood.bp index 74b34fbcf2a1..412f2b746887 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -104,6 +104,18 @@ genrule { ], } +genrule { + name: "framework-minus-apex.ravenwood.keep_all", + defaults: ["ravenwood-internal-only-visibility-genrule"], + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-minus-apex.ravenwood-base{hoststubgen_keep_all.txt}", + ], + out: [ + "hoststubgen_framework-minus-apex_keep_all.txt", + ], +} + java_library { name: "services.core-for-hoststubgen", installable: false, // host only jar. @@ -189,6 +201,18 @@ genrule { ], } +genrule { + name: "services.core.ravenwood.keep_all", + defaults: ["ravenwood-internal-only-visibility-genrule"], + cmd: "cp $(in) $(out)", + srcs: [ + ":services.core.ravenwood-base{hoststubgen_keep_all.txt}", + ], + out: [ + "hoststubgen_services.core_keep_all.txt", + ], +} + java_library { name: "services.core.ravenwood-jarjar", installable: false, diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 384d78618c8b..ff73a4922977 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -80,6 +80,7 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.os.storage.StorageManagerInternal; @@ -179,6 +180,8 @@ public class JobSchedulerService extends com.android.server.SystemService public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final boolean DEBUG_STANDBY = DEBUG || false; + public static final String TRACE_TRACK_NAME = "JobScheduler"; + /** The maximum number of jobs that we allow an app to schedule */ private static final int MAX_JOBS_PER_APP = 150; /** The number of the most recently completed jobs to keep track of for debugging purposes. */ @@ -4344,7 +4347,11 @@ public class JobSchedulerService extends com.android.server.SystemService final boolean wasConsideredCharging = isConsideredCharging(); mChargingPolicy = newPolicy; - + if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { + Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, + "CHARGING POLICY CHANGED#" + mChargingPolicy); + } if (isConsideredCharging() != wasConsideredCharging) { for (int c = mControllers.size() - 1; c >= 0; --c) { mControllers.get(c).onBatteryStateChangedLocked(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 39d50f53d2c4..d65a66c83fcc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -535,29 +535,17 @@ public final class JobServiceContext implements ServiceConnection { sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount()); final String sourcePackage = job.getSourcePackageName(); if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { - final String componentPackage = job.getServiceComponent().getPackageName(); - String traceTag = "*job*<" + job.getSourceUid() + ">" + sourcePackage; - if (!sourcePackage.equals(componentPackage)) { - traceTag += ":" + componentPackage; - } - traceTag += "/" + job.getServiceComponent().getShortClassName(); - if (!componentPackage.equals(job.serviceProcessName)) { - traceTag += "$" + job.serviceProcessName; - } - if (job.getNamespace() != null) { - traceTag += "@" + job.getNamespace(); - } - traceTag += "#" + job.getJobId(); - // Use the context's ID to distinguish traces since there'll only be one job // running per context. - Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", - traceTag, getId()); + Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, job.computeSystemTraceTag(), + getId()); } if (job.getAppTraceTag() != null) { // Use the job's ID to distinguish traces since the ID will be unique per app. - Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "JobScheduler", - job.getAppTraceTag(), job.getJobId()); + Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, + JobSchedulerService.TRACE_TRACK_NAME, job.getAppTraceTag(), + job.getJobId()); } try { mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid()); @@ -1605,12 +1593,12 @@ public final class JobServiceContext implements ServiceConnection { completedJob.getFilteredTraceTag(), completedJob.getFilteredDebugTags()); if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { - Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", - getId()); + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, getId()); } if (completedJob.getAppTraceTag() != null) { - Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "JobScheduler", - completedJob.getJobId()); + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, + JobSchedulerService.TRACE_TRACK_NAME, completedJob.getJobId()); } try { mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(), diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 7fca867356af..e3af1d894762 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -572,6 +572,9 @@ public final class JobStatus { /** The reason a job most recently went from ready to not ready. */ private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; + /** The system trace tag for this job. */ + private String mSystemTraceTag; + /** * Core constructor for JobStatus instances. All other ctors funnel down to this one. * @@ -1058,6 +1061,38 @@ public final class JobStatus { return job.getTraceTag(); } + /** Returns a trace tag using debug information provided by job scheduler service. */ + @NonNull + public String computeSystemTraceTag() { + // Guarded by JobSchedulerService.mLock, no need for synchronization. + if (mSystemTraceTag != null) { + return mSystemTraceTag; + } + + mSystemTraceTag = computeSystemTraceTagInner(); + return mSystemTraceTag; + } + + @NonNull + private String computeSystemTraceTagInner() { + final String componentPackage = getServiceComponent().getPackageName(); + StringBuilder traceTag = new StringBuilder(128); + traceTag.append("*job*<").append(sourceUid).append(">").append(sourcePackageName); + if (!sourcePackageName.equals(componentPackage)) { + traceTag.append(":").append(componentPackage); + } + traceTag.append("/").append(getServiceComponent().getShortClassName()); + if (!componentPackage.equals(serviceProcessName)) { + traceTag.append("$").append(serviceProcessName); + } + if (mNamespace != null && !mNamespace.trim().isEmpty()) { + traceTag.append("@").append(mNamespace); + } + traceTag.append("#").append(getJobId()); + + return traceTag.toString(); + } + /** Returns whether this job was scheduled by one app on behalf of another. */ public boolean isProxyJob() { return mIsProxyJob; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index c240b3f423a9..a1c72fb4c06c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -50,6 +50,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArraySet; @@ -2181,6 +2182,12 @@ public final class QuotaController extends StateController { } scheduleCutoff(); } + } else { + if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { + Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, + "QC/- " + mPkg); + } } } @@ -2720,6 +2727,11 @@ public final class QuotaController extends StateController { if (timeRemainingMs <= 50) { // Less than 50 milliseconds left. Start process of shutting down jobs. if (DEBUG) Slog.d(TAG, pkg + " has reached its quota."); + if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { + Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, + pkg + "#" + MSG_REACHED_TIME_QUOTA); + } mStateChangedListener.onControllerStateChanged( maybeUpdateConstraintForPkgLocked( sElapsedRealtimeClock.millis(), @@ -2748,6 +2760,11 @@ public final class QuotaController extends StateController { pkg.userId, pkg.packageName); if (timeRemainingMs <= 0) { if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota."); + if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { + Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, + pkg + "#" + MSG_REACHED_EJ_TIME_QUOTA); + } mStateChangedListener.onControllerStateChanged( maybeUpdateConstraintForPkgLocked( sElapsedRealtimeClock.millis(), @@ -2772,6 +2789,12 @@ public final class QuotaController extends StateController { Slog.d(TAG, pkg + " has reached its count quota."); } + if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { + Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, + pkg + "#" + MSG_REACHED_COUNT_QUOTA); + } + mStateChangedListener.onControllerStateChanged( maybeUpdateConstraintForPkgLocked( sElapsedRealtimeClock.millis(), @@ -2928,6 +2951,11 @@ public final class QuotaController extends StateController { } mTempAllowlistGraceCache.delete(uid); mTopAppGraceCache.delete(uid); + if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { + Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER, + JobSchedulerService.TRACE_TRACK_NAME, + "<" + uid + ">#" + MSG_END_GRACE_PERIOD); + } final ArraySet<String> packages = mService.getPackagesForUidLocked(uid); if (packages != null) { final int userId = UserHandle.getUserId(uid); diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java index 488292d68620..f726361effd6 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java @@ -292,13 +292,17 @@ public class AccessibilityNodeInfoDumper { int childCount = node.getChildCount(); for (int x = 0; x < childCount; x++) { AccessibilityNodeInfo childNode = node.getChild(x); - + if (childNode == null) { + continue; + } if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty() - || !safeCharSeqToString(childNode.getText()).isEmpty()) + || !safeCharSeqToString(childNode.getText()).isEmpty()) { return true; + } - if (childNafCheck(childNode)) + if (childNafCheck(childNode)) { return true; + } } return false; } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 0e201384812d..cd7f1e4c02c2 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -679,11 +679,13 @@ public class Dialog implements DialogInterface, Window.Callback, if (keyCode == KeyEvent.KEYCODE_ESCAPE) { if (mCancelable) { cancel(); - } else { + event.startTracking(); + return true; + } else if (mWindow.shouldCloseOnTouchOutside()) { dismiss(); + event.startTracking(); + return true; } - event.startTracking(); - return true; } return false; diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java index ef6982e4a13c..4ac40a1f77b2 100644 --- a/core/java/android/app/DreamManager.java +++ b/core/java/android/app/DreamManager.java @@ -18,6 +18,7 @@ package android.app; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; +import android.annotation.FlaggedApi; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; @@ -30,6 +31,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.service.dreams.DreamService; +import android.service.dreams.Flags; import android.service.dreams.IDreamManager; /** @@ -217,4 +219,19 @@ public class DreamManager { } return false; } + + /** + * Sets whether the dream is obscured by something. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED) + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void setDreamIsObscured(boolean isObscured) { + try { + mService.setDreamIsObscured(isObscured); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 9c806593b57d..8b19664c6abf 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5704,6 +5704,7 @@ public class Notification implements Parcelable p.headerless(resId == getBaseLayoutResource() || resId == getHeadsUpBaseLayoutResource() || resId == getCompactHeadsUpBaseLayoutResource() + || resId == getMessagingCompactHeadsUpLayoutResource() || resId == getMessagingLayoutResource() || resId == R.layout.notification_template_material_media); RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId); @@ -7294,6 +7295,10 @@ public class Notification implements Parcelable return R.layout.notification_template_material_compact_heads_up_base; } + private int getMessagingCompactHeadsUpLayoutResource() { + return R.layout.notification_template_material_messaging_compact_heads_up; + } + private int getBigBaseLayoutResource() { return R.layout.notification_template_material_big_base; } @@ -9166,10 +9171,78 @@ public class Notification implements Parcelable @Nullable @Override public RemoteViews makeCompactHeadsUpContentView() { - // TODO(b/336229954): Apply minimal HUN treatment to Messaging Notifications. - return makeHeadsUpContentView(false); + final boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY; + Icon conversationIcon = null; + Notification.Action remoteInputAction = null; + if (isConversationLayout) { + + conversationIcon = mShortcutIcon; + + // conversation icon is m + // Extract the conversation icon for one to one conversations from + // the latest incoming message since + // fixTitleAndTextExtras also uses it as data source for title and text + if (conversationIcon == null && !mIsGroupConversation) { + final Message message = findLatestIncomingMessage(); + if (message != null) { + final Person sender = message.mSender; + if (sender != null) { + conversationIcon = sender.getIcon(); + } + } + } + + if (Flags.compactHeadsUpNotificationReply()) { + // Get the first non-contextual inline reply action. + final List<Notification.Action> nonContextualActions = + mBuilder.getNonContextualActions(); + for (int i = 0; i < nonContextualActions.size(); i++) { + final Notification.Action action = nonContextualActions.get(i); + if (mBuilder.hasValidRemoteInput(action)) { + remoteInputAction = action; + break; + } + } + } + } + + // This method fills title and text + fixTitleAndTextExtras(mBuilder.mN.extras); + final StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP) + .highlightExpander(isConversationLayout) + .fillTextsFrom(mBuilder) + .hideTime(true) + .summaryText(""); + p.headerTextSecondary(p.mText); + TemplateBindResult bindResult = new TemplateBindResult(); + + RemoteViews contentView = mBuilder.applyStandardTemplate( + mBuilder.getMessagingCompactHeadsUpLayoutResource(), p, bindResult); + if (conversationIcon != null) { + contentView.setViewVisibility(R.id.icon, View.GONE); + contentView.setViewVisibility(R.id.conversation_icon, View.VISIBLE); + contentView.setBoolean(R.id.conversation_icon, "setApplyCircularCrop", true); + contentView.setImageViewIcon(R.id.conversation_icon, conversationIcon); + } + + if (remoteInputAction != null) { + contentView.setViewVisibility(R.id.reply_action_container, View.VISIBLE); + + final RemoteViews inlineReplyButton = + mBuilder.generateActionButton(remoteInputAction, false, p); + // Clear the drawable + inlineReplyButton.setInt(R.id.action0, "setBackgroundResource", 0); + inlineReplyButton.setTextViewText(R.id.action0, + mBuilder.mContext.getString(R.string.notification_compact_heads_up_reply)); + contentView.addView(R.id.reply_action_container, inlineReplyButton); + } else { + contentView.setViewVisibility(R.id.reply_action_container, View.GONE); + } + return contentView; } + /** * @hide */ diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 50c7b7f9798e..6ceae17d05fb 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -160,3 +160,10 @@ flag { description: "[Minimal HUN] Enables the compact heads up notification feature" bug: "270709257" } + +flag { + name: "compact_heads_up_notification_reply" + namespace: "systemui" + description: "[Minimal HUN] Enables the compact heads up notification reply capability for Conversation Notifications" + bug: "336229954" +}
\ No newline at end of file diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java index 5e1c1e053599..a37f51bb6944 100644 --- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java +++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java @@ -529,7 +529,6 @@ public final class OnDeviceIntelligenceManager { * {@link Bundle}s annotated with this type will be validated that they are in-effect read-only * when passed via Binder IPC. Following restrictions apply : * <ul> - * <li> No Nested Bundles are allowed.</li> * <li> {@link PersistableBundle}s are allowed.</li> * <li> Any primitive types or their collections can be added as usual.</li> * <li>IBinder objects should *not* be added.</li> diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index 3e23762a4f70..b29b52d67310 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -127,4 +127,15 @@ flag { metadata { purpose: PURPOSE_BUGFIX } +} + +flag { + name: "impulse_velocity_strategy_for_touch_navigation" + is_exported: true + namespace: "virtual_devices" + description: "Use impulse velocity strategy during conversion of touch navigation flings into Dpad events" + bug: "338426241" + metadata { + purpose: PURPOSE_BUGFIX + } }
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 4b579e7db9f8..1f6730b9e3f9 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2628,6 +2628,15 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + targetCode)) { + Slog.w(TAG, "Package requires development platform " + targetCode + + ", returning current version " + Build.VERSION.SDK_INT); + return Build.VERSION.SDK_INT; + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + targetCode @@ -2699,6 +2708,15 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + minCode)) { + Slog.w(TAG, "Package requires min development platform " + minCode + + ", returning current version " + Build.VERSION.SDK_INT); + return Build.VERSION.SDK_INT; + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + minCode diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index 153dd9a93490..c7403c0ea98c 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -316,6 +316,15 @@ public class FrameworkParsingPackageUtils { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + minCode)) { + Slog.w(TAG, "Parsed package requires min development platform " + minCode + + ", returning current version " + Build.VERSION.SDK_INT); + return input.success(Build.VERSION.SDK_INT); + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, @@ -368,19 +377,27 @@ public class FrameworkParsingPackageUtils { return input.success(targetVers); } + // If it's a pre-release SDK and the codename matches this platform, it + // definitely targets this SDK. + if (matchTargetCode(platformSdkCodenames, targetCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); + } + + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + targetCode)) { + Slog.w(TAG, "Parsed package requires development platform " + targetCode + + ", returning current version " + Build.VERSION.SDK_INT); + return input.success(Build.VERSION.SDK_INT); + } + try { if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } } catch (IllegalArgumentException e) { - // isAtMost() throws it when encountering an older SDK codename - return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage()); - } - - // If it's a pre-release SDK and the codename matches this platform, it - // definitely targets this SDK. - if (matchTargetCode(platformSdkCodenames, targetCode)) { - return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK"); } // Otherwise, we're looking at an incompatible pre-release SDK. diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 2b7d8f155bff..708f8a173aba 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -39,7 +39,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; import android.hardware.CameraExtensionSessionStats; -import android.hardware.CameraIdRemapping; import android.hardware.CameraStatus; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; @@ -1996,17 +1995,6 @@ public final class CameraManager { } /** - * Remaps Camera Ids in the CameraService. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA) - public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping) - throws CameraAccessException, SecurityException, IllegalArgumentException { - CameraManagerGlobal.get().remapCameraIds(cameraIdRemapping); - } - - /** * Injects session params into existing clients in the CameraService. * * @param cameraId The camera id of client to inject session params into. @@ -2117,13 +2105,6 @@ public final class CameraManager { private final Object mLock = new Object(); - /** - * The active CameraIdRemapping. This will be used to refresh the cameraIdRemapping state - * in the CameraService every time we connect to it, including when the CameraService - * Binder dies and we reconnect to it. - */ - @Nullable private CameraIdRemapping mActiveCameraIdRemapping; - // Access only through getCameraService to deal with binder death private ICameraService mCameraService; private boolean mHasOpenCloseListenerPermission = false; @@ -2275,41 +2256,6 @@ public final class CameraManager { } catch (RemoteException e) { // Camera service died in all probability } - - if (mActiveCameraIdRemapping != null) { - try { - cameraService.remapCameraIds(mActiveCameraIdRemapping); - } catch (ServiceSpecificException e) { - // Unexpected failure, ignore and continue. - Log.e(TAG, "Unable to remap camera Ids in the camera service"); - } catch (RemoteException e) { - // Camera service died in all probability - } - } - } - - /** Updates the cameraIdRemapping state in the CameraService. */ - public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping) - throws CameraAccessException, SecurityException { - synchronized (mLock) { - ICameraService cameraService = getCameraService(); - if (cameraService == null) { - throw new CameraAccessException( - CameraAccessException.CAMERA_DISCONNECTED, - "Camera service is currently unavailable."); - } - - try { - cameraService.remapCameraIds(cameraIdRemapping); - mActiveCameraIdRemapping = cameraIdRemapping; - } catch (ServiceSpecificException e) { - throw ExceptionUtils.throwAsPublicException(e); - } catch (RemoteException e) { - throw new CameraAccessException( - CameraAccessException.CAMERA_DISCONNECTED, - "Camera service is currently unavailable."); - } - } } /** Injects session params into an existing client for cameraid. */ diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 7754e328bbf9..de26384211a4 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2359,7 +2359,10 @@ public abstract class CameraMetadata<TKey> { * FPS.</p> * <p>If the session configuration is not supported, the AE mode reported in the * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p> - * <p>The application can observe the CapturerResult field + * <p>When this AE mode is enabled, the CaptureResult field + * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the + * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p> + * <p>The application can observe the CaptureResult field * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or * 'INACTIVE'.</p> * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 5765a73fb3c7..1460515c2438 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2819,6 +2819,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>When low light boost is enabled by setting the AE mode to * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light * boost when the light level threshold is exceeded.</p> + * <p>This field is present in the CaptureResult when the AE mode is set to + * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p> * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can * indicate when it is not being applied by returning 'INACTIVE'.</p> * <p>This key will be absent from the CaptureResult if AE mode is not set to diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index d340f3ff70bb..3f2ef84a4ef0 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -732,6 +732,10 @@ public class FaceManager implements BiometricAuthenticator { /** * Get statically configured sensor properties. + * @deprecated Generally unsafe to use, use + * {@link FaceManager#addAuthenticatorsRegisteredCallback} API instead. + * In most cases this method will work as expected, but during early boot up, it will be + * null/empty and there is no way for the caller to know when it's actual value is ready. * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 25bfb2ab91e7..2ded615580fd 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1189,6 +1189,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing /** * Get statically configured sensor properties. + * @deprecated Generally unsafe to use, use + * {@link FingerprintManager#addAuthenticatorsRegisteredCallback} API instead. + * In most cases this method will work as expected, but during early boot up, it will be + * null/empty and there is no way for the caller to know when it's actual value is ready. * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 594ec18d9996..334b2316b268 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -173,6 +173,12 @@ public class NetworkPolicyManager { public static final String FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY = "low_power_standby"; /** @hide */ public static final String FIREWALL_CHAIN_NAME_BACKGROUND = "background"; + /** @hide */ + public static final String FIREWALL_CHAIN_NAME_METERED_ALLOW = "metered_allow"; + /** @hide */ + public static final String FIREWALL_CHAIN_NAME_METERED_DENY_USER = "metered_deny_user"; + /** @hide */ + public static final String FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN = "metered_deny_admin"; private static final boolean ALLOW_PLATFORM_APP_POLICY = true; diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 05a3e182135c..fedc97dcf989 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -1387,7 +1387,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * @param scheme name or {@code null} if this is a relative Uri */ public Builder scheme(String scheme) { - this.scheme = scheme; + if (scheme != null) { + this.scheme = scheme.replaceAll("://", ""); + } else { + this.scheme = null; + } return this; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index d45a17f7194e..91ad22f51345 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -120,6 +120,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * StorageManager is the interface to the systems storage service. The storage @@ -2512,6 +2514,9 @@ public class StorageManager { return userId * PER_USER_RANGE + projectId; } + private static final Pattern PATTERN_USER_ID = Pattern.compile( + "(?i)^/storage/emulated/([0-9]+)"); + /** * Let StorageManager know that the quota type for a file on external storage should * be updated. Android tracks quotas for various media types. Consequently, this should be @@ -2541,26 +2546,35 @@ public class StorageManager { @SystemApi public void updateExternalStorageFileQuotaType(@NonNull File path, @QuotaType int quotaType) throws IOException { + if (!path.exists()) return; + long projectId; final String filePath = path.getCanonicalPath(); - int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE; - // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are also - // returned by enabling FLAG_INCLUDE_SHARED_PROFILE. - if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) { - volFlags |= FLAG_INCLUDE_SHARED_PROFILE; - } - final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags); - final StorageVolume volume = getStorageVolume(availableVolumes, path); - if (volume == null) { - Log.w(TAG, "Failed to update quota type for " + filePath); - return; - } - if (!volume.isEmulated()) { - // We only support quota tracking on emulated filesystems - return; + + final int userId; + final Matcher matcher = PATTERN_USER_ID.matcher(filePath); + if (matcher.find()) { + userId = Integer.parseInt(matcher.group(1)); + } else { // fallback + int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE; + // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are + // also returned by enabling FLAG_INCLUDE_SHARED_PROFILE. + if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) { + volFlags |= FLAG_INCLUDE_SHARED_PROFILE; + } + final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags); + final StorageVolume volume = getStorageVolume(availableVolumes, path); + if (volume == null) { + Log.w(TAG, "Failed to update quota type for " + filePath); + return; + } + if (!volume.isEmulated()) { + // We only support quota tracking on emulated filesystems + return; + } + userId = volume.getOwner().getIdentifier(); } - final int userId = volume.getOwner().getIdentifier(); if (userId < 0) { throw new IllegalStateException("Failed to update quota type for " + filePath); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 009713f0f942..4f5b67c34845 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11173,6 +11173,35 @@ public final class Settings { "visual_query_accessibility_detection_enabled"; /** + * Timeout to be used for unbinding to the configured remote + * {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService} if there are no + * requests in the queue. A value of -1 represents to never unbind. + * + * @hide + */ + public static final String ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS = + "on_device_intelligence_unbind_timeout_ms"; + + + /** + * Timeout that represents maximum idle time before which a callback should be populated. + * + * @hide + */ + public static final String ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS = + "on_device_intelligence_idle_timeout_ms"; + + /** + * Timeout to be used for unbinding to the configured remote + * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} if there + * are no requests in the queue. A value of -1 represents to never unbind. + * + * @hide + */ + public static final String ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS = + "on_device_inference_unbind_timeout_ms"; + + /** * Control whether Night display is currently activated. * @hide */ @@ -14909,6 +14938,17 @@ public final class Settings { public static final String DROPBOX_TAG_PREFIX = "dropbox:"; /** + * Lines of kernel logs to include with system crash/ANR/etc. reports, as a + * prefix of the dropbox tag of the report type. For example, + * "kernel_logs_for_system_server_anr" controls the lines of kernel logs + * captured with system server ANR reports. 0 to disable. + * + * @hide + */ + @Readable + public static final String ERROR_KERNEL_LOG_PREFIX = "kernel_logs_for_"; + + /** * Lines of logcat to include with system crash/ANR/etc. reports, as a * prefix of the dropbox tag of the report type. For example, * "logcat_for_system_server_anr" controls the lines of logcat captured diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING index d5ac7a7de461..2eb285dda0a2 100644 --- a/core/java/android/provider/TEST_MAPPING +++ b/core/java/android/provider/TEST_MAPPING @@ -8,6 +8,9 @@ } ] }, + { + "name": "CtsMediaProviderTestCases" + }, { "name": "CalendarProviderTests" }, diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 5f6bdbf193b9..38ab590b3c46 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -18,7 +18,7 @@ package android.service.dreams; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.dreams.Flags.dreamHandlesConfirmKeys; -import static android.service.dreams.Flags.dreamTracksFocus; +import static android.service.dreams.Flags.dreamHandlesBeingObscured; import android.annotation.FlaggedApi; import android.annotation.IdRes; @@ -571,15 +571,6 @@ public class DreamService extends Service implements Window.Callback { /** {@inheritDoc} */ @Override public void onWindowFocusChanged(boolean hasFocus) { - if (!dreamTracksFocus()) { - return; - } - - try { - mDreamManager.onDreamFocusChanged(hasFocus); - } catch (RemoteException ex) { - // system server died - } } /** {@inheritDoc} */ @@ -1737,7 +1728,7 @@ public class DreamService extends Service implements Window.Callback { @Override public void comeToFront() { - if (!dreamTracksFocus()) { + if (!dreamHandlesBeingObscured()) { return; } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index 85f0368a7b5c..cf98bfe05faf 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -48,5 +48,6 @@ interface IDreamManager { void setSystemDreamComponent(in ComponentName componentName); void registerDreamOverlayService(in ComponentName componentName); void startDreamActivity(in Intent intent); - void onDreamFocusChanged(in boolean hasFocus); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)") + oneway void setDreamIsObscured(in boolean isObscured); } diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig index a42eaff68917..54d950c18af8 100644 --- a/core/java/android/service/dreams/flags.aconfig +++ b/core/java/android/service/dreams/flags.aconfig @@ -39,8 +39,11 @@ flag { } flag { - name: "dream_tracks_focus" + name: "dream_handles_being_obscured" namespace: "communal" - description: "This flag enables the ability for dreams to track whether or not they have focus" - bug: "331798001" + description: "This flag enables the ability for dreams to handle being obscured" + bug: "337302237" + metadata { + purpose: PURPOSE_BUGFIX + } } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index d174bef90f9c..95897855586d 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -1025,7 +1025,8 @@ public abstract class WallpaperService extends Service { mWallpaperDimAmount = (!mShouldDimByDefault) ? mCustomDimAmount : Math.max(mDefaultDimAmount, mCustomDimAmount); - if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null + if (!ENABLE_WALLPAPER_DIMMING + || mBbqSurfaceControl == null || !mBbqSurfaceControl.isValid() || mWallpaperDimAmount == mPreviousWallpaperDimAmount) { return; } diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java index b9ab82cb63a9..4de7b62521d1 100644 --- a/core/java/android/tracing/perfetto/DataSource.java +++ b/core/java/android/tracing/perfetto/DataSource.java @@ -85,7 +85,7 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan new TracingContext<>(this, instanceIndex); fun.trace(ctx); - ctx.flush(); + nativeWritePackets(mNativeObj, ctx.getAndClearAllPendingTracePackets()); } while (nativePerfettoDsTraceIterateNext(mNativeObj)); } finally { nativePerfettoDsTraceIterateBreak(mNativeObj); @@ -130,7 +130,8 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan * @param params Params to initialize the datasource with. */ public void register(DataSourceParams params) { - nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy); + nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy, + params.willNotifyOnStop, params.noFlush); } /** @@ -163,8 +164,8 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan return this.createInstance(inputStream, instanceIndex); } - private static native void nativeRegisterDataSource( - long dataSourcePtr, int bufferExhaustedPolicy); + private static native void nativeRegisterDataSource(long dataSourcePtr, + int bufferExhaustedPolicy, boolean willNotifyOnStop, boolean noFlush); private static native long nativeCreate(DataSource thiz, String name); private static native void nativeFlushAll(long nativeDataSourcePointer); @@ -179,4 +180,6 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr); private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr); private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr); + + private static native void nativeWritePackets(long dataSourcePtr, byte[][] packetData); } diff --git a/core/java/android/tracing/perfetto/DataSourceParams.java b/core/java/android/tracing/perfetto/DataSourceParams.java index 6cd04e3d9a8b..e50f9d722fad 100644 --- a/core/java/android/tracing/perfetto/DataSourceParams.java +++ b/core/java/android/tracing/perfetto/DataSourceParams.java @@ -46,12 +46,67 @@ public class DataSourceParams { // after a while. public static final int PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT = 1; - public static DataSourceParams DEFAULTS = - new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP); + public static DataSourceParams DEFAULTS = new DataSourceParams.Builder().build(); - public DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy) { + private DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy, + boolean willNotifyOnStop, boolean noFlush) { this.bufferExhaustedPolicy = bufferExhaustedPolicy; + this.willNotifyOnStop = willNotifyOnStop; + this.noFlush = noFlush; } public final @PerfettoDsBufferExhausted int bufferExhaustedPolicy; + public final boolean willNotifyOnStop; + public final boolean noFlush; + + /** + * DataSource Parameters builder + * + * @hide + */ + public static final class Builder { + /** + * Specify behavior when running out of shared memory buffer space. + */ + public Builder setBufferExhaustedPolicy(@PerfettoDsBufferExhausted int value) { + this.mBufferExhaustedPolicy = value; + return this; + } + + /** + * If true, the data source is expected to ack the stop request through the + * NotifyDataSourceStopped() IPC. If false, the service won't wait for an ack. + * Set this parameter to false when dealing with potentially frozen producers + * that wouldn't be able to quickly ack the stop request. + * + * Default value: true + */ + public Builder setWillNotifyOnStop(boolean value) { + this.mWillNotifyOnStop = value; + return this; + } + + /** + * If true, the service won't emit flush requests for this data source. This + * allows the service to reduce the flush-related IPC traffic and better deal + * with frozen producers (see go/perfetto-frozen). + */ + public Builder setNoFlush(boolean value) { + this.mNoFlush = value; + return this; + } + + /** + * Build the DataSource parameters. + */ + public DataSourceParams build() { + return new DataSourceParams( + this.mBufferExhaustedPolicy, this.mWillNotifyOnStop, this.mNoFlush); + } + + private @PerfettoDsBufferExhausted int mBufferExhaustedPolicy = + PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP; + private boolean mWillNotifyOnStop = true; + private boolean mNoFlush = false; + } } diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java index 6b7df5441427..98cb4c838eed 100644 --- a/core/java/android/tracing/perfetto/TracingContext.java +++ b/core/java/android/tracing/perfetto/TracingContext.java @@ -59,19 +59,6 @@ public class TracingContext<DataSourceInstanceType extends DataSourceInstance, T } /** - * Forces a commit of the thread-local tracing data written so far to the - * service. This is almost never required (tracing data is periodically - * committed as trace pages are filled up) and has a non-negligible - * performance hit (requires an IPC + refresh of the current thread-local - * chunk). The only case when this should be used is when handling OnStop() - * asynchronously, to ensure sure that the data is committed before the - * Stop timeout expires. - */ - public void flush() { - nativeFlush(mDataSource.mNativeObj, getAndClearAllPendingTracePackets()); - } - - /** * Can optionally be used to store custom per-sequence * session data, which is not reset when incremental state is cleared * (e.g. configuration options). @@ -109,7 +96,7 @@ public class TracingContext<DataSourceInstanceType extends DataSourceInstance, T return incrementalState; } - private byte[][] getAndClearAllPendingTracePackets() { + protected byte[][] getAndClearAllPendingTracePackets() { byte[][] res = new byte[mTracePackets.size()][]; for (int i = 0; i < mTracePackets.size(); i++) { ProtoOutputStream tracePacket = mTracePackets.get(i); @@ -120,8 +107,6 @@ public class TracingContext<DataSourceInstanceType extends DataSourceInstance, T return res; } - private static native void nativeFlush(long dataSourcePtr, byte[][] packetData); - private static native Object nativeGetCustomTls(long nativeDsPtr); private static native void nativeSetCustomTls(long nativeDsPtr, Object tlsState); diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 9ff29a81a5c6..4837ee5a797c 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -26,11 +26,13 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; +import android.app.Activity; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.StrictMode; import android.os.SystemClock; @@ -299,6 +301,11 @@ public class GestureDetector { private VelocityTracker mVelocityTracker; /** + * Determines strategy for velocity calculation + */ + private @VelocityTracker.VelocityTrackerStrategy int mVelocityTrackerStrategy; + + /** * Consistency verifier for debugging purposes. */ private final InputEventConsistencyVerifier mInputEventConsistencyVerifier = @@ -347,17 +354,17 @@ public class GestureDetector { /** * Creates a GestureDetector with the supplied listener. - * This variant of the constructor should be used from a non-UI thread + * This variant of the constructor should be used from a non-UI thread * (as it allows specifying the Handler). - * + * * @param listener the listener invoked for all the callbacks, this must * not be null. * @param handler the handler to use * * @throws NullPointerException if {@code listener} is null. * - * @deprecated Use {@link #GestureDetector(android.content.Context, - * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead. + * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener, Handler)} + * instead. */ @Deprecated public GestureDetector(@NonNull OnGestureListener listener, @Nullable Handler handler) { @@ -367,15 +374,14 @@ public class GestureDetector { /** * Creates a GestureDetector with the supplied listener. * You may only use this constructor from a UI thread (this is the usual situation). - * @see android.os.Handler#Handler() - * + * @see Handler#Handler() + * * @param listener the listener invoked for all the callbacks, this must * not be null. - * + * * @throws NullPointerException if {@code listener} is null. * - * @deprecated Use {@link #GestureDetector(android.content.Context, - * android.view.GestureDetector.OnGestureListener)} instead. + * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener)} instead. */ @Deprecated public GestureDetector(@NonNull OnGestureListener listener) { @@ -384,10 +390,10 @@ public class GestureDetector { /** * Creates a GestureDetector with the supplied listener. - * You may only use this constructor from a {@link android.os.Looper} thread. - * @see android.os.Handler#Handler() + * You may only use this constructor from a {@link Looper} thread. + * @see Handler#Handler() * - * @param context An {@link android.app.Activity} or a {@link Context} created from + * @param context An {@link Activity} or a {@link Context} created from * {@link Context#createWindowContext(int, Bundle)} * @param listener the listener invoked for all the callbacks, this must * not be null. If the listener implements the {@link OnDoubleTapListener} or @@ -404,10 +410,10 @@ public class GestureDetector { /** * Creates a GestureDetector with the supplied listener that runs deferred events on the - * thread associated with the supplied {@link android.os.Handler}. - * @see android.os.Handler#Handler() + * thread associated with the supplied {@link Handler}. + * @see Handler#Handler() * - * @param context An {@link android.app.Activity} or a {@link Context} created from + * @param context An {@link Activity} or a {@link Context} created from * {@link Context#createWindowContext(int, Bundle)} * @param listener the listener invoked for all the callbacks, this must * not be null. If the listener implements the {@link OnDoubleTapListener} or @@ -419,6 +425,31 @@ public class GestureDetector { */ public GestureDetector(@Nullable @UiContext Context context, @NonNull OnGestureListener listener, @Nullable Handler handler) { + this(context, listener, handler, VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT); + } + + /** + * Creates a GestureDetector with the supplied listener that runs deferred events on the + * thread associated with the supplied {@link Handler}. + * @see Handler#Handler() + * + * @param context An {@link Activity} or a {@link Context} created from + * {@link Context#createWindowContext(int, Bundle)} + * @param listener the listener invoked for all the callbacks, this must + * not be null. If the listener implements the {@link OnDoubleTapListener} or + * {@link OnContextClickListener} then it will also be set as the listener for + * these callbacks (for example when using the {@link SimpleOnGestureListener}). + * @param handler the handler to use for running deferred listener events. + * @param velocityTrackerStrategy strategy to use for velocity calculation of scroll/fling + * events. + * + * @throws NullPointerException if {@code listener} is null. + * + * @hide + */ + public GestureDetector(@Nullable @UiContext Context context, + @NonNull OnGestureListener listener, @Nullable Handler handler, + @VelocityTracker.VelocityTrackerStrategy int velocityTrackerStrategy) { if (handler != null) { mHandler = new GestureHandler(handler); } else { @@ -431,15 +462,16 @@ public class GestureDetector { if (listener instanceof OnContextClickListener) { setContextClickListener((OnContextClickListener) listener); } + mVelocityTrackerStrategy = velocityTrackerStrategy; init(context); } - + /** * Creates a GestureDetector with the supplied listener that runs deferred events on the - * thread associated with the supplied {@link android.os.Handler}. - * @see android.os.Handler#Handler() + * thread associated with the supplied {@link Handler}. + * @see Handler#Handler() * - * @param context An {@link android.app.Activity} or a {@link Context} created from + * @param context An {@link Activity} or a {@link Context} created from * {@link Context#createWindowContext(int, Bundle)} * @param listener the listener invoked for all the callbacks, this must * not be null. @@ -547,7 +579,7 @@ public class GestureDetector { mCurrentMotionEvent = MotionEvent.obtain(ev); if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker = VelocityTracker.obtain(mVelocityTrackerStrategy); } mVelocityTracker.addMovement(ev); diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java index 9503f4925e14..8c14de6156a3 100644 --- a/core/java/android/view/ImeBackAnimationController.java +++ b/core/java/android/view/ImeBackAnimationController.java @@ -210,18 +210,9 @@ public class ImeBackAnimationController implements OnBackAnimationCallback { mInsetsController.setPredictiveBackImeHideAnimInProgress(true); notifyHideIme(); } - if (mStartRootScrollY != 0) { - // RootView is panned, ensure that it is scrolled back to the intended scroll position - if (triggerBack) { - // requesting ime as invisible - mInsetsController.setRequestedVisibleTypes(0, ime()); - // changes the animation state and notifies RootView of changed insets, which - // causes it to reset its scrollY to 0f (animated) - mInsetsController.onAnimationStateChanged(ime(), /*running*/ true); - } else { - // This causes RootView to update its scroll back to the panned position - mInsetsController.getHost().notifyInsetsChanged(); - } + if (mStartRootScrollY != 0 && !triggerBack) { + // This causes RootView to update its scroll back to the panned position + mInsetsController.getHost().notifyInsetsChanged(); } } @@ -237,6 +228,12 @@ public class ImeBackAnimationController implements OnBackAnimationCallback { // the IME away mInsetsController.getHost().getInputMethodManager() .notifyImeHidden(mInsetsController.getHost().getWindowToken(), statsToken); + + // requesting IME as invisible during post-commit + mInsetsController.setRequestedVisibleTypes(0, ime()); + // Changes the animation state. This also notifies RootView of changed insets, which causes + // it to reset its scrollY to 0f (animated) if it was panned + mInsetsController.onAnimationStateChanged(ime(), /*running*/ true); } private void reset() { diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index d31f82398cdf..27176a4c2094 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -264,7 +264,6 @@ public final class VelocityTracker { /** * Obtains a velocity tracker with the specified strategy. - * For testing and comparison purposes only. * * @param strategy The strategy Id, VELOCITY_TRACKER_STRATEGY_DEFAULT to use the default. * @return The velocity tracker. @@ -272,6 +271,9 @@ public final class VelocityTracker { * @hide */ public static VelocityTracker obtain(int strategy) { + if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) { + return obtain(); + } return new VelocityTracker(strategy); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 155c0537b5b5..1df851a29b07 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -427,12 +427,6 @@ public final class ViewRootImpl implements ViewParent, private static final long NANOS_PER_SEC = 1000000000; - // If the ViewRootImpl has been idle for more than 200ms, clear the preferred - // frame rate category and frame rate. - private static final int IDLE_TIME_MILLIS = 250; - - private static final long NANOS_PER_MILLI = 1_000_000; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -665,8 +659,6 @@ public final class ViewRootImpl implements ViewParent, private int mMinusOneFrameIntervalMillis = 0; // VRR interval between the previous and the frame before private int mMinusTwoFrameIntervalMillis = 0; - // VRR has the invalidation idle message been posted? - private boolean mInvalidationIdleMessagePosted = false; /** * Update the Choreographer's FrameInfo object with the timing information for the current @@ -4278,10 +4270,6 @@ public final class ViewRootImpl implements ViewParent, // when the values are applicable. if (mDrawnThisFrame) { mDrawnThisFrame = false; - if (!mInvalidationIdleMessagePosted) { - mInvalidationIdleMessagePosted = true; - mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS); - } setCategoryFromCategoryCounts(); updateInfrequentCount(); setPreferredFrameRate(mPreferredFrameRate); @@ -6523,8 +6511,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_WINDOW_TOUCH_MODE_CHANGED"; case MSG_KEEP_CLEAR_RECTS_CHANGED: return "MSG_KEEP_CLEAR_RECTS_CHANGED"; - case MSG_CHECK_INVALIDATION_IDLE: - return "MSG_CHECK_INVALIDATION_IDLE"; case MSG_REFRESH_POINTER_ICON: return "MSG_REFRESH_POINTER_ICON"; case MSG_TOUCH_BOOST_TIMEOUT: @@ -6789,30 +6775,6 @@ public final class ViewRootImpl implements ViewParent, mNumPausedForSync = 0; scheduleTraversals(); break; - case MSG_CHECK_INVALIDATION_IDLE: { - long delta; - if (mIsTouchBoosting || mIsFrameRateBoosting || mInsetsAnimationRunning) { - delta = 0; - } else { - delta = System.nanoTime() / NANOS_PER_MILLI - mLastUpdateTimeMillis; - } - if (delta >= IDLE_TIME_MILLIS) { - mFrameRateCategoryHighCount = 0; - mFrameRateCategoryHighHintCount = 0; - mFrameRateCategoryNormalCount = 0; - mFrameRateCategoryLowCount = 0; - mPreferredFrameRate = 0; - mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; - setPreferredFrameRateCategory(FRAME_RATE_CATEGORY_NO_PREFERENCE); - setPreferredFrameRate(0f); - mInvalidationIdleMessagePosted = false; - } else { - mInvalidationIdleMessagePosted = true; - mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, - IDLE_TIME_MILLIS - delta); - } - break; - } case MSG_TOUCH_BOOST_TIMEOUT: /** * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME). @@ -8609,48 +8571,55 @@ public final class ViewRootImpl implements ViewParent, private int mPendingKeyMetaState; - private final GestureDetector mGestureDetector = new GestureDetector(mContext, - new GestureDetector.OnGestureListener() { - @Override - public boolean onDown(@NonNull MotionEvent e) { - // This can be ignored since it's not clear what KeyEvent this will - // belong to. - return true; - } - - @Override - public void onShowPress(@NonNull MotionEvent e) { + private final GestureDetector mGestureDetector; - } + SyntheticTouchNavigationHandler() { + super(true); + int gestureDetectorVelocityStrategy = + android.companion.virtual.flags.Flags + .impulseVelocityStrategyForTouchNavigation() + ? VelocityTracker.VELOCITY_TRACKER_STRATEGY_IMPULSE + : VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT; + mGestureDetector = new GestureDetector(mContext, + new GestureDetector.OnGestureListener() { + @Override + public boolean onDown(@NonNull MotionEvent e) { + // This can be ignored since it's not clear what KeyEvent this will + // belong to. + return true; + } - @Override - public boolean onSingleTapUp(@NonNull MotionEvent e) { - dispatchTap(e.getEventTime()); - return true; - } + @Override + public void onShowPress(@NonNull MotionEvent e) { + } - @Override - public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2, - float distanceX, float distanceY) { - // Scroll doesn't translate to DPAD events so should be ignored. - return true; - } + @Override + public boolean onSingleTapUp(@NonNull MotionEvent e) { + dispatchTap(e.getEventTime()); + return true; + } - @Override - public void onLongPress(@NonNull MotionEvent e) { - // Long presses don't translate to DPAD events so should be ignored. - } + @Override + public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2, + float distanceX, float distanceY) { + // Scroll doesn't translate to DPAD events so should be ignored. + return true; + } - @Override - public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, - float velocityX, float velocityY) { - dispatchFling(velocityX, velocityY, e2.getEventTime()); - return true; - } - }); + @Override + public void onLongPress(@NonNull MotionEvent e) { + // Long presses don't translate to DPAD events so should be ignored. + } - SyntheticTouchNavigationHandler() { - super(true); + @Override + public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, + float velocityX, float velocityY) { + dispatchFling(velocityX, velocityY, e2.getEventTime()); + return true; + } + }, + /* handler= */ null, + gestureDetectorVelocityStrategy); } public void process(MotionEvent event) { @@ -13034,10 +13003,6 @@ public final class ViewRootImpl implements ViewParent, private void removeVrrMessages() { mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT); mHandler.removeMessages(MSG_FRAME_RATE_SETTING); - if (mInvalidationIdleMessagePosted) { - mInvalidationIdleMessagePosted = false; - mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE); - } } /** diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 7885cd95ca25..11ee286652a1 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -54,6 +54,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -431,6 +432,14 @@ public final class InputMethodInfo implements Parcelable { * @hide */ public InputMethodInfo(InputMethodInfo source) { + this(source, Collections.emptyList()); + } + + /** + * @hide + */ + public InputMethodInfo(@NonNull InputMethodInfo source, + @NonNull List<InputMethodSubtype> additionalSubtypes) { mId = source.mId; mSettingsActivityName = source.mSettingsActivityName; mLanguageSettingsActivityName = source.mLanguageSettingsActivityName; @@ -445,7 +454,19 @@ public final class InputMethodInfo implements Parcelable { mIsVrOnly = source.mIsVrOnly; mIsVirtualDeviceOnly = source.mIsVirtualDeviceOnly; mService = source.mService; - mSubtypes = source.mSubtypes; + if (additionalSubtypes.isEmpty()) { + mSubtypes = source.mSubtypes; + } else { + final ArrayList<InputMethodSubtype> subtypes = source.mSubtypes.toList(); + final int additionalSubtypeCount = additionalSubtypes.size(); + for (int i = 0; i < additionalSubtypeCount; ++i) { + final InputMethodSubtype additionalSubtype = additionalSubtypes.get(i); + if (!subtypes.contains(additionalSubtype)) { + subtypes.add(additionalSubtype); + } + } + mSubtypes = new InputMethodSubtypeArray(subtypes); + } mHandledConfigChanges = source.mHandledConfigChanges; mSupportsStylusHandwriting = source.mSupportsStylusHandwriting; mSupportsConnectionlessStylusHandwriting = source.mSupportsConnectionlessStylusHandwriting; diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index c243a22b27b1..e2d343f45e5d 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -25,6 +25,7 @@ import android.util.Slog; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.ArrayList; import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -163,6 +164,18 @@ public class InputMethodSubtypeArray { } /** + * @return A list of {@link InputMethodInfo} copied from this array. + */ + @NonNull + public ArrayList<InputMethodSubtype> toList() { + final ArrayList<InputMethodSubtype> list = new ArrayList<>(mCount); + for (int i = 0; i < mCount; ++i) { + list.add(get(i)); + } + return list; + } + + /** * Return the number of {@link InputMethodSubtype} objects. */ public int getCount() { diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig index 3a39631f8c81..503e542f715a 100644 --- a/core/java/android/widget/flags/notification_widget_flags.aconfig +++ b/core/java/android/widget/flags/notification_widget_flags.aconfig @@ -47,3 +47,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "messaging_child_request_layout" + namespace: "systemui" + description: "MessagingChild always needs to be measured during MessagingLinearLayout onMeasure." + bug: "324537506" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index 31e3a342d21a..af3d7eb87f71 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -56,6 +56,8 @@ import com.android.internal.R; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.policy.DecorView; +import java.io.Closeable; +import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.function.Consumer; @@ -568,6 +570,12 @@ public final class SplashScreenView extends FrameLayout { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); releaseAnimationSurfaceHost(); + if (mIconView instanceof ImageView imageView + && imageView.getDrawable() instanceof Closeable closeableDrawable) { + try { + closeableDrawable.close(); + } catch (IOException ignore) { } + } } @Override diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java index d0f3c3eb9455..6f7660a0fb3d 100644 --- a/core/java/android/window/TaskFragmentOperation.java +++ b/core/java/android/window/TaskFragmentOperation.java @@ -84,8 +84,8 @@ public final class TaskFragmentOperation implements Parcelable { /** * Sets the activity navigation to be isolated, where the activity navigation on the * TaskFragment is separated from the rest activities in the Task. Activities cannot be - * started on an isolated TaskFragment unless the activities are launched from the same - * TaskFragment or explicitly requested to. + * started on an isolated TaskFragment unless explicitly requested to. That said, new launched + * activities should be positioned as a sibling to the TaskFragment with higher z-ordering. */ public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11; @@ -149,6 +149,18 @@ public final class TaskFragmentOperation implements Parcelable { */ public static final int OP_TYPE_SET_DECOR_SURFACE_BOOSTED = 18; + /** + * Sets the TaskFragment to be pinned. + * <p> + * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other + * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the + * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or + * explicitly requested to. Non-embeddable activities are not restricted to. + * <p> + * See {@link #OP_TYPE_REORDER_TO_FRONT} on how to reorder a pinned TaskFragment to the top. + */ + public static final int OP_TYPE_SET_PINNED = 19; + @IntDef(prefix = { "OP_TYPE_" }, value = { OP_TYPE_UNKNOWN, OP_TYPE_CREATE_TASK_FRAGMENT, @@ -170,6 +182,7 @@ public final class TaskFragmentOperation implements Parcelable { OP_TYPE_SET_DIM_ON_TASK, OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH, OP_TYPE_SET_DECOR_SURFACE_BOOSTED, + OP_TYPE_SET_PINNED, }) @Retention(RetentionPolicy.SOURCE) public @interface OperationType {} diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java index 41b6d31661ce..a2e3d40e5c6b 100644 --- a/core/java/android/window/TaskSnapshot.java +++ b/core/java/android/window/TaskSnapshot.java @@ -16,6 +16,7 @@ package android.window; +import android.annotation.IntDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -32,6 +33,9 @@ import android.os.SystemClock; import android.view.Surface; import android.view.WindowInsetsController; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Represents a task snapshot. * @hide @@ -68,6 +72,21 @@ public class TaskSnapshot implements Parcelable { private final boolean mHasImeSurface; // Must be one of the named color spaces, otherwise, always use SRGB color space. private final ColorSpace mColorSpace; + private int mInternalReferences; + + /** This snapshot object is being broadcast. */ + public static final int REFERENCE_BROADCAST = 1; + /** This snapshot object is in the cache. */ + public static final int REFERENCE_CACHE = 1 << 1; + /** This snapshot object is being persistent. */ + public static final int REFERENCE_PERSIST = 1 << 2; + @IntDef(flag = true, prefix = { "REFERENCE_" }, value = { + REFERENCE_BROADCAST, + REFERENCE_CACHE, + REFERENCE_PERSIST + }) + @Retention(RetentionPolicy.SOURCE) + @interface ReferenceFlags {} public TaskSnapshot(long id, long captureTime, @NonNull ComponentName topActivityComponent, HardwareBuffer snapshot, @@ -296,7 +315,28 @@ public class TaskSnapshot implements Parcelable { + " mWindowingMode=" + mWindowingMode + " mAppearance=" + mAppearance + " mIsTranslucent=" + mIsTranslucent - + " mHasImeSurface=" + mHasImeSurface; + + " mHasImeSurface=" + mHasImeSurface + + " mInternalReferences=" + mInternalReferences; + } + + /** + * Adds a reference when the object is held somewhere. + * Only used in core. + */ + public synchronized void addReference(@ReferenceFlags int usage) { + mInternalReferences |= usage; + } + + /** + * Removes a reference when the object is not held from somewhere. The snapshot will be closed + * once the reference becomes zero. + * Only used in core. + */ + public synchronized void removeReference(@ReferenceFlags int usage) { + mInternalReferences &= ~usage; + if (mInternalReferences == 0 && mSnapshot != null && !mSnapshot.isClosed()) { + mSnapshot.close(); + } } public static final @NonNull Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() { diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java index 6864bf7bae16..8e18f842a055 100644 --- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java +++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java @@ -24,6 +24,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED; @@ -32,6 +33,7 @@ import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP; import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; @@ -48,7 +50,6 @@ import android.content.ComponentName; import android.content.Context; import android.provider.Settings; -import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; import com.android.internal.util.FrameworkStatsLog; @@ -248,6 +249,8 @@ public final class AccessibilityStatsLogUtils { } case HARDWARE: return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; + case QUICK_SETTINGS: + return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS; } return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; } diff --git a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java index 91b80ddaa836..24cd1c9cd7de 100644 --- a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java +++ b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java @@ -52,8 +52,14 @@ final class ImeTracingPerfettoImpl extends ImeTracing { ImeTracingPerfettoImpl() { Producer.init(InitArguments.DEFAULTS); - mDataSource.register( - new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)); + DataSourceParams params = + new DataSourceParams.Builder() + .setBufferExhaustedPolicy( + PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT) + .setNoFlush(true) + .setWillNotifyOnStop(false) + .build(); + mDataSource.register(params); } diff --git a/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java new file mode 100644 index 000000000000..92d453ba0e9f --- /dev/null +++ b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import android.annotation.BinderThread; +import android.view.autofill.AutofillId; +import android.view.inputmethod.InlineSuggestionsRequest; + +/** + * An internal interface that mirrors {@link IInlineSuggestionsRequestCallback}. + * + * <p>This interface is used to forward incoming IPCs from + * {@link com.android.server.inputmethod.AutofillSuggestionsController} to + * {@link com.android.server.autofill.AutofillInlineSuggestionsRequestSession}.</p> + */ +public interface InlineSuggestionsRequestCallback { + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsUnsupported()}. + */ + @BinderThread + void onInlineSuggestionsUnsupported(); + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest( + * InlineSuggestionsRequest, IInlineSuggestionsResponseCallback)}. + */ + @BinderThread + void onInlineSuggestionsRequest(InlineSuggestionsRequest request, + IInlineSuggestionsResponseCallback callback); + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInput(AutofillId)}. + */ + @BinderThread + void onInputMethodStartInput(AutofillId imeFieldId); + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodShowInputRequested(boolean)}. + */ + @BinderThread + void onInputMethodShowInputRequested(boolean requestResult); + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInputView()}. + */ + @BinderThread + void onInputMethodStartInputView(); + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInputView()}. + */ + @BinderThread + void onInputMethodFinishInputView(); + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInput()}. + */ + @BinderThread + void onInputMethodFinishInput(); + + /** + * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsSessionInvalidated()}. + */ + @BinderThread + void onInlineSuggestionsSessionInvalidated(); +} diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index ee33eb4f014b..37b72880dd0c 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -131,8 +131,13 @@ public class PerfettoProtoLogImpl implements IProtoLog { Runnable cacheUpdater ) { Producer.init(InitArguments.DEFAULTS); - mDataSource.register(new DataSourceParams( - DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)); + DataSourceParams params = + new DataSourceParams.Builder() + .setBufferExhaustedPolicy( + DataSourceParams + .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT) + .build(); + mDataSource.register(params); this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; this.mViewerConfigReader = viewerConfigReader; this.mLogGroups = logGroups; @@ -186,8 +191,6 @@ public class PerfettoProtoLogImpl implements IProtoLog { } os.end(outProtologViewerConfigToken); - - ctx.flush(); } catch (IOException e) { Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e); } diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java new file mode 100644 index 000000000000..1340156321b2 --- /dev/null +++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.ravenwood; + +/** + * Class to interact with the Ravenwood environment. + */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass +public class RavenwoodEnvironment { + private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment(); + + private RavenwoodEnvironment() { + } + + /** + * @return the singleton instance. + */ + public static RavenwoodEnvironment getInstance() { + return sInstance; + } + + /** + * USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment. + * + * <p>Using this allows code to behave differently on a real device and on Ravenwood, but + * generally speaking, that's a bad idea because we want the test target code to behave + * differently. + * + * <p>This should be only used when different behavior is absolutely needed. + * + * <p>If someone needs it without having access to the SDK, the following hack would work too. + * <code>System.getProperty("java.class.path").contains("ravenwood")</code> + */ + @android.ravenwood.annotation.RavenwoodReplace + public boolean isRunningOnRavenwood() { + return false; + } + + public boolean isRunningOnRavenwood$ravenwood() { + return true; + } +} diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java index e07acac52f2c..3d8237ea5389 100644 --- a/core/java/com/android/internal/widget/MessagingLinearLayout.java +++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java @@ -16,6 +16,8 @@ package com.android.internal.widget; +import static android.widget.flags.Flags.messagingChildRequestLayout; + import android.annotation.Nullable; import android.annotation.Px; import android.content.Context; @@ -92,6 +94,10 @@ public class MessagingLinearLayout extends ViewGroup { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.hide = true; + // Child always needs to be measured to calculate hide property correctly in onMeasure. + if (messagingChildRequestLayout()) { + child.requestLayout(); + } if (child instanceof MessagingChild) { MessagingChild messagingChild = (MessagingChild) child; // Whenever we encounter the message first, it's always first in the layout diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java index 4031b2fd335c..0f4615a12ea2 100644 --- a/core/java/com/android/internal/widget/NotificationRowIconView.java +++ b/core/java/com/android/internal/widget/NotificationRowIconView.java @@ -16,18 +16,27 @@ package com.android.internal.widget; +import android.annotation.Nullable; import android.app.Flags; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.util.AttributeSet; +import android.view.RemotableViewMethod; import android.widget.RemoteViews; -import androidx.annotation.Nullable; - /** * An image view that holds the icon displayed on the left side of a notification row. */ @RemoteViews.RemoteView public class NotificationRowIconView extends CachingIconView { + private boolean mApplyCircularCrop = false; + public NotificationRowIconView(Context context) { super(context); } @@ -57,4 +66,82 @@ public class NotificationRowIconView extends CachingIconView { super.onFinishInflate(); } + + @Nullable + @Override + Drawable loadSizeRestrictedIcon(@Nullable Icon icon) { + final Drawable original = super.loadSizeRestrictedIcon(icon); + final Drawable result; + if (mApplyCircularCrop) { + result = makeCircularDrawable(original); + } else { + result = original; + } + + return result; + } + + /** + * Enables circle crop that makes given image circular + */ + @RemotableViewMethod(asyncImpl = "setApplyCircularCropAsync") + public void setApplyCircularCrop(boolean applyCircularCrop) { + mApplyCircularCrop = applyCircularCrop; + } + + /** + * Async version of {@link NotificationRowIconView#setApplyCircularCrop} + */ + public Runnable setApplyCircularCropAsync(boolean applyCircularCrop) { + mApplyCircularCrop = applyCircularCrop; + return () -> { + }; + } + + @Nullable + private Drawable makeCircularDrawable(@Nullable Drawable original) { + if (original == null) { + return original; + } + + final Bitmap source = drawableToBitmap(original); + + int size = Math.min(source.getWidth(), source.getHeight()); + + Bitmap squared = Bitmap.createScaledBitmap(source, size, size, /* filter= */ false); + Bitmap result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + + final Canvas canvas = new Canvas(result); + final Paint paint = new Paint(); + paint.setShader( + new BitmapShader(squared, BitmapShader.TileMode.CLAMP, + BitmapShader.TileMode.CLAMP)); + paint.setAntiAlias(true); + float radius = size / 2f; + canvas.drawCircle(radius, radius, radius, paint); + return new BitmapDrawable(getResources(), result); + } + + private static Bitmap drawableToBitmap(Drawable drawable) { + if (drawable instanceof BitmapDrawable bitmapDrawable) { + final Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap.getConfig() == Bitmap.Config.HARDWARE) { + return bitmap.copy(Bitmap.Config.ARGB_8888, false); + } else { + return bitmap; + } + } + + int width = drawable.getIntrinsicWidth(); + width = width > 0 ? width : 1; + int height = drawable.getIntrinsicHeight(); + height = height > 0 ? height : 1; + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + return bitmap; + } } diff --git a/core/java/com/android/internal/widget/TEST_MAPPING b/core/java/com/android/internal/widget/TEST_MAPPING new file mode 100644 index 000000000000..91cecfda7a16 --- /dev/null +++ b/core/java/com/android/internal/widget/TEST_MAPPING @@ -0,0 +1,19 @@ +{ + // v2/sysui/suite/test-mapping-sysui-screenshot-test + "sysui-screenshot-test": [ + { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + } + ] +}
\ No newline at end of file diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp index f82ebfe8c947..17129d8913d7 100644 --- a/core/jni/android_tracing_PerfettoDataSource.cpp +++ b/core/jni/android_tracing_PerfettoDataSource.cpp @@ -244,8 +244,8 @@ static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&nativeDestroy)); } -void nativeFlush(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) { - ALOG(LOG_DEBUG, LOG_TAG, "nativeFlush(%p)", (void*)ds_ptr); +void nativeWritePackets(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) { + ALOG(LOG_DEBUG, LOG_TAG, "nativeWritePackets(%p)", (void*)ds_ptr); sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(ds_ptr); datasource->WritePackets(env, packets); } @@ -256,10 +256,12 @@ void nativeFlushAll(JNIEnv* env, jclass clazz, jlong ptr) { } void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr, - jint buffer_exhausted_policy) { + jint buffer_exhausted_policy, jboolean will_notify_on_stop, + jboolean no_flush) { sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr); struct PerfettoDsParams params = PerfettoDsParamsDefault(); + params.will_notify_on_stop = will_notify_on_stop; params.buffer_exhausted_policy = (PerfettoDsBufferExhaustedPolicy)buffer_exhausted_policy; params.user_arg = reinterpret_cast<void*>(datasource.get()); @@ -325,13 +327,15 @@ void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr, datasource_instance->onStart(env); }; - params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, void* inst_ctx, - struct PerfettoDsOnFlushArgs*) { - JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6); + if (!no_flush) { + params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, + void* inst_ctx, struct PerfettoDsOnFlushArgs*) { + JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6); - auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx); - datasource_instance->onFlush(env); - }; + auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx); + datasource_instance->onFlush(env); + }; + } params.on_stop_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* user_arg, void* inst_ctx, struct PerfettoDsOnStopArgs*) { @@ -422,7 +426,7 @@ const JNINativeMethod gMethods[] = { (void*)nativeCreate}, {"nativeFlushAll", "(J)V", (void*)nativeFlushAll}, {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer}, - {"nativeRegisterDataSource", "(JI)V", (void*)nativeRegisterDataSource}, + {"nativeRegisterDataSource", "(JIZZ)V", (void*)nativeRegisterDataSource}, {"nativeGetPerfettoInstanceLocked", "(JI)Landroid/tracing/perfetto/DataSourceInstance;", (void*)nativeGetPerfettoInstanceLocked}, {"nativeReleasePerfettoInstanceLocked", "(JI)V", @@ -431,11 +435,12 @@ const JNINativeMethod gMethods[] = { {"nativePerfettoDsTraceIterateBegin", "(J)Z", (void*)nativePerfettoDsTraceIterateBegin}, {"nativePerfettoDsTraceIterateNext", "(J)Z", (void*)nativePerfettoDsTraceIterateNext}, {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak}, - {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}}; + {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}, + + {"nativeWritePackets", "(J[[B)V", (void*)nativeWritePackets}}; const JNINativeMethod gMethodsTracingContext[] = { /* name, signature, funcPtr */ - {"nativeFlush", "(J[[B)V", (void*)nativeFlush}, {"nativeGetCustomTls", "(J)Ljava/lang/Object;", (void*)nativeGetCustomTls}, {"nativeGetIncrementalState", "(J)Ljava/lang/Object;", (void*)nativeGetIncrementalState}, {"nativeSetCustomTls", "(JLjava/lang/Object;)V", (void*)nativeSetCustomTls}, diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp index bf2fddab3d41..acef609448ad 100644 --- a/core/jni/platform/host/HostRuntime.cpp +++ b/core/jni/platform/host/HostRuntime.cpp @@ -330,7 +330,7 @@ static void init_keyboard(JNIEnv* env, const vector<string>& keyboardPaths) { InputDeviceInfo info = InputDeviceInfo(); info.initialize(keyboardId, 0, 0, InputDeviceIdentifier(), "keyboard " + std::to_string(keyboardId), true, false, - ui::ADISPLAY_ID_DEFAULT); + ui::LogicalDisplayId::DEFAULT); info.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); info.setKeyCharacterMap(*charMap); diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c92435f61ab1..654d83c827c9 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -607,7 +607,7 @@ message ImeInsetsSourceProviderProto { optional InsetsSourceProviderProto insets_source_provider = 1; optional WindowStateProto ime_target_from_ime = 2; - optional bool is_ime_layout_drawn = 3; + optional bool is_ime_layout_drawn = 3 [deprecated=true]; } message BackNavigationProto { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 70d923b8a9bb..8541704ecfb9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -299,6 +299,9 @@ <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" /> + <protected-broadcast android:name="android.hardware.hdmi.action.OSD_MESSAGE" /> + <protected-broadcast android:name="android.hardware.hdmi.action.ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI" /> + <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" /> <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" /> <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED" /> diff --git a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml index 7c45c20a758b..c692967d36b3 100644 --- a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml +++ b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml @@ -22,11 +22,11 @@ <path android:fillColor="@android:color/white" android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" /> - <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z"> + <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z"> <!-- 1 bar. move to higher ground. --> <path android:name="ic_signal_cellular_1_4_bar" android:fillColor="@android:color/white" - android:pathData="M6,0 H11 V20 H6 z" /> + android:pathData="M0,0 H11 V24 H0 z" /> </clip-path> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml index 02b646d310e5..b01c26972ac8 100644 --- a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml +++ b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml @@ -22,11 +22,11 @@ <path android:fillColor="@android:color/white" android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" /> - <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z"> + <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z"> <!-- 1 bar. might have to call you back. --> <path android:name="ic_signal_cellular_1_5_bar" android:fillColor="@android:color/white" - android:pathData="M6,0 H12 V20 H6 z" /> + android:pathData="M0,0 H12 V24 H0 z" /> </clip-path> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml index 514d1690abcf..982623d41060 100644 --- a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml +++ b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml @@ -22,11 +22,11 @@ <path android:fillColor="@android:color/white" android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" /> - <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z"> + <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z"> <!-- 2 bars. 2 out of 4 ain't bad. --> <path android:name="ic_signal_cellular_2_4_bar" android:fillColor="@android:color/white" - android:pathData="M6,0 H14 V20 H6 z" /> + android:pathData="M0,0 H14 V24 H0 z" /> </clip-path> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml index a97f771a6632..75daadd213dc 100644 --- a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml +++ b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml @@ -23,11 +23,11 @@ <path android:fillColor="@android:color/white" android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" /> - <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z"> + <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z"> <!-- 2 bars. hanging in there. --> <path android:name="ic_signal_cellular_2_5_bar" android:fillColor="@android:color/white" - android:pathData="M6,0 H14 V20 H6 z" /> + android:pathData="M0,0 H14 V24 H0 z" /> </clip-path> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml index 1bacf4ad678f..4e4bea397d26 100644 --- a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml +++ b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml @@ -22,11 +22,11 @@ <path android:fillColor="@android:color/white" android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" /> - <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z"> + <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z"> <!-- 3 bars. quite nice. --> <path android:name="ic_signal_cellular_3_4_bar" android:fillColor="@android:color/white" - android:pathData="M6,0 H17 V20 H6 z" /> + android:pathData="M0,0 H17 V24 H0 z" /> </clip-path> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml index 2789d3e9305c..9a98c2982574 100644 --- a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml +++ b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml @@ -22,11 +22,11 @@ <path android:fillColor="@android:color/white" android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" /> - <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z"> + <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z"> <!-- 3 bars. not great, not terrible. --> <path android:name="ic_signal_cellular_3_5_bar" android:fillColor="@android:color/white" - android:pathData="M6,0 H16 V20 H6 z" /> + android:pathData="M0,0 H16 V24 H0 z" /> </clip-path> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml index 8286dbb5576f..2a37d011c650 100644 --- a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml +++ b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml @@ -22,11 +22,11 @@ <path android:fillColor="@android:color/white" android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" /> - <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z"> + <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z"> <!-- 4 bars. extremely respectable. --> <path android:name="ic_signal_cellular_4_5_bar" android:fillColor="@android:color/white" - android:pathData="M6,0 H18 V20 H6 z" /> + android:pathData="M0,0 H18 V24 H0 z" /> </clip-path> </vector>
\ No newline at end of file diff --git a/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml new file mode 100644 index 000000000000..3b288d7038e1 --- /dev/null +++ b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml @@ -0,0 +1,104 @@ +<?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 + --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_header_height" + android:clipChildren="false" + android:tag="compactMessagingHUN" + android:gravity="center_vertical" + android:theme="@style/Theme.DeviceDefault.Notification" + android:importantForAccessibility="no"> + <com.android.internal.widget.NotificationRowIconView + android:id="@+id/icon" + android:layout_width="@dimen/notification_icon_circle_size" + android:layout_height="@dimen/notification_icon_circle_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_icon_circle_start" + android:background="@drawable/notification_icon_circle" + android:padding="@dimen/notification_icon_circle_padding" + android:maxDrawableWidth="@dimen/notification_icon_circle_size" + android:maxDrawableHeight="@dimen/notification_icon_circle_size" + /> + <com.android.internal.widget.NotificationRowIconView + android:id="@+id/conversation_icon" + android:layout_width="@dimen/notification_icon_circle_size" + android:layout_height="@dimen/notification_icon_circle_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_icon_circle_start" + android:maxDrawableWidth="@dimen/notification_icon_circle_size" + android:maxDrawableHeight="@dimen/notification_icon_circle_size" + android:scaleType="centerCrop" + android:importantForAccessibility="no" + /> + <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + android:focusable="false" + /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginStart="@dimen/notification_content_margin_start" + android:orientation="horizontal" + > + <NotificationTopLineView + android:id="@+id/notification_top_line" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_centerVertical="true" + android:layout_weight="1" + android:clipChildren="false" + android:gravity="center_vertical" + android:theme="@style/Theme.DeviceDefault.Notification" + > + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:ellipsize="end" + android:fadingEdge="horizontal" + android:singleLine="true" + android:textAlignment="viewStart" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + /> + <include layout="@layout/notification_top_line_views" /> + </NotificationTopLineView> + <FrameLayout + android:id="@+id/reply_action_container" + android:layout_width="wrap_content" + android:layout_height="@dimen/notification_action_list_height" + android:gravity="center_vertical" + android:orientation="horizontal" /> + <FrameLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + /> + </FrameLayout> + </LinearLayout> +</FrameLayout> diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml index 2c35c9b888cf..78299ab0ea99 100644 --- a/core/res/res/layout/side_fps_toast.xml +++ b/core/res/res/layout/side_fps_toast.xml @@ -25,6 +25,7 @@ android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="6" + android:paddingBottom="10dp" android:text="@string/fp_power_button_enrollment_title" android:textColor="@color/side_fps_text_color" android:paddingLeft="20dp"/> @@ -36,6 +37,7 @@ android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="3" + android:paddingBottom="10dp" android:text="@string/fp_power_button_enrollment_button_text" style="?android:attr/buttonBarNegativeButtonStyle" android:textColor="@color/side_fps_button_color" diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml index 31acd9af164c..52662149b23a 100644 --- a/core/res/res/values-watch/config.xml +++ b/core/res/res/values-watch/config.xml @@ -93,4 +93,7 @@ <!-- If this is true, allow wake from theater mode from motion. --> <bool name="config_allowTheaterModeWakeFromMotion">true</bool> + + <!-- True if the device supports system decorations on secondary displays. --> + <bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 37d39a752c65..5fa13bab7b0c 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1346,6 +1346,51 @@ <attr name="materialColorTertiary" format="color"/> <!-- The error color for the app, intended to draw attention to error conditions. @hide --> <attr name="materialColorError" format="color"/> + + <!-- System Custom Tokens--> + <!-- @hide --> + <attr name="customColorWidgetBackground" format="color"/> + <!-- @hide --> + <attr name="customColorClockHour" format="color"/> + <!-- @hide --> + <attr name="customColorClockMinute" format="color"/> + <!-- @hide --> + <attr name="customColorClockSecond" format="color"/> + <!-- @hide --> + <attr name="customColorThemeApp" format="color"/> + <!-- @hide --> + <attr name="customColorOnThemeApp" format="color"/> + <!-- @hide --> + <attr name="customColorThemeAppRing" format="color"/> + <!-- @hide --> + <attr name="customColorOnThemeAppRing" format="color"/> + <!-- @hide --> + <attr name="customColorBrandA" format="color"/> + <!-- @hide --> + <attr name="customColorBrandB" format="color"/> + <!-- @hide --> + <attr name="customColorBrandC" format="color"/> + <!-- @hide --> + <attr name="customColorBrandD" format="color"/> + <!-- @hide --> + <attr name="customColorUnderSurface" format="color"/> + <!-- @hide --> + <attr name="customColorShadeActive" format="color"/> + <!-- @hide --> + <attr name="customColorOnShadeActive" format="color"/> + <!-- @hide --> + <attr name="customColorOnShadeActiveVariant" format="color"/> + <!-- @hide --> + <attr name="customColorShadeInactive" format="color"/> + <!-- @hide --> + <attr name="customColorOnShadeInactive" format="color"/> + <!-- @hide --> + <attr name="customColorOnShadeInactiveVariant" format="color"/> + <!-- @hide --> + <attr name="customColorShadeDisabled" format="color"/> + <!-- @hide --> + <attr name="customColorOverviewBackground" format="color"/> + </declare-styleable> <!-- **************************************************************** --> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index e6719195565e..5e039b5e958d 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -581,6 +581,51 @@ <color name="system_on_tertiary_fixed">#FFFFFF</color> <color name="system_on_tertiary_fixed_variant">#FFFFFF</color> + <!--Colors used in Android system, from design system. These values can be overlaid at runtime + by OverlayManager RROs.--> + <color name="system_widget_background_light">#EEF0FF</color> + <color name="system_clock_hour_light">#1D2435</color> + <color name="system_clock_minute_light">#20386A</color> + <color name="system_clock_second_light">#000000</color> + <color name="system_theme_app_light">#2F4578</color> + <color name="system_on_theme_app_light">#D6DFFF</color> + <color name="system_theme_app_ring_light">#94AAE4</color> + <color name="system_on_theme_app_ring_light">#FDD7FA</color> + <color name="system_brand_a_light">#3A5084</color> + <color name="system_brand_b_light">#6E7488</color> + <color name="system_brand_c_light">#6076AC</color> + <color name="system_brand_d_light">#8C6D8C</color> + <color name="system_under_surface_light">#000000</color> + <color name="system_shade_active_light">#D9E2FF</color> + <color name="system_on_shade_active_light">#152E60</color> + <color name="system_on_shade_active_variant_light">#2F4578</color> + <color name="system_shade_inactive_light">#2F3036</color> + <color name="system_on_shade_inactive_light">#E1E2EC</color> + <color name="system_on_shade_inactive_variant_light">#C5C6D0</color> + <color name="system_shade_disabled_light">#0C0E13</color> + <color name="system_overview_background_light">#50525A</color> + <color name="system_widget_background_dark">#152E60</color> + <color name="system_clock_hour_dark">#9AA0B6</color> + <color name="system_clock_minute_dark">#D8E1FF</color> + <color name="system_clock_second_dark">#FFFFFF</color> + <color name="system_theme_app_dark">#D9E2FF</color> + <color name="system_on_theme_app_dark">#304679</color> + <color name="system_theme_app_ring_dark">#94AAE4</color> + <color name="system_on_theme_app_ring_dark">#E0BBDD</color> + <color name="system_brand_a_dark">#90A6DF</color> + <color name="system_brand_b_dark">#A4ABC1</color> + <color name="system_brand_c_dark">#7A90C8</color> + <color name="system_brand_d_dark">#A886A6</color> + <color name="system_under_surface_dark">#000000</color> + <color name="system_shade_active_dark">#D9E2FF</color> + <color name="system_on_shade_active_dark">#001945</color> + <color name="system_on_shade_active_variant_dark">#2F4578</color> + <color name="system_shade_inactive_dark">#2F3036</color> + <color name="system_on_shade_inactive_dark">#E1E2EC</color> + <color name="system_on_shade_inactive_variant_dark">#C5C6D0</color> + <color name="system_shade_disabled_dark">#0C0E13</color> + <color name="system_overview_background_dark">#C5C6D0</color> + <!-- Accessibility shortcut icon background color --> <color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 --> <color name="accessibility_magnification_background">#F50D60</color> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 59e4161b2e0b..28678c1c5fb0 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -794,6 +794,9 @@ <!-- The divider symbol between different parts of the notification header including spaces. not translatable [CHAR LIMIT=3] --> <string name="notification_header_divider_symbol_with_spaces" translatable="false">" • "</string> + <!-- Text for inline reply button for compact conversation heads ups --> + <string name="notification_compact_heads_up_reply">Reply</string> + <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen --> <string name="notification_hidden_text">New notification</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ae79a4c68f9e..a9d03f543d55 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2353,6 +2353,7 @@ <java-symbol type="layout" name="notification_template_material_base" /> <java-symbol type="layout" name="notification_template_material_heads_up_base" /> <java-symbol type="layout" name="notification_template_material_compact_heads_up_base" /> + <java-symbol type="layout" name="notification_template_material_messaging_compact_heads_up" /> <java-symbol type="layout" name="notification_template_material_big_base" /> <java-symbol type="layout" name="notification_template_material_big_picture" /> <java-symbol type="layout" name="notification_template_material_inbox" /> @@ -3628,6 +3629,7 @@ <java-symbol type="drawable" name="lockscreen_selected" /> <java-symbol type="string" name="notification_header_divider_symbol_with_spaces" /> + <java-symbol type="string" name="notification_compact_heads_up_reply" /> <java-symbol type="color" name="notification_primary_text_color_light" /> <java-symbol type="color" name="notification_primary_text_color_dark" /> @@ -4522,6 +4524,7 @@ <java-symbol type="id" name="expand_button_container" /> <java-symbol type="id" name="expand_button_a11y_container" /> <java-symbol type="id" name="expand_button_touch_container" /> + <java-symbol type="id" name="reply_action_container" /> <java-symbol type="id" name="messaging_group_content_container" /> <java-symbol type="id" name="expand_button_and_content_container" /> <java-symbol type="id" name="conversation_header" /> @@ -5292,6 +5295,71 @@ <java-symbol name="materialColorTertiary" type="attr"/> <java-symbol name="materialColorError" type="attr"/> + <java-symbol name="customColorWidgetBackground" type="attr"/> + <java-symbol name="customColorClockHour" type="attr"/> + <java-symbol name="customColorClockMinute" type="attr"/> + <java-symbol name="customColorClockSecond" type="attr"/> + <java-symbol name="customColorThemeApp" type="attr"/> + <java-symbol name="customColorOnThemeApp" type="attr"/> + <java-symbol name="customColorThemeAppRing" type="attr"/> + <java-symbol name="customColorOnThemeAppRing" type="attr"/> + <java-symbol name="customColorBrandA" type="attr"/> + <java-symbol name="customColorBrandB" type="attr"/> + <java-symbol name="customColorBrandC" type="attr"/> + <java-symbol name="customColorBrandD" type="attr"/> + <java-symbol name="customColorUnderSurface" type="attr"/> + <java-symbol name="customColorShadeActive" type="attr"/> + <java-symbol name="customColorOnShadeActive" type="attr"/> + <java-symbol name="customColorOnShadeActiveVariant" type="attr"/> + <java-symbol name="customColorShadeInactive" type="attr"/> + <java-symbol name="customColorOnShadeInactive" type="attr"/> + <java-symbol name="customColorOnShadeInactiveVariant" type="attr"/> + <java-symbol name="customColorShadeDisabled" type="attr"/> + <java-symbol name="customColorOverviewBackground" type="attr"/> + + <java-symbol name="system_widget_background_light" type="color"/> + <java-symbol name="system_clock_hour_light" type="color"/> + <java-symbol name="system_clock_minute_light" type="color"/> + <java-symbol name="system_clock_second_light" type="color"/> + <java-symbol name="system_theme_app_light" type="color"/> + <java-symbol name="system_on_theme_app_light" type="color"/> + <java-symbol name="system_theme_app_ring_light" type="color"/> + <java-symbol name="system_on_theme_app_ring_light" type="color"/> + <java-symbol name="system_brand_a_light" type="color"/> + <java-symbol name="system_brand_b_light" type="color"/> + <java-symbol name="system_brand_c_light" type="color"/> + <java-symbol name="system_brand_d_light" type="color"/> + <java-symbol name="system_under_surface_light" type="color"/> + <java-symbol name="system_shade_active_light" type="color"/> + <java-symbol name="system_on_shade_active_light" type="color"/> + <java-symbol name="system_on_shade_active_variant_light" type="color"/> + <java-symbol name="system_shade_inactive_light" type="color"/> + <java-symbol name="system_on_shade_inactive_light" type="color"/> + <java-symbol name="system_on_shade_inactive_variant_light" type="color"/> + <java-symbol name="system_shade_disabled_light" type="color"/> + <java-symbol name="system_overview_background_light" type="color"/> + <java-symbol name="system_widget_background_dark" type="color"/> + <java-symbol name="system_clock_hour_dark" type="color"/> + <java-symbol name="system_clock_minute_dark" type="color"/> + <java-symbol name="system_clock_second_dark" type="color"/> + <java-symbol name="system_theme_app_dark" type="color"/> + <java-symbol name="system_on_theme_app_dark" type="color"/> + <java-symbol name="system_theme_app_ring_dark" type="color"/> + <java-symbol name="system_on_theme_app_ring_dark" type="color"/> + <java-symbol name="system_brand_a_dark" type="color"/> + <java-symbol name="system_brand_b_dark" type="color"/> + <java-symbol name="system_brand_c_dark" type="color"/> + <java-symbol name="system_brand_d_dark" type="color"/> + <java-symbol name="system_under_surface_dark" type="color"/> + <java-symbol name="system_shade_active_dark" type="color"/> + <java-symbol name="system_on_shade_active_dark" type="color"/> + <java-symbol name="system_on_shade_active_variant_dark" type="color"/> + <java-symbol name="system_shade_inactive_dark" type="color"/> + <java-symbol name="system_on_shade_inactive_dark" type="color"/> + <java-symbol name="system_on_shade_inactive_variant_dark" type="color"/> + <java-symbol name="system_shade_disabled_dark" type="color"/> + <java-symbol name="system_overview_background_dark" type="color"/> + <java-symbol type="attr" name="actionModeUndoDrawable" /> <java-symbol type="attr" name="actionModeRedoDrawable" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index ee191449ac35..24d493867906 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -284,6 +284,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" /> @@ -380,6 +402,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme @@ -475,6 +519,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and @@ -572,6 +638,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent @@ -668,6 +756,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be @@ -772,6 +882,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a @@ -867,6 +999,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar --> @@ -961,6 +1115,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width @@ -1056,6 +1232,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> @@ -1167,6 +1365,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- DeviceDefault theme for a window without an action bar that will be displayed either @@ -1263,6 +1483,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- DeviceDefault theme for a presentation window on a secondary display. --> @@ -1357,6 +1599,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- DeviceDefault theme for panel windows. This removes all extraneous window @@ -1453,6 +1717,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear @@ -1548,6 +1834,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear @@ -1643,6 +1951,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- DeviceDefault style for input methods, which is used by the @@ -1738,6 +2068,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- DeviceDefault style for input methods, which is used by the @@ -1833,6 +2185,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert"> @@ -1928,6 +2302,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Theme for the dialog shown when an app crashes or ANRs. --> @@ -2028,6 +2424,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame"> @@ -2121,6 +2539,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style --> @@ -2352,6 +2792,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an @@ -2447,6 +2909,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar --> @@ -2541,6 +3025,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar. @@ -2636,6 +3142,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar @@ -2733,6 +3261,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent @@ -2829,6 +3379,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be @@ -2931,6 +3503,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a @@ -3029,6 +3623,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar --> @@ -3126,6 +3742,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum @@ -3224,6 +3862,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> @@ -3303,6 +3963,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. --> @@ -3382,6 +4064,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller @@ -3480,6 +4184,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- DeviceDefault light theme for a window without an action bar that will be displayed either @@ -3579,6 +4305,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- DeviceDefault light theme for a presentation window on a secondary display. --> @@ -3676,6 +4424,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- DeviceDefault light theme for panel windows. This removes all extraneous window @@ -3772,6 +4542,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert"> @@ -3867,6 +4659,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" /> @@ -3962,6 +4776,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice"> @@ -4055,6 +4891,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- DeviceDefault theme for a window that should look like the Settings app. --> @@ -4156,6 +5014,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.SystemUI" parent="Theme.DeviceDefault.Light"> @@ -4238,6 +5118,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.SystemUI.Dialog" parent="Theme.DeviceDefault.Light.Dialog"> @@ -4312,6 +5214,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar --> @@ -4407,6 +5331,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog"> @@ -4486,6 +5432,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Settings.DialogBase"> @@ -4605,6 +5573,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert"> @@ -4702,6 +5692,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" /> @@ -4825,6 +5837,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <style name="ThemeOverlay.DeviceDefault.Accent.Light"> @@ -4878,6 +5912,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. --> @@ -4935,6 +5991,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen"> @@ -4988,6 +6066,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_light</item> <item name="materialColorTertiary">@color/system_tertiary_light</item> <item name="materialColorError">@color/system_error_light</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_light</item> + <item name="customColorClockHour">@color/system_clock_hour_light</item> + <item name="customColorClockMinute">@color/system_clock_minute_light</item> + <item name="customColorClockSecond">@color/system_clock_second_light</item> + <item name="customColorThemeApp">@color/system_theme_app_light</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item> + <item name="customColorBrandA">@color/system_brand_a_light</item> + <item name="customColorBrandB">@color/system_brand_b_light</item> + <item name="customColorBrandC">@color/system_brand_c_light</item> + <item name="customColorBrandD">@color/system_brand_d_light</item> + <item name="customColorUnderSurface">@color/system_under_surface_light</item> + <item name="customColorShadeActive">@color/system_shade_active_light</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_light</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item> + <item name="customColorOverviewBackground">@color/system_overview_background_light</item> </style> <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification"> @@ -5052,6 +6152,28 @@ easier. <item name="materialColorSecondary">@color/system_secondary_dark</item> <item name="materialColorTertiary">@color/system_tertiary_dark</item> <item name="materialColorError">@color/system_error_dark</item> + + <item name="customColorWidgetBackground">@color/system_widget_background_dark</item> + <item name="customColorClockHour">@color/system_clock_hour_dark</item> + <item name="customColorClockMinute">@color/system_clock_minute_dark</item> + <item name="customColorClockSecond">@color/system_clock_second_dark</item> + <item name="customColorThemeApp">@color/system_theme_app_dark</item> + <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item> + <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item> + <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item> + <item name="customColorBrandA">@color/system_brand_a_dark</item> + <item name="customColorBrandB">@color/system_brand_b_dark</item> + <item name="customColorBrandC">@color/system_brand_c_dark</item> + <item name="customColorBrandD">@color/system_brand_d_dark</item> + <item name="customColorUnderSurface">@color/system_under_surface_dark</item> + <item name="customColorShadeActive">@color/system_shade_active_dark</item> + <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item> + <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item> + <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item> + <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item> + <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item> + <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item> + <item name="customColorOverviewBackground">@color/system_overview_background_dark</item> </style> <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight"> <item name="colorListDivider">@color/list_divider_opacity_device_default_light</item> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 436ba15235c9..eb3c84a3ba0c 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -260,6 +260,7 @@ android_ravenwood_test { "src/com/android/internal/os/**/*.java", "src/com/android/internal/util/**/*.java", "src/com/android/internal/power/EnergyConsumerStatsTest.java", + "src/com/android/internal/ravenwood/**/*.java", // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests, // to avoid having a dependency to FrameworksCoreTests. diff --git a/core/tests/coretests/src/android/net/OWNERS b/core/tests/coretests/src/android/net/OWNERS index a779c00814cb..beb77dc8f4fd 100644 --- a/core/tests/coretests/src/android/net/OWNERS +++ b/core/tests/coretests/src/android/net/OWNERS @@ -1,4 +1,5 @@ include /services/core/java/com/android/server/net/OWNERS -per-file SSL*,Uri*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com +per-file SSL*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS +per-file Uri* = varunshah@google.com diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index 2a4ca79d997e..57cb1586bcd0 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -18,6 +18,7 @@ package android.net; import android.content.ContentUris; import android.os.Parcel; +import android.platform.test.annotations.AsbSecurityTest; import androidx.test.filters.SmallTest; @@ -86,6 +87,16 @@ public class UriTest extends TestCase { assertNull(u.getHost()); } + @AsbSecurityTest(cveBugId = 261721900) + @SmallTest + public void testSchemeSanitization() { + Uri uri = new Uri.Builder() + .scheme("http://https://evil.com:/te:st/") + .authority("google.com").path("one/way").build(); + assertEquals("httphttpsevil.com:/te:st/", uri.getScheme()); + assertEquals("httphttpsevil.com:/te:st/://google.com/one/way", uri.toString()); + } + @SmallTest public void testStringUri() { assertEquals("bob lee", diff --git a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java index 6ae3d6597934..df9a89e07404 100644 --- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java +++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java @@ -674,8 +674,6 @@ public class DataSourceTest { protoOutputStream.write(SINGLE_INT, singleIntValue); protoOutputStream.end(payloadToken); protoOutputStream.end(forTestingToken); - - ctx.flush(); }), (args) -> {} ); diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java index 57bbb1cc9b57..5917cc181b47 100644 --- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java +++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java @@ -37,6 +37,7 @@ import static org.testng.Assert.assertEquals; import android.content.Context; import android.graphics.Insets; import android.platform.test.annotations.Presubmit; +import android.util.SparseArray; import android.view.animation.BackGestureInterpolator; import android.view.animation.Interpolator; import android.view.inputmethod.InputMethodManager; @@ -54,6 +55,8 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.lang.reflect.Field; + /** * Tests for {@link ImeBackAnimationController}. * @@ -104,6 +107,13 @@ public class ImeBackAnimationControllerTest { when(mInsetsController.getHost()).thenReturn(mViewRootInsetsControllerHost); when(mViewRootInsetsControllerHost.getInputMethodManager()).thenReturn( inputMethodManager); + try { + Field field = InsetsController.class.getDeclaredField("mSourceConsumers"); + field.setAccessible(true); + field.set(mInsetsController, new SparseArray<InsetsSourceConsumer>()); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Unable to set mSourceConsumers", e); + } }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java index bc0ae9f31904..0b1b40c8ba8b 100644 --- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java +++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java @@ -623,28 +623,6 @@ public class ViewFrameRateTest { assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory()); } - @Test - @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, - FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY - }) - public void idleDetected() throws Throwable { - waitForFrameRateCategoryToSettle(); - mActivityRule.runOnUiThread(() -> { - mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH); - mMovingView.setFrameContentVelocity(Float.MAX_VALUE); - mMovingView.invalidate(); - runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH, - mViewRoot.getLastPreferredFrameRateCategory())); - }); - waitForAfterDraw(); - - // Wait for idle timeout - Thread.sleep(500); - assertEquals(0f, mViewRoot.getLastPreferredFrameRate()); - assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE, - mViewRoot.getLastPreferredFrameRateCategory()); - } - private void runAfterDraw(@NonNull Runnable runnable) { Handler handler = new Handler(Looper.getMainLooper()); mAfterDrawLatch = new CountDownLatch(1); diff --git a/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java new file mode 100644 index 000000000000..d1ef61b2e365 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.ravenwood; + +import static junit.framework.TestCase.assertEquals; + +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RavenwoodEnvironmentTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Test + public void testIsRunningOnRavenwood() { + assertEquals(RavenwoodRule.isUnderRavenwood(), + RavenwoodEnvironment.getInstance().isRunningOnRavenwood()); + } +} diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml index ac1b06495832..a4ee82544b37 100644 --- a/data/fonts/font_fallback_cjkvf.xml +++ b/data/fonts/font_fallback_cjkvf.xml @@ -768,7 +768,7 @@ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> </family> <family lang="zh-Hans"> - <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin" + <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular" supportedAxes="wght"> NotoSansCJK-Regular.ttc <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 @@ -780,7 +780,7 @@ </font> </family> <family lang="zh-Hant,zh-Bopo"> - <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin" + <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular" supportedAxes="wght"> NotoSansCJK-Regular.ttc <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 @@ -792,7 +792,7 @@ </font> </family> <family lang="ja"> - <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin" + <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular" supportedAxes="wght"> NotoSansCJK-Regular.ttc <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 @@ -810,7 +810,7 @@ </font> </family> <family lang="ko"> - <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin" + <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular" supportedAxes="wght"> NotoSansCJK-Regular.ttc <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400 diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml index 9545ae718574..8cbc3000c250 100644 --- a/data/fonts/fonts_cjkvf.xml +++ b/data/fonts/fonts_cjkvf.xml @@ -1409,39 +1409,39 @@ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> </family> <family lang="zh-Hans"> - <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="100"/> </font> - <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="200"/> </font> - <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="300"/> </font> - <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="700"/> </font> - <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="800"/> </font> - <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"> + <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="900"/> </font> @@ -1450,39 +1450,39 @@ </font> </family> <family lang="zh-Hant,zh-Bopo"> - <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="100"/> </font> - <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="200"/> </font> - <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="300"/> </font> - <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="700"/> </font> - <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="800"/> </font> - <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"> + <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="900"/> </font> @@ -1491,39 +1491,39 @@ </font> </family> <family lang="ja"> - <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="100"/> </font> - <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="200"/> </font> - <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="300"/> </font> - <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="700"/> </font> - <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="800"/> </font> - <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"> + <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="900"/> </font> @@ -1542,39 +1542,39 @@ </font> </family> <family lang="ko"> - <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="100"/> </font> - <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="200"/> </font> - <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="300"/> </font> - <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="700"/> </font> - <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="800"/> </font> - <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"> + <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"> NotoSansCJK-Regular.ttc <axis tag="wght" stylevalue="900"/> </font> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index e8b4104a33bb..f8d3bffbe00b 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -438,9 +438,15 @@ key usage 0x0c0070 BRIGHTNESS_DOWN FALLBACK_USAGE_MAPPING key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING +key usage 0x0c00D9 EMOJI_PICKER FALLBACK_USAGE_MAPPING key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING +key usage 0x0c019F SETTINGS FALLBACK_USAGE_MAPPING key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING +key usage 0x0c0227 REFRESH FALLBACK_USAGE_MAPPING +key usage 0x0c029D LANGUAGE_SWITCH FALLBACK_USAGE_MAPPING +key usage 0x0c029F RECENT_APPS FALLBACK_USAGE_MAPPING +key usage 0x0c02A2 ALL_APPS FALLBACK_USAGE_MAPPING key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index e93b0bf1e69a..1fbaeeac8608 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -256,8 +256,10 @@ class DividerPresenter implements View.OnTouchListener { static Color getContainerBackgroundColor( @NonNull TaskFragmentContainer container, @NonNull Color defaultColor) { final Activity activity = container.getTopNonFinishingActivity(); - if (activity == null || !activity.isResumed()) { - // This can happen when the top activity in the container is from a different process. + if (activity == null) { + // This can happen when the activities in the container are from a different process. + // TODO(b/340984203) Report whether the top activity is in the same process. Use default + // color if not. return defaultColor; } @@ -515,8 +517,11 @@ class DividerPresenter implements View.OnTouchListener { private void onStartDragging() { mRenderer.mIsDragging = true; mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging); + mRenderer.updateSurface(); + + // Veil visibility change should be applied together with the surface boost transaction in + // the wct. final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mRenderer.updateSurface(t); mRenderer.showVeils(t); // Callbacks must be executed on the executor to release mLock and prevent deadlocks. @@ -532,18 +537,18 @@ class DividerPresenter implements View.OnTouchListener { @GuardedBy("mLock") private void onDrag() { - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mRenderer.updateSurface(t); - t.apply(); + mRenderer.updateSurface(); } @GuardedBy("mLock") private void onFinishDragging() { mDividerPosition = adjustDividerPositionForSnapPoints(mDividerPosition); mRenderer.setDividerPosition(mDividerPosition); + mRenderer.updateSurface(); + // Veil visibility change should be applied together with the surface boost transaction in + // the wct. final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mRenderer.updateSurface(t); mRenderer.hideVeils(t); // Callbacks must be executed on the executor to release mLock and prevent deadlocks. @@ -994,6 +999,22 @@ class DividerPresenter implements View.OnTouchListener { * Updates the positions and crops of the divider surface and veil surfaces. This method * should be called when {@link #mProperties} is changed or while dragging to update the * position of the divider surface and the veil surfaces. + * + * This method applies the changes in a stand-alone surface transaction immediately. + */ + private void updateSurface() { + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + updateSurface(t); + t.apply(); + } + + /** + * Updates the positions and crops of the divider surface and veil surfaces. This method + * should be called when {@link #mProperties} is changed or while dragging to update the + * position of the divider surface and the veil surfaces. + * + * This method applies the changes in the provided surface transaction and can be synced + * with other changes. */ private void updateSurface(@NonNull SurfaceControl.Transaction t) { final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index a23a47416fb0..f9a6caf42e6e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -21,6 +21,7 @@ import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION; +import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED; import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior; import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior; @@ -358,6 +359,13 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.addTaskFragmentOperation(fragmentToken, operation); } + void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct, + @NonNull IBinder fragmentToken, boolean pinned) { + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_SET_PINNED).setBooleanValue(pinned).build(); + wct.addTaskFragmentOperation(fragmentToken, operation); + } + void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, boolean dimOnTask) { final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 5b0e6b9c49a1..13c2d1f73461 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -350,8 +350,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Resets the isolated navigation and updates the container. final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); final WindowContainerTransaction wct = transactionRecord.getTransaction(); - mPresenter.setTaskFragmentIsolatedNavigation(wct, containerToUnpin, - false /* isolated */); + mPresenter.setTaskFragmentPinned(wct, containerToUnpin, false /* pinned */); updateContainer(wct, containerToUnpin); transactionRecord.apply(false /* shouldApplyIndependently */); updateCallbackIfNecessary(); @@ -1078,8 +1077,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } - // Skip resolving if the activity is on an isolated navigated TaskFragmentContainer. - if (container != null && container.isIsolatedNavigationEnabled()) { + if (container != null && container.shouldSkipActivityResolving()) { return true; } @@ -1535,8 +1533,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity( launchingActivity); if (taskFragmentContainer != null - && taskFragmentContainer.isIsolatedNavigationEnabled()) { - // Skip resolving if started from an isolated navigated TaskFragmentContainer. + && taskFragmentContainer.shouldSkipActivityResolving()) { return null; } if (isAssociatedWithOverlay(launchingActivity)) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 0e4fb3075c65..27048136afd8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -401,18 +401,26 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { return; } - setTaskFragmentIsolatedNavigation(wct, secondaryContainer, !isStacked /* isolatedNav */); + setTaskFragmentPinned(wct, secondaryContainer, !isStacked /* pinned */); if (isStacked && !splitPinRule.isSticky()) { secondaryContainer.getTaskContainer().removeSplitPinContainer(); } } /** - * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer} + * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}. + * <p> + * If a container enables isolated navigation, activities can't be launched to this container + * unless explicitly requested to be launched to. + * + * @see TaskFragmentContainer#isOverlayWithActivityAssociation() */ void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container, boolean isolatedNavigationEnabled) { + if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) { + return; + } if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) { return; } @@ -422,6 +430,28 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } /** + * Sets whether to pin this {@link TaskFragmentContainer}. + * <p> + * If a container is pinned, it won't be chosen as the launch target unless it's the launching + * container. + * + * @see TaskFragmentContainer#isAlwaysOnTopOverlay() + * @see TaskContainer#getSplitPinContainer() + */ + void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer container, + boolean pinned) { + if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) { + return; + } + if (container.isPinned() == pinned) { + return; + } + container.setPinned(pinned); + setTaskFragmentPinned(wct, container.getTaskFragmentToken(), pinned); + } + + /** * Resizes the task fragment if it was already registered. Skips the operation if the container * creation has not been reported from the server yet. */ @@ -586,6 +616,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { super.setCompanionTaskFragment(wct, primary, secondary); } + /** + * Applies the {@code attributes} to a standalone {@code container}. + * + * @param minDimensions the minimum dimension of the container. + */ void applyActivityStackAttributes( @NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container, @@ -594,16 +629,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final Rect relativeBounds = sanitizeBounds(attributes.getRelativeBounds(), minDimensions, container); final boolean isFillParent = relativeBounds.isEmpty(); - // Note that we only set isolated navigation for overlay container without activity - // association. Activity will be launched to an expanded container on top of the overlay - // if the overlay is associated with an activity. Thus, an overlay with activity association - // will never be isolated navigated. - final boolean isIsolatedNavigated = container.isAlwaysOnTopOverlay() && !isFillParent; final boolean dimOnTask = !isFillParent - && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK - && Flags.fullscreenDimFlag(); + && Flags.fullscreenDimFlag() + && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK; final IBinder fragmentToken = container.getTaskFragmentToken(); + if (container.isAlwaysOnTopOverlay()) { + setTaskFragmentPinned(wct, container, !isFillParent); + } else if (container.isOverlayWithActivityAssociation()) { + setTaskFragmentIsolatedNavigation(wct, container, !isFillParent); + } + // TODO(b/243518738): Update to resizeTaskFragment after we migrate WCT#setRelativeBounds // and WCT#setWindowingMode to take fragmentToken. resizeTaskFragmentIfRegistered(wct, container, relativeBounds); @@ -612,7 +648,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode); // Always use default animation for standalone ActivityStack. updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT); - setTaskFragmentIsolatedNavigation(wct, container, isIsolatedNavigated); setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index c952dfea4de5..482554351b97 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -184,6 +184,11 @@ class TaskFragmentContainer { private boolean mIsIsolatedNavigationEnabled; /** + * Whether this TaskFragment is pinned. + */ + private boolean mIsPinned; + + /** * Whether to apply dimming on the parent Task that was requested last. */ private boolean mLastDimOnTask; @@ -893,6 +898,34 @@ class TaskFragmentContainer { mIsIsolatedNavigationEnabled = isolatedNavigationEnabled; } + /** + * Returns whether this container is pinned. + * + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED + */ + boolean isPinned() { + return mIsPinned; + } + + /** + * Sets whether to pin this container or not. + * + * @see #isPinned() + */ + void setPinned(boolean pinned) { + mIsPinned = pinned; + } + + /** + * Indicates to skip activity resolving if the activity is from this container. + * + * @see #isIsolatedNavigationEnabled() + * @see #isPinned() + */ + boolean shouldSkipActivityResolving() { + return isIsolatedNavigationEnabled() || isPinned(); + } + /** Sets whether to apply dim on the parent Task. */ void setLastDimOnTask(boolean lastDimOnTask) { mLastDimOnTask = lastDimOnTask; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java index ad913c98482c..b0a45e285896 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java @@ -631,15 +631,8 @@ public class DividerPresenterTest { assertEquals(defaultColor, DividerPresenter.getContainerBackgroundColor(container, defaultColor)); - // When the top non-finishing activity is not resumed, the default color should be returned. + // When the top non-finishing activity is non-null, its background color should be returned. when(container.getTopNonFinishingActivity()).thenReturn(activity); - when(activity.isResumed()).thenReturn(false); - assertEquals(defaultColor, - DividerPresenter.getContainerBackgroundColor(container, defaultColor)); - - // When the top non-finishing activity is resumed, its background color should be returned. - when(container.getTopNonFinishingActivity()).thenReturn(activity); - when(activity.isResumed()).thenReturn(true); assertEquals(activityBackgroundColor, DividerPresenter.getContainerBackgroundColor(container, defaultColor)); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 049a9e2c2aca..9ebcb759115d 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -188,6 +188,32 @@ public class OverlayPresentationTest { } @Test + public void testSetIsolatedNavigation_overlayFeatureDisabled_earlyReturn() { + mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG); + + final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test"); + + mSplitPresenter.setTaskFragmentIsolatedNavigation(mTransaction, container, + !container.isIsolatedNavigationEnabled()); + + verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(), + any(IBinder.class), anyBoolean()); + } + + @Test + public void testSetPinned_overlayFeatureDisabled_earlyReturn() { + mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG); + + final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test"); + + mSplitPresenter.setTaskFragmentPinned(mTransaction, container, + !container.isPinned()); + + verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), any(IBinder.class), + anyBoolean()); + } + + @Test public void testGetAllNonFinishingOverlayContainers() { assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty(); @@ -608,8 +634,11 @@ public class OverlayPresentationTest { WINDOWING_MODE_UNDEFINED); verify(mSplitPresenter).updateAnimationParams(mTransaction, token, TaskFragmentAnimationParams.DEFAULT); - verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false); verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false); + verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), + any(TaskFragmentContainer.class), anyBoolean()); + verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(), + any(TaskFragmentContainer.class), anyBoolean()); } @Test @@ -630,9 +659,9 @@ public class OverlayPresentationTest { WINDOWING_MODE_MULTI_WINDOW); verify(mSplitPresenter).updateAnimationParams(mTransaction, token, TaskFragmentAnimationParams.DEFAULT); - // Set isolated navigation to false if the overlay container is associated with - // the launching activity. - verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false); + verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, true); + verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), + any(TaskFragmentContainer.class), anyBoolean()); verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true); } @@ -655,10 +684,9 @@ public class OverlayPresentationTest { container, WINDOWING_MODE_MULTI_WINDOW); verify(mSplitPresenter).updateAnimationParams(mTransaction, token, TaskFragmentAnimationParams.DEFAULT); - // Set isolated navigation to false if the overlay container is associated with - // the launching activity. - verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, - container, true); + verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(), + any(TaskFragmentContainer.class), anyBoolean()); + verify(mSplitPresenter).setTaskFragmentPinned(mTransaction, container, true); verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true); } @@ -678,6 +706,8 @@ public class OverlayPresentationTest { verify(mSplitPresenter).updateAnimationParams(mTransaction, token, TaskFragmentAnimationParams.DEFAULT); verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false); + verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), + any(TaskFragmentContainer.class), anyBoolean()); verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java index c677484f64f1..3fbce9ec31a5 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; +import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED; import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY; import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY; @@ -285,6 +286,28 @@ public class SplitPresenterTest { } @Test + public void testSetTaskFragmentPinned() { + final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID); + + // Verify the default. + assertFalse(container.isPinned()); + + mPresenter.setTaskFragmentPinned(mTransaction, container, true); + + final TaskFragmentOperation expectedOperation = new TaskFragmentOperation.Builder( + OP_TYPE_SET_PINNED).setBooleanValue(true).build(); + verify(mTransaction).addTaskFragmentOperation(container.getTaskFragmentToken(), + expectedOperation); + assertTrue(container.isPinned()); + + // No request to set the same animation params. + clearInvocations(mTransaction); + mPresenter.setTaskFragmentPinned(mTransaction, container, true); + + verify(mTransaction, never()).addTaskFragmentOperation(any(), any()); + } + + @Test public void testGetMinDimensionsForIntent() { final Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MinimumDimensionActivity.class); diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index fe68123513ca..8977d5cad780 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -71,3 +71,10 @@ flag { description: "Hides the bubble overflow if there aren't any overflowed bubbles" bug: "334175587" } + +flag { + name: "enable_retrievable_bubbles" + namespace: "multitasking" + description: "Allow opening bubbles overflow UI without bubbles being visible" + bug: "340337839" +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java index 5af4c3b0a716..bdd89c0e1ac9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.desktopmode; +package com.android.wm.shell.shared; import android.annotation.NonNull; import android.content.Context; @@ -127,7 +127,7 @@ public class DesktopModeStatus { /** * Return the maximum limit on the number of Tasks to show in Desktop Mode at any one time. */ - static int getMaxTaskLimit() { + public static int getMaxTaskLimit() { return MAX_TASK_LIMIT; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 163a896e2659..5600664a8f47 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -646,7 +646,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void tryDispatchOnBackCancelled(IOnBackInvokedCallback callback) { if (!mOnBackStartDispatched) { - Log.e(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched."); + Log.d(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched."); return; } if (callback == null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt index 037b1ec9247c..c988c2fb5103 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.wm.shell.back import android.animation.Animator @@ -34,6 +35,7 @@ import android.view.RemoteAnimationTarget import android.view.SurfaceControl import android.view.animation.DecelerateInterpolator import android.view.animation.Interpolator +import android.view.animation.Transformation import android.window.BackEvent import android.window.BackMotionEvent import android.window.BackNavigationInfo @@ -46,52 +48,45 @@ import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.animation.Interpolators import com.android.wm.shell.protolog.ShellProtoLogGroup -import com.android.wm.shell.shared.annotations.ShellMainThread -import javax.inject.Inject import kotlin.math.abs import kotlin.math.max import kotlin.math.min -/** Class that defines cross-activity animation. */ -@ShellMainThread -class CrossActivityBackAnimation @Inject constructor( +abstract class CrossActivityBackAnimation( private val context: Context, private val background: BackAnimationBackground, - private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, + protected val transaction: SurfaceControl.Transaction, + private val choreographer: Choreographer ) : ShellBackAnimation() { - private val startClosingRect = RectF() - private val targetClosingRect = RectF() - private val currentClosingRect = RectF() + protected val startClosingRect = RectF() + protected val targetClosingRect = RectF() + protected val currentClosingRect = RectF() - private val startEnteringRect = RectF() - private val targetEnteringRect = RectF() - private val currentEnteringRect = RectF() + protected val startEnteringRect = RectF() + protected val targetEnteringRect = RectF() + protected val currentEnteringRect = RectF() - private val backAnimRect = Rect() + protected val backAnimRect = Rect() private val cropRect = Rect() private var cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context) - private val backAnimationRunner = BackAnimationRunner( - Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY - ) + private val backAnimationRunner = + BackAnimationRunner(Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY) private val initialTouchPos = PointF() private val transformMatrix = Matrix() private val tmpFloat9 = FloatArray(9) - private var enteringTarget: RemoteAnimationTarget? = null - private var closingTarget: RemoteAnimationTarget? = null - private val transaction = SurfaceControl.Transaction() + protected var enteringTarget: RemoteAnimationTarget? = null + protected var closingTarget: RemoteAnimationTarget? = null private var triggerBack = false private var finishCallback: IRemoteAnimationFinishedCallback? = null private val progressAnimator = BackProgressAnimator() private val displayBoundsMargin = context.resources.getDimension(R.dimen.cross_task_back_vertical_margin) - private val enteringStartOffset = - context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset) private val gestureInterpolator = Interpolators.BACK_GESTURE - private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN private val verticalMoveInterpolator: Interpolator = DecelerateInterpolator() private var scrimLayer: SurfaceControl? = null @@ -103,13 +98,42 @@ class CrossActivityBackAnimation @Inject constructor( private var rightLetterboxLayer: SurfaceControl? = null private var letterboxColor: Int = 0 + /** Background color to be used during the animation, also see [getBackgroundColor] */ + protected var customizedBackgroundColor = 0 + + /** + * Whether the entering target should be shifted vertically with the user gesture in pre-commit + */ + abstract val allowEnteringYShift: Boolean + + /** + * Subclasses must set the [startEnteringRect] and [targetEnteringRect] to define the movement + * of the enteringTarget during pre-commit phase. + */ + abstract fun preparePreCommitEnteringRectMovement() + + /** + * Returns a base transformation to apply to the entering target during pre-commit. The system + * will apply the default animation on top of it. + */ + protected open fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation? = + null + override fun onConfigurationChanged(newConfiguration: Configuration) { cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context) } override fun getRunner() = backAnimationRunner - private fun startBackAnimation(backMotionEvent: BackMotionEvent) { + private fun getBackgroundColor(): Int = + when { + customizedBackgroundColor != 0 -> customizedBackgroundColor + isLetterboxed -> letterboxColor + enteringTarget != null -> enteringTarget!!.taskInfo.taskDescription!!.backgroundColor + else -> 0 + } + + protected open fun startBackAnimation(backMotionEvent: BackMotionEvent) { if (enteringTarget == null || closingTarget == null) { ProtoLog.d( ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, @@ -122,8 +146,8 @@ class CrossActivityBackAnimation @Inject constructor( transaction.setAnimationTransaction() isLetterboxed = closingTarget!!.taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed - enteringHasSameLetterbox = isLetterboxed && - closingTarget!!.localBounds.equals(enteringTarget!!.localBounds) + enteringHasSameLetterbox = + isLetterboxed && closingTarget!!.localBounds.equals(enteringTarget!!.localBounds) if (isLetterboxed && !enteringHasSameLetterbox) { // Play animation with letterboxes, if closing and entering target have mismatching @@ -143,32 +167,27 @@ class CrossActivityBackAnimation @Inject constructor( targetClosingRect.scaleCentered(MAX_SCALE) if (backMotionEvent.swipeEdge != BackEvent.EDGE_RIGHT) { targetClosingRect.offset( - startClosingRect.right - targetClosingRect.right - displayBoundsMargin, 0f + startClosingRect.right - targetClosingRect.right - displayBoundsMargin, + 0f ) } - // the entering target starts 96dp to the left of the screen edge... - startEnteringRect.set(startClosingRect) - startEnteringRect.offset(-enteringStartOffset, 0f) - - // ...and gets scaled in sync with the closing target - targetEnteringRect.set(startEnteringRect) - targetEnteringRect.scaleCentered(MAX_SCALE) + preparePreCommitEnteringRectMovement() - // Draw background with task background color (or letterbox color). - val backgroundColor = if (isLetterboxed) { - letterboxColor - } else { - enteringTarget!!.taskInfo.taskDescription!!.backgroundColor - } background.ensureBackground( - closingTarget!!.windowConfiguration.bounds, backgroundColor, transaction + closingTarget!!.windowConfiguration.bounds, + getBackgroundColor(), + transaction ) ensureScrimLayer() if (isLetterboxed && enteringHasSameLetterbox) { // crop left and right letterboxes - cropRect.set(closingTarget!!.localBounds.left, 0, closingTarget!!.localBounds.right, - closingTarget!!.windowConfiguration.bounds.height()) + cropRect.set( + closingTarget!!.localBounds.left, + 0, + closingTarget!!.localBounds.right, + closingTarget!!.windowConfiguration.bounds.height() + ) // and add fake letterbox square surfaces instead ensureLetterboxes() } else { @@ -185,8 +204,14 @@ class CrossActivityBackAnimation @Inject constructor( currentClosingRect.offset(0f, yOffset) applyTransform(closingTarget?.leash, currentClosingRect, 1f) currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress) - currentEnteringRect.offset(0f, yOffset) - applyTransform(enteringTarget?.leash, currentEnteringRect, 1f) + if (allowEnteringYShift) currentEnteringRect.offset(0f, yOffset) + val enteringTransformation = getPreCommitEnteringBaseTransformation(progress) + applyTransform( + enteringTarget?.leash, + currentEnteringRect, + enteringTransformation?.alpha ?: 1f, + enteringTransformation + ) applyTransaction() } @@ -199,30 +224,25 @@ class CrossActivityBackAnimation @Inject constructor( val deltaYRatio = min(screenHeight / 2f, abs(rawYDelta)) / (screenHeight / 2f) val interpolatedYRatio: Float = verticalMoveInterpolator.getInterpolation(deltaYRatio) // limit y-shift so surface never passes 8dp screen margin - val deltaY = yDirection * interpolatedYRatio * max( - 0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin - ) + val deltaY = + max(0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin) * + interpolatedYRatio * + yDirection return deltaY } - private fun onGestureCommitted() { - if (closingTarget?.leash == null || enteringTarget?.leash == null || - !enteringTarget!!.leash.isValid || !closingTarget!!.leash.isValid + protected open fun onGestureCommitted() { + if ( + closingTarget?.leash == null || + enteringTarget?.leash == null || + !enteringTarget!!.leash.isValid || + !closingTarget!!.leash.isValid ) { finishAnimation() return } - // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current - // coordinate of the gesture driven phase. Let's update the start and target rects and kick - // off the animator - startClosingRect.set(currentClosingRect) - startEnteringRect.set(currentEnteringRect) - targetEnteringRect.set(backAnimRect) - targetClosingRect.set(backAnimRect) - targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f) - - val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION) + val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_COMMIT_DURATION) valueAnimator.addUpdateListener { animation: ValueAnimator -> val progress = animation.animatedFraction onPostCommitProgress(progress) @@ -230,27 +250,22 @@ class CrossActivityBackAnimation @Inject constructor( background.resetStatusBarCustomization() } } - valueAnimator.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - background.resetStatusBarCustomization() - finishAnimation() + valueAnimator.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + background.resetStatusBarCustomization() + finishAnimation() + } } - }) + ) valueAnimator.start() } - private fun onPostCommitProgress(linearProgress: Float) { - val closingAlpha = max(1f - linearProgress * 2, 0f) - val progress = postCommitInterpolator.getInterpolation(linearProgress) + protected open fun onPostCommitProgress(linearProgress: Float) { scrimLayer?.let { transaction.setAlpha(it, maxScrimAlpha * (1f - linearProgress)) } - currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress) - applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha) - currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress) - applyTransform(enteringTarget?.leash, currentEnteringRect, 1f) - applyTransaction() } - private fun finishAnimation() { + protected open fun finishAnimation() { enteringTarget?.let { if (it.leash != null && it.leash.isValid) { transaction.setCornerRadius(it.leash, 0f) @@ -278,47 +293,56 @@ class CrossActivityBackAnimation @Inject constructor( enteringHasSameLetterbox = false } - private fun applyTransform(leash: SurfaceControl?, rect: RectF, alpha: Float) { + protected fun applyTransform( + leash: SurfaceControl?, + rect: RectF, + alpha: Float, + baseTransformation: Transformation? = null + ) { if (leash == null || !leash.isValid) return val scale = rect.width() / backAnimRect.width() - transformMatrix.reset() - val scalePivotX = if (isLetterboxed && enteringHasSameLetterbox) { - closingTarget!!.localBounds.left.toFloat() - } else { - 0f - } - transformMatrix.setScale(scale, scale, scalePivotX, 0f) - transformMatrix.postTranslate(rect.left, rect.top) - transaction.setAlpha(leash, alpha) - .setMatrix(leash, transformMatrix, tmpFloat9) + val matrix = baseTransformation?.matrix ?: transformMatrix.apply { reset() } + val scalePivotX = + if (isLetterboxed && enteringHasSameLetterbox) { + closingTarget!!.localBounds.left.toFloat() + } else { + 0f + } + matrix.postScale(scale, scale, scalePivotX, 0f) + matrix.postTranslate(rect.left, rect.top) + transaction + .setAlpha(leash, keepMinimumAlpha(alpha)) + .setMatrix(leash, matrix, tmpFloat9) .setCrop(leash, cropRect) .setCornerRadius(leash, cornerRadius) } - private fun applyTransaction() { - transaction.setFrameTimelineVsync(Choreographer.getInstance().vsyncId) + protected fun applyTransaction() { + transaction.setFrameTimelineVsync(choreographer.vsyncId) transaction.apply() } private fun ensureScrimLayer() { if (scrimLayer != null) return val isDarkTheme: Boolean = isDarkMode(context) - val scrimBuilder = SurfaceControl.Builder() - .setName("Cross-Activity back animation scrim") - .setCallsite("CrossActivityBackAnimation") - .setColorLayer() - .setOpaque(false) - .setHidden(false) + val scrimBuilder = + SurfaceControl.Builder() + .setName("Cross-Activity back animation scrim") + .setCallsite("CrossActivityBackAnimation") + .setColorLayer() + .setOpaque(false) + .setHidden(false) rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, scrimBuilder) scrimLayer = scrimBuilder.build() val colorComponents = floatArrayOf(0f, 0f, 0f) maxScrimAlpha = if (isDarkTheme) MAX_SCRIM_ALPHA_DARK else MAX_SCRIM_ALPHA_LIGHT - val scrimCrop = if (isLetterboxed) { - closingTarget!!.windowConfiguration.bounds - } else { - closingTarget!!.localBounds - } + val scrimCrop = + if (isLetterboxed) { + closingTarget!!.windowConfiguration.bounds + } else { + closingTarget!!.localBounds + } transaction .setColor(scrimLayer, colorComponents) .setAlpha(scrimLayer!!, maxScrimAlpha) @@ -339,21 +363,34 @@ class CrossActivityBackAnimation @Inject constructor( private fun ensureLetterboxes() { closingTarget?.let { t -> if (t.localBounds.left != 0 && leftLetterboxLayer == null) { - val bounds = Rect(0, t.windowConfiguration.bounds.top, t.localBounds.left, - t.windowConfiguration.bounds.bottom) + val bounds = + Rect( + 0, + t.windowConfiguration.bounds.top, + t.localBounds.left, + t.windowConfiguration.bounds.bottom + ) leftLetterboxLayer = ensureLetterbox(bounds) } - if (t.localBounds.right != t.windowConfiguration.bounds.right && - rightLetterboxLayer == null) { - val bounds = Rect(t.localBounds.right, t.windowConfiguration.bounds.top, - t.windowConfiguration.bounds.right, t.windowConfiguration.bounds.bottom) + if ( + t.localBounds.right != t.windowConfiguration.bounds.right && + rightLetterboxLayer == null + ) { + val bounds = + Rect( + t.localBounds.right, + t.windowConfiguration.bounds.top, + t.windowConfiguration.bounds.right, + t.windowConfiguration.bounds.bottom + ) rightLetterboxLayer = ensureLetterbox(bounds) } } } private fun ensureLetterbox(bounds: Rect): SurfaceControl { - val letterboxBuilder = SurfaceControl.Builder() + val letterboxBuilder = + SurfaceControl.Builder() .setName("Cross-Activity back animation letterbox") .setCallsite("CrossActivityBackAnimation") .setColorLayer() @@ -362,13 +399,17 @@ class CrossActivityBackAnimation @Inject constructor( rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, letterboxBuilder) val layer = letterboxBuilder.build() - val colorComponents = floatArrayOf(Color.red(letterboxColor) / 255f, - Color.green(letterboxColor) / 255f, Color.blue(letterboxColor) / 255f) + val colorComponents = + floatArrayOf( + Color.red(letterboxColor) / 255f, + Color.green(letterboxColor) / 255f, + Color.blue(letterboxColor) / 255f + ) transaction - .setColor(layer, colorComponents) - .setCrop(layer, bounds) - .setRelativeLayer(layer, closingTarget!!.leash, 1) - .show(layer) + .setColor(layer, colorComponents) + .setCrop(layer, bounds) + .setRelativeLayer(layer, closingTarget!!.leash, 1) + .show(layer) return layer } @@ -389,8 +430,8 @@ class CrossActivityBackAnimation @Inject constructor( } override fun prepareNextAnimation( - animationInfo: BackNavigationInfo.CustomAnimationInfo?, - letterboxColor: Int + animationInfo: BackNavigationInfo.CustomAnimationInfo?, + letterboxColor: Int ): Boolean { this.letterboxColor = letterboxColor return false @@ -415,9 +456,7 @@ class CrossActivityBackAnimation @Inject constructor( } override fun onBackCancelled() { - progressAnimator.onBackCancelled { - finishAnimation() - } + progressAnimator.onBackCancelled { finishAnimation() } } override fun onBackInvoked() { @@ -435,7 +474,8 @@ class CrossActivityBackAnimation @Inject constructor( finishedCallback: IRemoteAnimationFinishedCallback ) { ProtoLog.d( - ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "Start back to activity animation." + ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, + "Start back to activity animation." ) for (a in apps) { when (a.mode) { @@ -452,23 +492,25 @@ class CrossActivityBackAnimation @Inject constructor( } companion object { - /** Max scale of the entering/closing window.*/ - private const val MAX_SCALE = 0.9f - - /** Duration of post animation after gesture committed. */ - private const val POST_ANIMATION_DURATION = 300L - + /** Max scale of the closing window. */ + internal const val MAX_SCALE = 0.9f private const val MAX_SCRIM_ALPHA_DARK = 0.8f private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f + private const val POST_COMMIT_DURATION = 300L } } +// The target will loose focus when alpha == 0, so keep a minimum value for it. +private fun keepMinimumAlpha(transAlpha: Float): Float { + return max(transAlpha.toDouble(), 0.005).toFloat() +} + private fun isDarkMode(context: Context): Boolean { return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES + Configuration.UI_MODE_NIGHT_YES } -private fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) { +internal fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) { require(!(progress < 0 || progress > 1)) { "Progress value must be between 0 and 1" } left = start.left + (target.left - start.left) * progress top = start.top + (target.top - start.top) * progress @@ -476,7 +518,7 @@ private fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Fl bottom = start.bottom + (target.bottom - start.bottom) * progress } -private fun RectF.scaleCentered( +internal fun RectF.scaleCentered( scale: Float, pivotX: Float = left + width() / 2, pivotY: Float = top + height() / 2 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt new file mode 100644 index 000000000000..e6ec2b449616 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt @@ -0,0 +1,256 @@ +/* + * 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.back + +import android.content.Context +import android.graphics.Rect +import android.graphics.RectF +import android.util.MathUtils +import android.view.Choreographer +import android.view.SurfaceControl +import android.view.animation.Animation +import android.view.animation.Transformation +import android.window.BackMotionEvent +import android.window.BackNavigationInfo +import com.android.internal.R +import com.android.internal.policy.TransitionAnimation +import com.android.internal.protolog.common.ProtoLog +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.shared.annotations.ShellMainThread +import javax.inject.Inject + +/** Class that handles customized predictive cross activity back animations. */ +@ShellMainThread +class CustomCrossActivityBackAnimation( + context: Context, + background: BackAnimationBackground, + rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, + transaction: SurfaceControl.Transaction, + choreographer: Choreographer, + private val customAnimationLoader: CustomAnimationLoader +) : + CrossActivityBackAnimation( + context, + background, + rootTaskDisplayAreaOrganizer, + transaction, + choreographer + ) { + + private var enterAnimation: Animation? = null + private var closeAnimation: Animation? = null + private val transformation = Transformation() + private var gestureProgress = 0f + + override val allowEnteringYShift = false + + @Inject + constructor( + context: Context, + background: BackAnimationBackground, + rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + ) : this( + context, + background, + rootTaskDisplayAreaOrganizer, + SurfaceControl.Transaction(), + Choreographer.getInstance(), + CustomAnimationLoader( + TransitionAnimation(context, false /* debug */, "CustomCrossActivityBackAnimation") + ) + ) + + override fun preparePreCommitEnteringRectMovement() { + // No movement for the entering rect + startEnteringRect.set(startClosingRect) + targetEnteringRect.set(startClosingRect) + } + + override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation { + gestureProgress = progress + transformation.clear() + enterAnimation!!.getTransformationAt(progress * PRE_COMMIT_MAX_PROGRESS, transformation) + return transformation + } + + override fun startBackAnimation(backMotionEvent: BackMotionEvent) { + super.startBackAnimation(backMotionEvent) + if ( + closeAnimation == null || + enterAnimation == null || + closingTarget == null || + enteringTarget == null + ) { + ProtoLog.d( + ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, + "Enter animation or close animation is null." + ) + return + } + initializeAnimation(closeAnimation!!, closingTarget!!.localBounds) + initializeAnimation(enterAnimation!!, enteringTarget!!.localBounds) + } + + override fun onPostCommitProgress(linearProgress: Float) { + super.onPostCommitProgress(linearProgress) + if (closingTarget == null || enteringTarget == null) return + + // TODO: Should we use the duration from the custom xml spec for the post-commit animation? + applyTransform(closingTarget!!.leash, currentClosingRect, linearProgress, closeAnimation!!) + val enteringProgress = + MathUtils.lerp(gestureProgress * PRE_COMMIT_MAX_PROGRESS, 1f, linearProgress) + applyTransform( + enteringTarget!!.leash, + currentEnteringRect, + enteringProgress, + enterAnimation!! + ) + applyTransaction() + } + + private fun applyTransform( + leash: SurfaceControl, + rect: RectF, + progress: Float, + animation: Animation + ) { + transformation.clear() + animation.getTransformationAt(progress, transformation) + applyTransform(leash, rect, transformation.alpha, transformation) + } + + override fun finishAnimation() { + closeAnimation?.reset() + closeAnimation = null + enterAnimation?.reset() + enterAnimation = null + transformation.clear() + gestureProgress = 0f + super.finishAnimation() + } + + /** Load customize animation before animation start. */ + override fun prepareNextAnimation( + animationInfo: BackNavigationInfo.CustomAnimationInfo?, + letterboxColor: Int + ): Boolean { + super.prepareNextAnimation(animationInfo, letterboxColor) + if (animationInfo == null) return false + customAnimationLoader.loadAll(animationInfo)?.let { result -> + closeAnimation = result.closeAnimation + enterAnimation = result.enterAnimation + customizedBackgroundColor = result.backgroundColor + return true + } + return false + } + + class AnimationLoadResult { + var closeAnimation: Animation? = null + var enterAnimation: Animation? = null + var backgroundColor = 0 + } + + companion object { + private const val PRE_COMMIT_MAX_PROGRESS = 0.2f + } +} + +/** Helper class to load custom animation. */ +class CustomAnimationLoader(private val transitionAnimation: TransitionAnimation) { + + /** + * Load both enter and exit animation for the close activity transition. Note that the result is + * only valid if the exit animation has set and loaded success. If the entering animation has + * not set(i.e. 0), here will load the default entering animation for it. + * + * @param animationInfo The information of customize animation, which can be set from + * [Activity.overrideActivityTransition] and/or [LayoutParams.windowAnimations] + */ + fun loadAll( + animationInfo: BackNavigationInfo.CustomAnimationInfo + ): CustomCrossActivityBackAnimation.AnimationLoadResult? { + if (animationInfo.packageName.isEmpty()) return null + val close = loadAnimation(animationInfo, false) ?: return null + val open = loadAnimation(animationInfo, true) + val result = CustomCrossActivityBackAnimation.AnimationLoadResult() + result.closeAnimation = close + result.enterAnimation = open + result.backgroundColor = animationInfo.customBackground + return result + } + + /** + * Load enter or exit animation from CustomAnimationInfo + * + * @param animationInfo The information for customize animation. + * @param enterAnimation true when load for enter animation, false for exit animation. + * @return Loaded animation. + */ + fun loadAnimation( + animationInfo: BackNavigationInfo.CustomAnimationInfo, + enterAnimation: Boolean + ): Animation? { + var a: Animation? = null + // Activity#overrideActivityTransition has higher priority than windowAnimations + // Try to get animation from Activity#overrideActivityTransition + if ( + enterAnimation && animationInfo.customEnterAnim != 0 || + !enterAnimation && animationInfo.customExitAnim != 0 + ) { + a = + transitionAnimation.loadAppTransitionAnimation( + animationInfo.packageName, + if (enterAnimation) animationInfo.customEnterAnim + else animationInfo.customExitAnim + ) + } else if (animationInfo.windowAnimations != 0) { + // try to get animation from LayoutParams#windowAnimations + a = + transitionAnimation.loadAnimationAttr( + animationInfo.packageName, + animationInfo.windowAnimations, + if (enterAnimation) R.styleable.WindowAnimation_activityCloseEnterAnimation + else R.styleable.WindowAnimation_activityCloseExitAnimation, + false /* translucent */ + ) + } + // Only allow to load default animation for opening target. + if (a == null && enterAnimation) { + a = loadDefaultOpenAnimation() + } + if (a != null) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a) + } else { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "No custom animation loaded") + } + return a + } + + private fun loadDefaultOpenAnimation(): Animation? { + return transitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_activityCloseEnterAnimation, + false /* translucent */ + ) + } +} + +private fun initializeAnimation(animation: Animation, bounds: Rect) { + val width = bounds.width() + val height = bounds.height() + animation.initialize(width, height, width, height) +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java deleted file mode 100644 index e27b40e58591..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.back; - -import static android.view.RemoteAnimationTarget.MODE_CLOSING; -import static android.view.RemoteAnimationTarget.MODE_OPENING; - -import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.Activity; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Color; -import android.graphics.Rect; -import android.os.RemoteException; -import android.util.FloatProperty; -import android.view.Choreographer; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.IRemoteAnimationRunner; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.view.WindowManager.LayoutParams; -import android.view.animation.Animation; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Transformation; -import android.window.BackEvent; -import android.window.BackMotionEvent; -import android.window.BackNavigationInfo; -import android.window.BackProgressAnimator; -import android.window.IOnBackInvokedCallback; - -import com.android.internal.R; -import com.android.internal.dynamicanimation.animation.SpringAnimation; -import com.android.internal.dynamicanimation.animation.SpringForce; -import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.internal.policy.TransitionAnimation; -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.shared.annotations.ShellMainThread; - -import javax.inject.Inject; - -/** Class that handle customized close activity transition animation. */ -@ShellMainThread -public class CustomizeActivityAnimation extends ShellBackAnimation { - private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); - private final BackAnimationRunner mBackAnimationRunner; - private float mCornerRadius; - private final SurfaceControl.Transaction mTransaction; - private final BackAnimationBackground mBackground; - private RemoteAnimationTarget mEnteringTarget; - private RemoteAnimationTarget mClosingTarget; - private IRemoteAnimationFinishedCallback mFinishCallback; - /** Duration of post animation after gesture committed. */ - private static final int POST_ANIMATION_DURATION = 250; - - private static final int SCALE_FACTOR = 1000; - private final SpringAnimation mProgressSpring; - private float mLatestProgress = 0.0f; - - private static final float TARGET_COMMIT_PROGRESS = 0.5f; - - private final float[] mTmpFloat9 = new float[9]; - private final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); - - final CustomAnimationLoader mCustomAnimationLoader; - private Animation mEnterAnimation; - private Animation mCloseAnimation; - private int mNextBackgroundColor; - final Transformation mTransformation = new Transformation(); - - private final Choreographer mChoreographer; - private final Context mContext; - - @Inject - public CustomizeActivityAnimation(Context context, BackAnimationBackground background) { - this(context, background, new SurfaceControl.Transaction(), null); - } - - CustomizeActivityAnimation(Context context, BackAnimationBackground background, - SurfaceControl.Transaction transaction, Choreographer choreographer) { - mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); - mBackground = background; - mBackAnimationRunner = new BackAnimationRunner( - new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY); - mCustomAnimationLoader = new CustomAnimationLoader(context); - - mProgressSpring = new SpringAnimation(this, ENTER_PROGRESS_PROP); - mProgressSpring.setSpring(new SpringForce() - .setStiffness(SpringForce.STIFFNESS_MEDIUM) - .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)); - mTransaction = transaction == null ? new SurfaceControl.Transaction() : transaction; - mChoreographer = choreographer != null ? choreographer : Choreographer.getInstance(); - mContext = context; - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext); - } - - private float getLatestProgress() { - return mLatestProgress * SCALE_FACTOR; - } - private void setLatestProgress(float value) { - mLatestProgress = value / SCALE_FACTOR; - applyTransformTransaction(mLatestProgress); - } - - private static final FloatProperty<CustomizeActivityAnimation> ENTER_PROGRESS_PROP = - new FloatProperty<>("enter") { - @Override - public void setValue(CustomizeActivityAnimation anim, float value) { - anim.setLatestProgress(value); - } - - @Override - public Float get(CustomizeActivityAnimation object) { - return object.getLatestProgress(); - } - }; - - // The target will lose focus when alpha == 0, so keep a minimum value for it. - private static float keepMinimumAlpha(float transAlpha) { - return Math.max(transAlpha, 0.005f); - } - - private static void initializeAnimation(Animation animation, Rect bounds) { - final int width = bounds.width(); - final int height = bounds.height(); - animation.initialize(width, height, width, height); - } - - private void startBackAnimation() { - if (mEnteringTarget == null || mClosingTarget == null - || mCloseAnimation == null || mEnterAnimation == null) { - ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Entering target or closing target is null."); - return; - } - initializeAnimation(mCloseAnimation, mClosingTarget.localBounds); - initializeAnimation(mEnterAnimation, mEnteringTarget.localBounds); - - // Draw background with task background color. - if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) { - mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(), - mNextBackgroundColor == Color.TRANSPARENT - ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor() - : mNextBackgroundColor, - mTransaction); - } - } - - private void applyTransformTransaction(float progress) { - if (mClosingTarget == null || mEnteringTarget == null) { - return; - } - applyTransform(mClosingTarget.leash, progress, mCloseAnimation); - applyTransform(mEnteringTarget.leash, progress, mEnterAnimation); - mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId()); - mTransaction.apply(); - } - - private void applyTransform(SurfaceControl leash, float progress, Animation animation) { - mTransformation.clear(); - animation.getTransformationAt(progress, mTransformation); - mTransaction.setMatrix(leash, mTransformation.getMatrix(), mTmpFloat9); - mTransaction.setAlpha(leash, keepMinimumAlpha(mTransformation.getAlpha())); - mTransaction.setCornerRadius(leash, mCornerRadius); - } - - void finishAnimation() { - if (mCloseAnimation != null) { - mCloseAnimation.reset(); - mCloseAnimation = null; - } - if (mEnterAnimation != null) { - mEnterAnimation.reset(); - mEnterAnimation = null; - } - if (mEnteringTarget != null) { - mEnteringTarget.leash.release(); - mEnteringTarget = null; - } - if (mClosingTarget != null) { - mClosingTarget.leash.release(); - mClosingTarget = null; - } - if (mBackground != null) { - mBackground.removeBackground(mTransaction); - } - mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId()); - mTransaction.apply(); - mTransformation.clear(); - mLatestProgress = 0; - mNextBackgroundColor = Color.TRANSPARENT; - if (mFinishCallback != null) { - try { - mFinishCallback.onAnimationFinished(); - } catch (RemoteException e) { - e.printStackTrace(); - } - mFinishCallback = null; - } - mProgressSpring.animateToFinalPosition(0); - mProgressSpring.skipToEnd(); - } - - void onGestureProgress(@NonNull BackEvent backEvent) { - if (mEnteringTarget == null || mClosingTarget == null - || mCloseAnimation == null || mEnterAnimation == null) { - return; - } - - final float progress = backEvent.getProgress(); - - float springProgress = (progress > 0.1f - ? mapLinear(progress, 0.1f, 1f, TARGET_COMMIT_PROGRESS, 1f) - : mapLinear(progress, 0, 1f, 0f, TARGET_COMMIT_PROGRESS)) * SCALE_FACTOR; - - mProgressSpring.animateToFinalPosition(springProgress); - } - - static float mapLinear(float x, float a1, float a2, float b1, float b2) { - return b1 + (x - a1) * (b2 - b1) / (a2 - a1); - } - - void onGestureCommitted() { - if (mEnteringTarget == null || mClosingTarget == null - || mCloseAnimation == null || mEnterAnimation == null) { - finishAnimation(); - return; - } - mProgressSpring.cancel(); - - // Enter phase 2 of the animation - final ValueAnimator valueAnimator = ValueAnimator.ofFloat(mLatestProgress, 1f) - .setDuration(POST_ANIMATION_DURATION); - valueAnimator.setInterpolator(mDecelerateInterpolator); - valueAnimator.addUpdateListener(animation -> { - float progress = (float) animation.getAnimatedValue(); - applyTransformTransaction(progress); - }); - - valueAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - finishAnimation(); - } - }); - valueAnimator.start(); - } - - /** Load customize animation before animation start. */ - @Override - public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo, - int letterboxColor) { - if (animationInfo == null) { - return false; - } - final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo); - if (result != null) { - mCloseAnimation = result.mCloseAnimation; - mEnterAnimation = result.mEnterAnimation; - mNextBackgroundColor = result.mBackgroundColor; - return true; - } - return false; - } - - @Override - public BackAnimationRunner getRunner() { - return mBackAnimationRunner; - } - - private final class Callback extends IOnBackInvokedCallback.Default { - @Override - public void onBackStarted(BackMotionEvent backEvent) { - // in case we're still animating an onBackCancelled event, let's remove the finish- - // callback from the progress animator to prevent calling finishAnimation() before - // restarting a new animation - mProgressAnimator.removeOnBackCancelledFinishCallback(); - - mProgressAnimator.onBackStarted(backEvent, - CustomizeActivityAnimation.this::onGestureProgress); - } - - @Override - public void onBackProgressed(@NonNull BackMotionEvent backEvent) { - mProgressAnimator.onBackProgressed(backEvent); - } - - @Override - public void onBackCancelled() { - mProgressAnimator.onBackCancelled(CustomizeActivityAnimation.this::finishAnimation); - } - - @Override - public void onBackInvoked() { - mProgressAnimator.reset(); - onGestureCommitted(); - } - } - - private final class Runner extends IRemoteAnimationRunner.Default { - @Override - public void onAnimationStart( - int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - IRemoteAnimationFinishedCallback finishedCallback) { - ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to customize animation."); - for (RemoteAnimationTarget a : apps) { - if (a.mode == MODE_CLOSING) { - mClosingTarget = a; - } - if (a.mode == MODE_OPENING) { - mEnteringTarget = a; - } - } - if (mCloseAnimation == null || mEnterAnimation == null) { - ProtoLog.d(WM_SHELL_BACK_PREVIEW, - "No animation loaded, should choose cross-activity animation?"); - } - - startBackAnimation(); - mFinishCallback = finishedCallback; - } - - @Override - public void onAnimationCancelled() { - finishAnimation(); - } - } - - - static final class AnimationLoadResult { - Animation mCloseAnimation; - Animation mEnterAnimation; - int mBackgroundColor; - } - - /** - * Helper class to load custom animation. - */ - static class CustomAnimationLoader { - final TransitionAnimation mTransitionAnimation; - - CustomAnimationLoader(Context context) { - mTransitionAnimation = new TransitionAnimation( - context, false /* debug */, "CustomizeBackAnimation"); - } - - /** - * Load both enter and exit animation for the close activity transition. - * Note that the result is only valid if the exit animation has set and loaded success. - * If the entering animation has not set(i.e. 0), here will load the default entering - * animation for it. - * - * @param animationInfo The information of customize animation, which can be set from - * {@link Activity#overrideActivityTransition} and/or - * {@link LayoutParams#windowAnimations} - */ - AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) { - if (animationInfo.getPackageName().isEmpty()) { - return null; - } - final Animation close = loadAnimation(animationInfo, false); - if (close == null) { - return null; - } - final Animation open = loadAnimation(animationInfo, true); - AnimationLoadResult result = new AnimationLoadResult(); - result.mCloseAnimation = close; - result.mEnterAnimation = open; - result.mBackgroundColor = animationInfo.getCustomBackground(); - return result; - } - - /** - * Load enter or exit animation from CustomAnimationInfo - * @param animationInfo The information for customize animation. - * @param enterAnimation true when load for enter animation, false for exit animation. - * @return Loaded animation. - */ - @Nullable - Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo, - boolean enterAnimation) { - Animation a = null; - // Activity#overrideActivityTransition has higher priority than windowAnimations - // Try to get animation from Activity#overrideActivityTransition - if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0) - || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) { - a = mTransitionAnimation.loadAppTransitionAnimation( - animationInfo.getPackageName(), - enterAnimation ? animationInfo.getCustomEnterAnim() - : animationInfo.getCustomExitAnim()); - } else if (animationInfo.getWindowAnimations() != 0) { - // try to get animation from LayoutParams#windowAnimations - a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(), - animationInfo.getWindowAnimations(), enterAnimation - ? R.styleable.WindowAnimation_activityCloseEnterAnimation - : R.styleable.WindowAnimation_activityCloseExitAnimation, - false /* translucent */); - } - // Only allow to load default animation for opening target. - if (a == null && enterAnimation) { - a = loadDefaultOpenAnimation(); - } - if (a != null) { - ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a); - } else { - ProtoLog.e(WM_SHELL_BACK_PREVIEW, "No custom animation loaded"); - } - return a; - } - - private Animation loadDefaultOpenAnimation() { - return mTransitionAnimation.loadDefaultAnimationAttr( - R.styleable.WindowAnimation_activityCloseEnterAnimation, - false /* translucent */); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt new file mode 100644 index 000000000000..f33c5b9bd183 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.back + +import android.content.Context +import android.view.Choreographer +import android.view.SurfaceControl +import com.android.wm.shell.R +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.animation.Interpolators +import com.android.wm.shell.shared.annotations.ShellMainThread +import javax.inject.Inject +import kotlin.math.max + +/** Class that defines cross-activity animation. */ +@ShellMainThread +class DefaultCrossActivityBackAnimation +@Inject +constructor( + context: Context, + background: BackAnimationBackground, + rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer +) : + CrossActivityBackAnimation( + context, + background, + rootTaskDisplayAreaOrganizer, + SurfaceControl.Transaction(), + Choreographer.getInstance() + ) { + + private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN + private val enteringStartOffset = + context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset) + override val allowEnteringYShift = true + + override fun preparePreCommitEnteringRectMovement() { + // the entering target starts 96dp to the left of the screen edge... + startEnteringRect.set(startClosingRect) + startEnteringRect.offset(-enteringStartOffset, 0f) + // ...and gets scaled in sync with the closing target + targetEnteringRect.set(startEnteringRect) + targetEnteringRect.scaleCentered(MAX_SCALE) + } + + override fun onGestureCommitted() { + // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current + // coordinate of the gesture driven phase. Let's update the start and target rects and kick + // off the animator in the superclass + startClosingRect.set(currentClosingRect) + startEnteringRect.set(currentEnteringRect) + targetEnteringRect.set(backAnimRect) + targetClosingRect.set(backAnimRect) + targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f) + super.onGestureCommitted() + } + + override fun onPostCommitProgress(linearProgress: Float) { + super.onPostCommitProgress(linearProgress) + val closingAlpha = max(1f - linearProgress * 2, 0f) + val progress = postCommitInterpolator.getInterpolation(linearProgress) + currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress) + applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha) + currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress) + applyTransform(enteringTarget?.leash, currentEnteringRect, 1f) + applyTransaction() + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt index dba0a985411d..579a7943829e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt @@ -152,7 +152,8 @@ object PipUtils { "org.chromium.arc", 0) val isTv = AppGlobals.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK, 0) - isPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false) || + isPip2ExperimentEnabled = SystemProperties.getBoolean( + "persist.wm_shell.pip2", false) || (Flags.enablePip2Implementation() && !isArc && !isTv) } return isPip2ExperimentEnabled as Boolean diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index e729c7dd802b..991fbafed296 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -72,7 +72,6 @@ import com.android.wm.shell.compatui.CompatUIConfiguration; import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.compatui.CompatUIShellCommandHandler; import com.android.wm.shell.desktopmode.DesktopMode; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; @@ -88,6 +87,7 @@ import com.android.wm.shell.performance.PerfHintController; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.shared.ShellTransitions; import com.android.wm.shell.shared.annotations.ShellAnimationThread; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index a1910c5eb3a3..fb0a1ab3062e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -57,7 +57,6 @@ import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksLimiter; @@ -77,6 +76,7 @@ import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.shared.annotations.ShellAnimationThread; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java index 795bc1a7113b..d2895b149b2c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java @@ -16,9 +16,9 @@ package com.android.wm.shell.dagger.back; -import com.android.wm.shell.back.CrossActivityBackAnimation; import com.android.wm.shell.back.CrossTaskBackAnimation; -import com.android.wm.shell.back.CustomizeActivityAnimation; +import com.android.wm.shell.back.CustomCrossActivityBackAnimation; +import com.android.wm.shell.back.DefaultCrossActivityBackAnimation; import com.android.wm.shell.back.ShellBackAnimation; import com.android.wm.shell.back.ShellBackAnimationRegistry; @@ -47,7 +47,7 @@ public interface ShellBackAnimationModule { @Binds @ShellBackAnimation.CrossActivity ShellBackAnimation bindCrossActivityShellBackAnimation( - CrossActivityBackAnimation crossActivityBackAnimation); + DefaultCrossActivityBackAnimation defaultCrossActivityBackAnimation); /** Default cross task back animation */ @Binds @@ -59,5 +59,5 @@ public interface ShellBackAnimationModule { @Binds @ShellBackAnimation.CustomizeActivity ShellBackAnimation provideCustomizeActivityShellBackAnimation( - CustomizeActivityAnimation customizeActivityAnimation); + CustomCrossActivityBackAnimation customCrossActivityBackAnimation); } 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 9038aaad9178..0b7a3e838e88 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 @@ -39,6 +39,7 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterRe import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 6bbc8fec2894..7e0234ef8546 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -446,11 +446,6 @@ class DesktopModeTaskRepository { * Called when the desktop changes the number of visible freeform tasks. */ fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {} - - /** - * Called when the desktop stashed status changes. - */ - fun onStashedChanged(displayId: Int, stashed: Boolean) {} } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt new file mode 100644 index 000000000000..aa11a7d8a663 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.util.Log +import com.android.internal.logging.InstanceId +import com.android.internal.logging.InstanceIdSequence +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger +import com.android.wm.shell.dagger.WMSingleton +import javax.inject.Inject + +/** + * Log Aster UIEvents for desktop windowing mode. + */ +@WMSingleton +class DesktopModeUiEventLogger @Inject constructor( + private val mUiEventLogger: UiEventLogger, + private val mInstanceIdSequence: InstanceIdSequence +) { + /** + * Logs an event for a CUI, on a particular package. + * + * @param uid The user id associated with the package the user is interacting with + * @param packageName The name of the package the user is interacting with + * @param event The event type to generate + */ + fun log(uid: Int, packageName: String, event: DesktopUiEventEnum) { + if (packageName.isEmpty() || uid < 0) { + Log.d(TAG, "Skip logging since package name is empty or bad uid") + return + } + mUiEventLogger.log(event, uid, packageName) + } + + /** + * Retrieves a new instance id for a new interaction. + */ + fun getNewInstanceId(): InstanceId = mInstanceIdSequence.newInstanceId() + + /** + * Logs an event as part of a particular CUI, on a particular package. + * + * @param instanceId The id identifying an interaction, potentially taking place across multiple + * surfaces. There should be a new id generated for each distinct CUI. + * @param uid The user id associated with the package the user is interacting with + * @param packageName The name of the package the user is interacting with + * @param event The event type to generate + */ + fun logWithInstanceId( + instanceId: InstanceId, + uid: Int, + packageName: String, + event: DesktopUiEventEnum + ) { + if (packageName.isEmpty() || uid < 0) { + Log.d(TAG, "Skip logging since package name is empty or bad uid") + return + } + mUiEventLogger.logWithInstanceId(event, uid, packageName, instanceId) + } + + companion object { + /** + * Enums for logging desktop windowing mode UiEvents. + */ + enum class DesktopUiEventEnum(private val mId: Int) : UiEventLogger.UiEventEnum { + + @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the edge") + DESKTOP_WINDOW_EDGE_DRAG_RESIZE(1721), + + @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the corner") + DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722), + + @UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723), + + @UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode") + DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724); + + override fun getId(): Int = mId + } + + private const val TAG = "DesktopModeUiEventLogger" + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index b2bdbfefb9aa..2dc4573b4921 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -72,6 +72,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.recents.RecentsTransitionHandler import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.shared.annotations.ExternalThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.splitscreen.SplitScreenController @@ -227,7 +228,7 @@ class DesktopTasksController( bringDesktopAppsToFront(displayId, wct) if (Transitions.ENABLE_SHELL_TRANSITIONS) { - // TODO(b/255649902): ensure remote transition is supplied once state is introduced + // TODO(b/309014605): ensure remote transition is supplied once state is introduced val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT val handler = remoteTransition?.let { OneShotRemoteHandler(transitions.mainExecutor, remoteTransition) @@ -1374,16 +1375,6 @@ class DesktopTasksController( l -> l.onTasksVisibilityChanged(displayId, visibleTasksCount) } } - - override fun onStashedChanged(displayId: Int, stashed: Boolean) { - KtProtoLog.v( - WM_SHELL_DESKTOP_MODE, - "IDesktopModeImpl: onStashedChanged display=%d stashed=%b", - displayId, - stashed - ) - remoteListener.call { l -> l.onStashedChanged(displayId, stashed) } - } } init { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt index 3404d376fe92..0f88384ec2ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt @@ -25,6 +25,7 @@ import android.window.WindowContainerTransaction import androidx.annotation.VisibleForTesting import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionObserver import com.android.wm.shell.util.KtProtoLog diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt index 451e09c3cd9c..dae75f90e3ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt @@ -23,6 +23,7 @@ import android.view.WindowManager import android.window.TransitionInfo import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.KtProtoLog diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl index 8ed87f23bf40..8ebdfdcf4731 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl @@ -25,6 +25,6 @@ interface IDesktopTaskListener { /** Desktop tasks visibility has changed. Visible if at least 1 task is visible. */ oneway void onTasksVisibilityChanged(int displayId, int visibleTasksCount); - /** Desktop task stashed status has changed. */ + /** @deprecated this is no longer supported. */ oneway void onStashedChanged(int displayId, boolean stashed); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index a414a55eb633..e0e2e706d649 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -27,9 +27,9 @@ import android.view.SurfaceControl; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.WindowDecorViewModel; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index 9eaf7e4e2e21..c79eef7efb61 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss; import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING; @@ -83,6 +84,7 @@ public class KeyguardTransitionHandler * @see KeyguardTransitions */ private IRemoteTransition mExitTransition = null; + private IRemoteTransition mAppearTransition = null; private IRemoteTransition mOccludeTransition = null; private IRemoteTransition mOccludeByDreamTransition = null; private IRemoteTransition mUnoccludeTransition = null; @@ -170,26 +172,28 @@ public class KeyguardTransitionHandler // Choose a transition applicable for the changes and keyguard state. if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { - return startAnimation(mExitTransition, - "going-away", + return startAnimation(mExitTransition, "going-away", transition, info, startTransaction, finishTransaction, finishCallback); } + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0) { + return startAnimation(mAppearTransition, "appearing", + transition, info, startTransaction, finishTransaction, finishCallback); + } + + // Occlude/unocclude animations are only played if the keyguard is locked. if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) { if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) { if (hasOpeningDream(info)) { - return startAnimation(mOccludeByDreamTransition, - "occlude-by-dream", + return startAnimation(mOccludeByDreamTransition, "occlude-by-dream", transition, info, startTransaction, finishTransaction, finishCallback); } else { - return startAnimation(mOccludeTransition, - "occlude", + return startAnimation(mOccludeTransition, "occlude", transition, info, startTransaction, finishTransaction, finishCallback); } } else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { - return startAnimation(mUnoccludeTransition, - "unocclude", + return startAnimation(mUnoccludeTransition, "unocclude", transition, info, startTransaction, finishTransaction, finishCallback); } } @@ -359,11 +363,13 @@ public class KeyguardTransitionHandler @Override public void register( IRemoteTransition exitTransition, + IRemoteTransition appearTransition, IRemoteTransition occludeTransition, IRemoteTransition occludeByDreamTransition, IRemoteTransition unoccludeTransition) { mMainExecutor.execute(() -> { mExitTransition = exitTransition; + mAppearTransition = appearTransition; mOccludeTransition = occludeTransition; mOccludeByDreamTransition = occludeByDreamTransition; mUnoccludeTransition = unoccludeTransition; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java index 4215b2cc5f29..b7245b91f36c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java @@ -35,6 +35,7 @@ public interface KeyguardTransitions { */ default void register( @NonNull IRemoteTransition unlockTransition, + @NonNull IRemoteTransition appearTransition, @NonNull IRemoteTransition occludeTransition, @NonNull IRemoteTransition occludeByDreamTransition, @NonNull IRemoteTransition unoccludeTransition) {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index fdde3ee01264..2082756feda7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -824,12 +824,10 @@ public class PipTransition extends PipTransitionController { @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TaskInfo taskInfo) { startTransaction.apply(); - if (info.getChanges().isEmpty()) { + final TransitionInfo.Change pipChange = findCurrentPipTaskChange(info); + if (pipChange == null) { ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "removePipImmediately is called with empty changes"); - } else { - finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(), - mPipDisplayLayoutState.getDisplayBounds()); + "removePipImmediately is called without pip change"); } mPipOrganizer.onExitPipFinished(taskInfo); finishCallback.onTransitionFinished(null); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index a8611d966a29..c53e7fe00598 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -50,9 +50,9 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.shared.annotations.ExternalThread; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellCommandHandler; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java index e419462012e3..e07e1b460168 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java @@ -45,6 +45,7 @@ import android.window.SplashScreenView; import com.android.internal.R; +import java.io.Closeable; import java.util.function.LongConsumer; /** @@ -100,7 +101,7 @@ public class SplashscreenIconDrawableFactory { * Drawable pre-drawing the scaled icon in a separate thread to increase the speed of the * final drawing. */ - private static class ImmobileIconDrawable extends Drawable { + private static class ImmobileIconDrawable extends Drawable implements Closeable { private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG); private final Matrix mMatrix = new Matrix(); @@ -154,6 +155,16 @@ public class SplashscreenIconDrawableFactory { public int getOpacity() { return 1; } + + @Override + public void close() { + synchronized (mPaint) { + if (mIconBitmap != null) { + mIconBitmap.recycle(); + mIconBitmap = null; + } + } + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java index 1897560deed7..6adbe4f7ce92 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java @@ -49,8 +49,12 @@ public class PerfettoTransitionTracer implements TransitionTracer { public PerfettoTransitionTracer() { Producer.init(InitArguments.DEFAULTS); - mDataSource.register( - new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)); + DataSourceParams params = + new DataSourceParams.Builder() + .setBufferExhaustedPolicy( + PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT) + .build(); + mDataSource.register(params); } /** @@ -214,8 +218,6 @@ public class PerfettoTransitionTracer implements TransitionTracer { } os.end(mappingsToken); - - ctx.flush(); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index dfdb58a50892..9afb057ffbe5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -84,12 +84,12 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.splitscreen.SplitScreenController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 4c347adcb486..4d4dc3c72420 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -66,8 +66,8 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder; import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index de6c03549f0e..541825437c86 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -52,7 +52,7 @@ import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml index 4dd14f4011d0..f69a90cc793f 100644 --- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml @@ -91,6 +91,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml index 5c86a386fc6c..b76d06565700 100644 --- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml @@ -91,6 +91,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt index bc486c277aa5..984abf8cf8b4 100644 --- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt @@ -32,7 +32,7 @@ import org.junit.runners.Parameterized /** * Test launching a new activity from bubble. * - * To run this test: `atest WMShellFlickerTests:MultiBubblesScreen` + * To run this test: `atest WMShellFlickerTestsBubbles:ChangeActiveActivityFromBubbleTest` * * Actions: * ``` diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt index 2a9b1078afe3..886b70c5e464 100644 --- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt @@ -35,7 +35,7 @@ import org.junit.runners.Parameterized /** * Test launching a new activity from bubble. * - * To run this test: `atest WMShellFlickerTests:DismissBubbleScreen` + * To run this test: `atest WMShellFlickerTestsBubbles:DragToDismissBubbleScreenTest` * * Actions: * ``` diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt index 9ef49c1c9e7e..2ee53f4fce66 100644 --- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized /** * Test launching a new activity from bubble. * - * To run this test: `atest WMShellFlickerTests:OpenActivityFromBubbleOnLocksreenTest` + * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleOnLocksreenTest` * * Actions: * ``` diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt index ef7fbfb79beb..463fe0e60da3 100644 --- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt @@ -29,7 +29,7 @@ import org.junit.runners.Parameterized /** * Test launching a new activity from bubble. * - * To run this test: `atest WMShellFlickerTests:ExpandBubbleScreen` + * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleTest` * * Actions: * ``` diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt index 87224b151b78..8df50567a29c 100644 --- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt @@ -29,7 +29,7 @@ import org.junit.runners.Parameterized /** * Test creating a bubble notification * - * To run this test: `atest WMShellFlickerTests:LaunchBubbleScreen` + * To run this test: `atest WMShellFlickerTestsBubbles:SendBubbleNotificationTest` * * Actions: * ``` diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml index aa70c093b847..041978c371ff 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml @@ -91,6 +91,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml index c7c804f2361a..a66dfb4566f9 100644 --- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml @@ -91,6 +91,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt index 17cace0da739..d485b82f5ddb 100644 --- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt @@ -21,6 +21,7 @@ import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd +import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart import android.tools.flicker.assertors.assertions.AppWindowRemainInsideDisplayBounds @@ -133,9 +134,8 @@ class DesktopModeFlickerScenarios { } ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + listOf( - AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP), + AppWindowIsVisibleAlways(Components.DESKTOP_MODE_APP), AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP), AppWindowRemainInsideDisplayBounds(Components.DESKTOP_MODE_APP), ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml index 214bdfaa0743..85715db3d952 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml @@ -91,6 +91,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index f99b4b2beef0..f6f3aa49bc6e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -120,7 +120,7 @@ public class BackAnimationControllerTest extends ShellTestCase { private TestableContentResolver mContentResolver; private TestableLooper mTestableLooper; - private CrossActivityBackAnimation mCrossActivityBackAnimation; + private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation; private CrossTaskBackAnimation mCrossTaskBackAnimation; private ShellBackAnimationRegistry mShellBackAnimationRegistry; @@ -135,13 +135,14 @@ public class BackAnimationControllerTest extends ShellTestCase { ANIMATION_ENABLED); mTestableLooper = TestableLooper.get(this); mShellInit = spy(new ShellInit(mShellExecutor)); - mCrossActivityBackAnimation = new CrossActivityBackAnimation(mContext, mAnimationBackground, - mRootTaskDisplayAreaOrganizer); + mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext, + mAnimationBackground, mRootTaskDisplayAreaOrganizer); mCrossTaskBackAnimation = new CrossTaskBackAnimation(mContext, mAnimationBackground); mShellBackAnimationRegistry = - new ShellBackAnimationRegistry(mCrossActivityBackAnimation, mCrossTaskBackAnimation, - /* dialogCloseAnimation= */ null, - new CustomizeActivityAnimation(mContext, mAnimationBackground), + new ShellBackAnimationRegistry(mDefaultCrossActivityBackAnimation, + mCrossTaskBackAnimation, /* dialogCloseAnimation= */ null, + new CustomCrossActivityBackAnimation(mContext, mAnimationBackground, + mRootTaskDisplayAreaOrganizer), /* defaultBackToHomeAnimation= */ null); mController = new BackAnimationController( @@ -582,7 +583,7 @@ public class BackAnimationControllerTest extends ShellTestCase { @Test public void testBackToActivity() throws RemoteException { verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY, - mCrossActivityBackAnimation.getRunner()); + mDefaultCrossActivityBackAnimation.getRunner()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt new file mode 100644 index 000000000000..8bf011192347 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License.f + */ +package com.android.wm.shell.back + +import android.app.ActivityManager +import android.app.ActivityManager.RunningTaskInfo +import android.app.AppCompatTaskInfo +import android.app.WindowConfiguration +import android.graphics.Color +import android.graphics.Point +import android.graphics.Rect +import android.os.RemoteException +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.Choreographer +import android.view.RemoteAnimationTarget +import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction +import android.view.animation.Animation +import android.window.BackEvent +import android.window.BackMotionEvent +import android.window.BackNavigationInfo +import androidx.test.filters.SmallTest +import com.android.internal.policy.TransitionAnimation +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.ShellTestCase +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import junit.framework.TestCase.assertEquals +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +class CustomCrossActivityBackAnimationTest : ShellTestCase() { + @Mock private lateinit var backAnimationBackground: BackAnimationBackground + @Mock private lateinit var mockCloseAnimation: Animation + @Mock private lateinit var mockOpenAnimation: Animation + @Mock private lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + @Mock private lateinit var transitionAnimation: TransitionAnimation + @Mock private lateinit var appCompatTaskInfo: AppCompatTaskInfo + @Mock private lateinit var transaction: Transaction + + private lateinit var customCrossActivityBackAnimation: CustomCrossActivityBackAnimation + private lateinit var customAnimationLoader: CustomAnimationLoader + + @Before + @Throws(Exception::class) + fun setUp() { + customAnimationLoader = CustomAnimationLoader(transitionAnimation) + customCrossActivityBackAnimation = + CustomCrossActivityBackAnimation( + context, + backAnimationBackground, + rootTaskDisplayAreaOrganizer, + transaction, + mock(Choreographer::class.java), + customAnimationLoader + ) + + whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(OPEN_RES_ID))) + .thenReturn(mockOpenAnimation) + whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(CLOSE_RES_ID))) + .thenReturn(mockCloseAnimation) + whenever(transaction.setColor(any(), any())).thenReturn(transaction) + whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction) + whenever(transaction.setCrop(any(), any())).thenReturn(transaction) + whenever(transaction.setRelativeLayer(any(), any(), anyInt())).thenReturn(transaction) + spy(customCrossActivityBackAnimation) + } + + @Test + @Throws(InterruptedException::class) + fun receiveFinishAfterInvoke() { + val finishCalled = startCustomAnimation() + try { + customCrossActivityBackAnimation.getRunner().callback.onBackInvoked() + } catch (r: RemoteException) { + Assert.fail("onBackInvoked throw remote exception") + } + finishCalled.await(1, TimeUnit.SECONDS) + } + + @Test + @Throws(InterruptedException::class) + fun receiveFinishAfterCancel() { + val finishCalled = startCustomAnimation() + try { + customCrossActivityBackAnimation.getRunner().callback.onBackCancelled() + } catch (r: RemoteException) { + Assert.fail("onBackCancelled throw remote exception") + } + finishCalled.await(1, TimeUnit.SECONDS) + } + + @Test + @Throws(InterruptedException::class) + fun receiveFinishWithoutAnimationAfterInvoke() { + val finishCalled = startCustomAnimation(targets = arrayOf()) + try { + customCrossActivityBackAnimation.getRunner().callback.onBackInvoked() + } catch (r: RemoteException) { + Assert.fail("onBackInvoked throw remote exception") + } + finishCalled.await(1, TimeUnit.SECONDS) + } + + @Test + fun testLoadCustomAnimation() { + testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 0) + } + + @Test + fun testLoadCustomAnimationNoEnter() { + testLoadCustomAnimation(0, CLOSE_RES_ID, 0) + } + + @Test + fun testLoadWindowAnimations() { + testLoadCustomAnimation(0, 0, 30) + } + + @Test + fun testCustomAnimationHigherThanWindowAnimations() { + testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 30) + } + + private fun testLoadCustomAnimation(enterResId: Int, exitResId: Int, windowAnimations: Int) { + val builder = + BackNavigationInfo.Builder() + .setCustomAnimation(PACKAGE_NAME, enterResId, exitResId, Color.GREEN) + .setWindowAnimations(PACKAGE_NAME, windowAnimations) + val info = builder.build().customAnimationInfo!! + whenever( + transitionAnimation.loadAnimationAttr( + eq(PACKAGE_NAME), + eq(windowAnimations), + anyInt(), + anyBoolean() + ) + ) + .thenReturn(mockCloseAnimation) + whenever(transitionAnimation.loadDefaultAnimationAttr(anyInt(), anyBoolean())) + .thenReturn(mockOpenAnimation) + val result = customAnimationLoader.loadAll(info)!! + if (exitResId != 0) { + if (enterResId == 0) { + verify(transitionAnimation, never()) + .loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(enterResId)) + verify(transitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean()) + } else { + assertEquals(result.enterAnimation, mockOpenAnimation) + } + assertEquals(result.backgroundColor.toLong(), Color.GREEN.toLong()) + assertEquals(result.closeAnimation, mockCloseAnimation) + verify(transitionAnimation, never()) + .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean()) + } else if (windowAnimations != 0) { + verify(transitionAnimation, times(2)) + .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean()) + Assert.assertEquals(result.closeAnimation, mockCloseAnimation) + } + } + + private fun startCustomAnimation( + targets: Array<RemoteAnimationTarget> = + arrayOf(createAnimationTarget(false), createAnimationTarget(true)) + ): CountDownLatch { + val backNavigationInfo = + BackNavigationInfo.Builder() + .setCustomAnimation(PACKAGE_NAME, OPEN_RES_ID, CLOSE_RES_ID, /*backgroundColor*/ 0) + .build() + customCrossActivityBackAnimation.prepareNextAnimation( + backNavigationInfo.customAnimationInfo, + 0 + ) + val finishCalled = CountDownLatch(1) + val finishCallback = Runnable { finishCalled.countDown() } + customCrossActivityBackAnimation + .getRunner() + .startAnimation(targets, null, null, finishCallback) + customCrossActivityBackAnimation.runner.callback.onBackStarted(backMotionEventFrom(0f, 0f)) + if (targets.isNotEmpty()) { + verify(mockCloseAnimation) + .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE)) + verify(mockOpenAnimation) + .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE)) + } + return finishCalled + } + + private fun backMotionEventFrom(touchX: Float, progress: Float) = + BackMotionEvent( + /* touchX = */ touchX, + /* touchY = */ 0f, + /* progress = */ progress, + /* velocityX = */ 0f, + /* velocityY = */ 0f, + /* triggerBack = */ false, + /* swipeEdge = */ BackEvent.EDGE_LEFT, + /* departingAnimationTarget = */ null + ) + + private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget { + val topWindowLeash = SurfaceControl() + val taskInfo = RunningTaskInfo() + taskInfo.appCompatTaskInfo = appCompatTaskInfo + taskInfo.taskDescription = ActivityManager.TaskDescription() + return RemoteAnimationTarget( + 1, + if (open) RemoteAnimationTarget.MODE_OPENING else RemoteAnimationTarget.MODE_CLOSING, + topWindowLeash, + false, + Rect(), + Rect(), + -1, + Point(0, 0), + Rect(0, 0, BOUND_SIZE, BOUND_SIZE), + Rect(), + WindowConfiguration(), + true, + null, + null, + taskInfo, + false, + -1 + ) + } + + companion object { + private const val BOUND_SIZE = 100 + private const val OPEN_RES_ID = 1000 + private const val CLOSE_RES_ID = 1001 + private const val PACKAGE_NAME = "TestPackage" + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java deleted file mode 100644 index 158d640dca30..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.back; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -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.verify; - -import android.app.WindowConfiguration; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.RemoteException; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.view.Choreographer; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.view.animation.Animation; -import android.window.BackNavigationInfo; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -@SmallTest -@TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) -public class CustomizeActivityAnimationTest extends ShellTestCase { - private static final int BOUND_SIZE = 100; - @Mock - private BackAnimationBackground mBackAnimationBackground; - @Mock - private Animation mMockCloseAnimation; - @Mock - private Animation mMockOpenAnimation; - - private CustomizeActivityAnimation mCustomizeActivityAnimation; - - @Before - public void setUp() throws Exception { - mCustomizeActivityAnimation = new CustomizeActivityAnimation(mContext, - mBackAnimationBackground, mock(SurfaceControl.Transaction.class), - mock(Choreographer.class)); - spyOn(mCustomizeActivityAnimation); - spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation); - } - - RemoteAnimationTarget createAnimationTarget(boolean open) { - SurfaceControl topWindowLeash = new SurfaceControl(); - return new RemoteAnimationTarget(1, - open ? RemoteAnimationTarget.MODE_OPENING : RemoteAnimationTarget.MODE_CLOSING, - topWindowLeash, false, new Rect(), new Rect(), -1, - new Point(0, 0), new Rect(0, 0, BOUND_SIZE, BOUND_SIZE), new Rect(), - new WindowConfiguration(), true, null, null, null, false, -1); - } - - @Test - public void receiveFinishAfterInvoke() throws InterruptedException { - spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); - doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) - .loadAnimation(any(), eq(false)); - doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) - .loadAnimation(any(), eq(true)); - - mCustomizeActivityAnimation.prepareNextAnimation( - new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0); - final RemoteAnimationTarget close = createAnimationTarget(false); - final RemoteAnimationTarget open = createAnimationTarget(true); - // start animation with remote animation targets - final CountDownLatch finishCalled = new CountDownLatch(1); - final Runnable finishCallback = finishCalled::countDown; - mCustomizeActivityAnimation - .getRunner() - .startAnimation( - new RemoteAnimationTarget[] {close, open}, null, null, finishCallback); - verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), - eq(BOUND_SIZE), eq(BOUND_SIZE)); - verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), - eq(BOUND_SIZE), eq(BOUND_SIZE)); - - try { - mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked(); - } catch (RemoteException r) { - fail("onBackInvoked throw remote exception"); - } - verify(mCustomizeActivityAnimation).onGestureCommitted(); - finishCalled.await(1, TimeUnit.SECONDS); - } - - @Test - public void receiveFinishAfterCancel() throws InterruptedException { - spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); - doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) - .loadAnimation(any(), eq(false)); - doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) - .loadAnimation(any(), eq(true)); - - mCustomizeActivityAnimation.prepareNextAnimation( - new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0); - final RemoteAnimationTarget close = createAnimationTarget(false); - final RemoteAnimationTarget open = createAnimationTarget(true); - // start animation with remote animation targets - final CountDownLatch finishCalled = new CountDownLatch(1); - final Runnable finishCallback = finishCalled::countDown; - mCustomizeActivityAnimation - .getRunner() - .startAnimation( - new RemoteAnimationTarget[] {close, open}, null, null, finishCallback); - verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), - eq(BOUND_SIZE), eq(BOUND_SIZE)); - verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), - eq(BOUND_SIZE), eq(BOUND_SIZE)); - - try { - mCustomizeActivityAnimation.getRunner().getCallback().onBackCancelled(); - } catch (RemoteException r) { - fail("onBackCancelled throw remote exception"); - } - finishCalled.await(1, TimeUnit.SECONDS); - } - - @Test - public void receiveFinishWithoutAnimationAfterInvoke() throws InterruptedException { - mCustomizeActivityAnimation.prepareNextAnimation( - new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0); - // start animation without any remote animation targets - final CountDownLatch finishCalled = new CountDownLatch(1); - final Runnable finishCallback = finishCalled::countDown; - mCustomizeActivityAnimation - .getRunner() - .startAnimation(new RemoteAnimationTarget[] {}, null, null, finishCallback); - - try { - mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked(); - } catch (RemoteException r) { - fail("onBackInvoked throw remote exception"); - } - verify(mCustomizeActivityAnimation).onGestureCommitted(); - finishCalled.await(1, TimeUnit.SECONDS); - } - - @Test - public void testLoadCustomAnimation() { - testLoadCustomAnimation(10, 20, 0); - } - - @Test - public void testLoadCustomAnimationNoEnter() { - testLoadCustomAnimation(0, 10, 0); - } - - @Test - public void testLoadWindowAnimations() { - testLoadCustomAnimation(0, 0, 30); - } - - @Test - public void testCustomAnimationHigherThanWindowAnimations() { - testLoadCustomAnimation(10, 20, 30); - } - - private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) { - final String testPackage = "TestPackage"; - BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() - .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN) - .setWindowAnimations(testPackage, windowAnimations); - final BackNavigationInfo.CustomAnimationInfo info = builder.build() - .getCustomAnimationInfo(); - - doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader - .mTransitionAnimation) - .loadAppTransitionAnimation(eq(testPackage), eq(enterResId)); - doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader - .mTransitionAnimation) - .loadAppTransitionAnimation(eq(testPackage), eq(exitResId)); - doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader - .mTransitionAnimation) - .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean()); - doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader - .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean()); - - CustomizeActivityAnimation.AnimationLoadResult result = - mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info); - - if (exitResId != 0) { - if (enterResId == 0) { - verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, - never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId)); - verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation) - .loadDefaultAnimationAttr(anyInt(), anyBoolean()); - } else { - assertEquals(result.mEnterAnimation, mMockOpenAnimation); - } - assertEquals(result.mBackgroundColor, Color.GREEN); - assertEquals(result.mCloseAnimation, mMockCloseAnimation); - verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never()) - .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean()); - } else if (windowAnimations != 0) { - verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, - times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean()); - assertEquals(result.mCloseAnimation, mMockCloseAnimation); - } - } -} 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 60a7dcda5351..2a2483df0792 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 @@ -41,6 +41,7 @@ import com.android.modules.utils.testing.ExtendedMockitoRule 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.shared.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder import com.android.wm.shell.transition.Transitions diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt new file mode 100644 index 000000000000..285e5b6a04a5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.internal.logging.InstanceId +import com.android.internal.logging.InstanceIdSequence +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.Companion.DesktopUiEventEnum.DESKTOP_WINDOW_EDGE_DRAG_RESIZE +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Test class for [DesktopModeUiEventLogger] + * + * Usage: atest WMShellUnitTests:DesktopModeUiEventLoggerTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopModeUiEventLoggerTest : ShellTestCase() { + private lateinit var uiEventLoggerFake: UiEventLoggerFake + private lateinit var logger: DesktopModeUiEventLogger + private val instanceIdSequence = InstanceIdSequence(10) + + + @Before + fun setUp() { + uiEventLoggerFake = UiEventLoggerFake() + logger = DesktopModeUiEventLogger(uiEventLoggerFake, instanceIdSequence) + } + + @Test + fun log_invalidUid_eventNotLogged() { + logger.log(-1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0) + } + + @Test + fun log_emptyPackageName_eventNotLogged() { + logger.log(UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0) + } + + @Test + fun log_eventLogged() { + val event = + DESKTOP_WINDOW_EDGE_DRAG_RESIZE + logger.log(UID, PACKAGE_NAME, event) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id) + assertThat(uiEventLoggerFake[0].instanceId).isNull() + assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID) + assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME) + } + + @Test + fun getNewInstanceId() { + val first = logger.getNewInstanceId() + assertThat(first).isNotEqualTo(logger.getNewInstanceId()) + } + + @Test + fun logWithInstanceId_invalidUid_eventNotLogged() { + logger.logWithInstanceId(INSTANCE_ID, -1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0) + } + + @Test + fun logWithInstanceId_emptyPackageName_eventNotLogged() { + logger.logWithInstanceId(INSTANCE_ID, UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0) + } + + @Test + fun logWithInstanceId_eventLogged() { + val event = + DESKTOP_WINDOW_EDGE_DRAG_RESIZE + logger.logWithInstanceId(INSTANCE_ID, UID, PACKAGE_NAME, event) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id) + assertThat(uiEventLoggerFake[0].instanceId).isEqualTo(INSTANCE_ID) + assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID) + assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME) + } + + + companion object { + private val INSTANCE_ID = InstanceId.fakeInstanceId(0) + private const val UID = 10 + private const val PACKAGE_NAME = "com.foo" + } +} 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 7e55628b5641..f67da5573b7d 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 @@ -79,6 +79,7 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplit import com.android.wm.shell.draganddrop.DragAndDropController import com.android.wm.shell.recents.RecentsTransitionHandler import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt index 539d5b86453f..3c488cac6edd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt @@ -32,6 +32,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.transition.TransitionInfoBuilder import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.StubTransaction diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java index 665077be3af7..cd68c6996578 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java @@ -35,8 +35,8 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.windowdecor.WindowDecorViewModel; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index 240324ba4420..884cb6ec9f74 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -67,8 +67,8 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 7d19f3cbf659..aa2cee79fcfc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -60,8 +60,8 @@ import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue -import com.android.wm.shell.desktopmode.DesktopModeStatus import com.android.wm.shell.desktopmode.DesktopTasksController +import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.sysui.KeyguardChangeListener import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 4eb44d747486..8b8cd119effd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -75,7 +75,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.tests.R; import org.junit.Before; diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index 5cf5a1d00815..f1ee3256dbee 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -467,10 +467,10 @@ void MouseCursorController::startAnimationLocked() REQUIRES(mLock) { std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1); /* - * Using ui::ADISPLAY_ID_NONE for displayId here to avoid removing the callback + * Using ui::LogicalDisplayId::INVALID for displayId here to avoid removing the callback * if a TouchSpotController with the same display is removed. */ - mContext.addAnimationCallback(ui::ADISPLAY_ID_NONE, func); + mContext.addAnimationCallback(ui::LogicalDisplayId::INVALID, func); } } // namespace android diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 70e5c2455d81..c6430f7f36ff 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -109,7 +109,7 @@ private: struct Locked { Presentation presentation; - ui::LogicalDisplayId pointerDisplayId = ui::ADISPLAY_ID_NONE; + ui::LogicalDisplayId pointerDisplayId = ui::LogicalDisplayId::INVALID; std::vector<gui::DisplayInfo> mDisplayInfos; std::unordered_map<ui::LogicalDisplayId, TouchSpotController> spotControllers; diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 070c90c372ff..e147c567ae2d 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -174,7 +174,7 @@ private: int32_t layer{0}; float alpha{1.0f}; SpriteTransformationMatrix transformationMatrix; - ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_DEFAULT}; + ui::LogicalDisplayId displayId{ui::LogicalDisplayId::DEFAULT}; sp<SurfaceControl> surfaceControl; int32_t surfaceWidth{0}; diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 7a133801f514..2dcb1f1d1650 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -166,7 +166,7 @@ protected: PointerControllerTest(); ~PointerControllerTest(); - void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT); + void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT); sp<MockSprite> mPointerSprite; sp<MockPointerControllerPolicyInterface> mPolicy; @@ -335,23 +335,23 @@ TEST_F(PointerControllerTest, updatesSkipScreenshotFlagForTouchSpots) { // Update spots to sync state with sprite mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits, - ui::ADISPLAY_ID_DEFAULT); + ui::LogicalDisplayId::DEFAULT); testing::Mock::VerifyAndClearExpectations(testSpotSprite.get()); // Marking the display to skip screenshot should update sprite as well - mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, true); + mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, true); EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true)); // Update spots to sync state with sprite mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits, - ui::ADISPLAY_ID_DEFAULT); + ui::LogicalDisplayId::DEFAULT); testing::Mock::VerifyAndClearExpectations(testSpotSprite.get()); // Reset flag and verify again - mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, false); + mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, false); EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false)); mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits, - ui::ADISPLAY_ID_DEFAULT); + ui::LogicalDisplayId::DEFAULT); testing::Mock::VerifyAndClearExpectations(testSpotSprite.get()); } diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml index 3cc64a6ef266..76fa7b921e77 100644 --- a/packages/PrintSpooler/res/values-night/themes.xml +++ b/packages/PrintSpooler/res/values-night/themes.xml @@ -24,6 +24,7 @@ <style name="Theme.SelectPrinterActivity" parent="android:style/Theme.DeviceDefault"> <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault"> diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml index bd9602540878..22842f724036 100644 --- a/packages/PrintSpooler/res/values/themes.xml +++ b/packages/PrintSpooler/res/values/themes.xml @@ -24,6 +24,7 @@ parent="android:style/Theme.DeviceDefault.Light"> <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item> <item name="android:windowLightStatusBar">true</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault.Light"> diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index d25d5dcaac87..ff09084e24cd 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -785,6 +785,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } else { onPrinterUnavailable(printerInfo); } + if (mPrinterRegistry != null) { + mPrinterRegistry.setTrackedPrinter(mCurrentPrinter.getId()); + } mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerInfo); diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index 05507e0ea11d..493818b2e74f 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -80,14 +80,15 @@ public class FooterPreference extends Preference { continue; } final URLSpan urlSpan = (URLSpan) clickable; - if (!urlSpan.getURL().startsWith(INTENT_URL_PREFIX)) { + final String url = urlSpan.getURL(); + if (url == null || !url.startsWith(INTENT_URL_PREFIX)) { continue; } final int start = spannable.getSpanStart(urlSpan); final int end = spannable.getSpanEnd(urlSpan); spannable.removeSpan(urlSpan); try { - final Intent intent = Intent.parseUri(urlSpan.getURL(), Intent.URI_INTENT_SCHEME); + final Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); final ClickableSpan clickableSpan = new ClickableSpan() { @Override @@ -98,7 +99,7 @@ public class FooterPreference extends Preference { }; spannable.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } catch (URISyntaxException e) { - Log.e(TAG, "Invalid URI " + urlSpan.getURL(), e); + Log.e(TAG, "Invalid URI " + url, e); } } title.setText(spannable); diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index e7823df7ce0e..45667f55882d 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -41,7 +41,7 @@ subprojects { defaultConfig { minSdk = 21 - targetSdk = 34 + targetSdk = 35 } } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt index da1ee77bcbfb..e867a8f0a8d1 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt @@ -21,6 +21,7 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.annotation.VisibleForTesting import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable @@ -30,7 +31,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier -import androidx.core.view.WindowCompat import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavGraphBuilder @@ -82,7 +82,7 @@ open class BrowseActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setTheme(R.style.Theme_SpaLib) super.onCreate(savedInstanceState) - WindowCompat.setDecorFitsSystemWindows(window, false) + enableEdgeToEdge() spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK) setContent { diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt index 36cd136602f3..9a344c3d7f14 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt @@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LocalContentColor @@ -42,11 +43,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Surface import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.SideEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableFloatStateOf @@ -79,7 +80,12 @@ import kotlin.math.abs import kotlin.math.max import kotlin.math.roundToInt -@OptIn(ExperimentalMaterial3Api::class) +private val windowInsets: WindowInsets + @Composable + @NonRestartableComposable + get() = WindowInsets.safeDrawing + .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top) + @Composable internal fun CustomizedTopAppBar( title: @Composable () -> Unit, @@ -91,7 +97,7 @@ internal fun CustomizedTopAppBar( titleTextStyle = MaterialTheme.typography.titleMedium, navigationIcon = navigationIcon, actions = actions, - windowInsets = TopAppBarDefaults.windowInsets, + windowInsets = windowInsets, colors = topAppBarColors(), ) } @@ -118,7 +124,7 @@ internal fun CustomizedLargeTopAppBar( navigationIcon = navigationIcon, actions = actions, colors = topAppBarColors(), - windowInsets = TopAppBarDefaults.windowInsets, + windowInsets = windowInsets, pinnedHeight = ContainerHeight, scrollBehavior = scrollBehavior, ) @@ -336,7 +342,7 @@ private fun TwoRowsTopAppBar( Modifier.draggable( orientation = Orientation.Vertical, state = rememberDraggableState { delta -> - scrollBehavior.state.heightOffset = scrollBehavior.state.heightOffset + delta + scrollBehavior.state.heightOffset += delta }, onDragStopped = { velocity -> settleAppBar( @@ -411,6 +417,7 @@ private fun TwoRowsTopAppBar( * (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and * the actions are optional. * + * @param modifier a [Modifier] * @param heightPx the total height this layout is capped to * @param navigationIconContentColor the content color that will be applied via a * [LocalContentColor] when composing the navigation icon diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt index a49b358ca782..4a7937a3c2ac 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt @@ -22,9 +22,11 @@ import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.ExperimentalMaterial3Api @@ -92,6 +94,7 @@ fun SearchScaffold( ) }, containerColor = MaterialTheme.colorScheme.settingsBackground, + contentWindowInsets = WindowInsets.safeDrawing, ) { paddingValues -> Box( Modifier diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt index af7a14647570..4cf741e517be 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt @@ -23,7 +23,9 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -57,6 +59,7 @@ fun SettingsScaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { SettingsTopAppBar(title, scrollBehavior, actions) }, containerColor = MaterialTheme.colorScheme.settingsBackground, + contentWindowInsets = WindowInsets.safeDrawing, ) { paddingValues -> Box(Modifier.padding(paddingValues.horizontalValues())) { content(paddingValues.verticalValues()) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt index fc409302a2eb..4726dadc3688 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt @@ -21,7 +21,9 @@ import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -55,7 +57,10 @@ fun SuwScaffold( content: @Composable () -> Unit, ) { ActivityTitle(title) - Scaffold(containerColor = MaterialTheme.colorScheme.settingsBackground) { innerPadding -> + Scaffold( + containerColor = MaterialTheme.colorScheme.settingsBackground, + contentWindowInsets = WindowInsets.safeDrawing, + ) { innerPadding -> BoxWithConstraints( Modifier .padding(innerPadding) diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java index 57bde56b4c04..c3651423b487 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java @@ -128,7 +128,7 @@ public class RecentAppOpsAccess { final long now = mClock.millis(); final UserManager um = mContext.getSystemService(UserManager.class); final List<UserHandle> profiles = um.getUserProfiles(); - ArrayMap<UserHandle, Boolean> shouldIncludeAppsByUsers = new ArrayMap<>(); + ArrayMap<UserHandle, Boolean> shouldHideAppsByUsers = new ArrayMap<>(); for (int i = 0; i < appOpsCount; ++i) { AppOpsManager.PackageOps ops = appOps.get(i); @@ -136,13 +136,13 @@ public class RecentAppOpsAccess { int uid = ops.getUid(); UserHandle user = UserHandle.getUserHandleForUid(uid); - if (!shouldIncludeAppsByUsers.containsKey(user)) { - shouldIncludeAppsByUsers.put(user, shouldHideUser(um, user)); + if (!shouldHideAppsByUsers.containsKey(user)) { + shouldHideAppsByUsers.put(user, shouldHideUser(um, user)); } // Don't show apps belonging to background users except for profiles that shouldn't // be shown in quiet mode. - if (!profiles.contains(user) || !shouldIncludeAppsByUsers.get(user)) { + if (!profiles.contains(user) || shouldHideAppsByUsers.get(user)) { continue; } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index be3f4108fdd1..888e39593a2e 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -274,6 +274,9 @@ public class SecureSettings { Settings.Secure.SCREEN_RESOLUTION_MODE, Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL, - Settings.Secure.CHARGE_OPTIMIZATION_MODE + Settings.Secure.CHARGE_OPTIMIZATION_MODE, + Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, + Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, + Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index b1feede57506..b992ddc8a397 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -18,6 +18,7 @@ package android.provider.settings.validators; import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.ANY_LONG_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR; @@ -433,5 +434,8 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL, new InclusiveIntegerRangeValidator(0, 10)); VALIDATORS.put(Secure.CHARGE_OPTIMIZATION_MODE, new InclusiveIntegerRangeValidator(0, 10)); + VALIDATORS.put(Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR); + VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR); + VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java index 677c81ad9271..255b1ad3b3d2 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java @@ -239,6 +239,18 @@ public class SettingsValidators { } }; + static final Validator ANY_LONG_VALIDATOR = value -> { + if (value == null) { + return true; + } + try { + Long.parseLong(value); + return true; + } catch (NumberFormatException e) { + return false; + } + }; + static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() { @Override public boolean validate(String value) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 92167ee4d423..ab9a30b65e08 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -233,6 +233,7 @@ public class SettingsBackupTest { Settings.Global.ENHANCED_4G_MODE_ENABLED, Settings.Global.ENABLE_16K_PAGES, // Added for 16K developer option Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES, + Settings.Global.ERROR_KERNEL_LOG_PREFIX, Settings.Global.ERROR_LOGCAT_PREFIX, Settings.Global.EUICC_PROVISIONED, Settings.Global.EUICC_SUPPORTED_COUNTRIES, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index c04ec4f61c89..d2ca11207084 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -78,11 +78,23 @@ filegroup { visibility: ["//visibility:private"], } +filegroup { + name: "SystemUI-tests-broken-robofiles-run", + srcs: [ + "tests/src/**/systemui/util/LifecycleFragmentTest.java", + "tests/src/**/systemui/util/TestableAlertDialogTest.kt", + "tests/src/**/systemui/util/kotlin/PairwiseFlowTest", + "tests/src/**/systemui/util/sensors/AsyncManagerTest.java", + "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java", + "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java", + ], +} + // We are running robolectric tests in the tests directory as well as // multivalent tests. If you add a test, and it doesn't run in robolectric, // it should be added to this exclusion list. go/multivalent-tests filegroup { - name: "SystemUI-tests-broken-robofiles", + name: "SystemUI-tests-broken-robofiles-compile", srcs: [ "tests/src/**/*DeviceOnlyTest.java", "tests/src/**/*DeviceOnlyTest.kt", @@ -703,7 +715,8 @@ android_robolectric_test { ":SystemUI-tests-robofiles", ], exclude_srcs: [ - ":SystemUI-tests-broken-robofiles", + ":SystemUI-tests-broken-robofiles-compile", + ":SystemUI-tests-broken-robofiles-run", ], static_libs: [ "RoboTestLibraries", diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 582b6a1489f7..aff86e8a14c4 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -34,6 +34,13 @@ flag { } flag { + name: "priority_people_section" + namespace: "systemui" + description: "Add a new section for priority people (aka important conversations)." + bug: "340294566" +} + +flag { name: "notification_minimalism_prototype" namespace: "systemui" description: "Prototype of notification minimalism; the new 'Intermediate' lockscreen customization proposal." @@ -398,6 +405,16 @@ flag { } flag { + name: "fix_image_wallpaper_crash_surface_already_released" + namespace: "systemui" + description: "Make sure ImageWallpaper doesn't return from OnSurfaceDestroyed until any drawing is finished" + bug: "337287154" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "activity_transition_use_largest_window" namespace: "systemui" description: "Target largest opening window during activity transitions." @@ -855,6 +872,9 @@ flag { namespace: "systemui" description: "Enforce BaseUserRestriction for DISALLOW_CONFIG_BRIGHTNESS." bug: "329205638" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { @@ -913,3 +933,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "validate_keyboard_shortcut_helper_icon_uri" + namespace: "systemui" + description: "Adds a check that the caller can access the content URI of an icon in the shortcut helper." + bug: "331180422" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt index 14dd1be0e025..e8da4bd2d2cd 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt @@ -181,7 +181,6 @@ fun FooterActions( val horizontalPadding = dimensionResource(R.dimen.qs_content_horizontal_padding) Row( modifier - .sysuiResTag("qs_footer_actions") .fillMaxWidth() .graphicsLayer { this.alpha = alpha } .then(backgroundModifier) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index eb82c30233a2..d1099883c5e5 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -34,6 +34,7 @@ import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState import com.android.compose.animation.scene.ValueKey import com.android.compose.modifiers.thenIf +import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.qs.ui.adapter.QSSceneAdapter import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding @@ -143,7 +144,9 @@ fun SceneScope.QuickSettings( MovableElement( key = QuickSettings.Elements.Content, modifier = - modifier.fillMaxWidth().layout { measurable, constraints -> + modifier.sysuiResTag("quick_settings_panel").fillMaxWidth().layout { + measurable, + constraints -> val placeable = measurable.measure(constraints) // Use the height of the correct view based on the scene it is being composed in val height = heightProvider().coerceAtLeast(0) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index c09009628e96..d76b19f3fa82 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -345,7 +345,7 @@ private fun SceneScope.QuickSettingsScene( viewModel.qsSceneAdapter, { viewModel.qsSceneAdapter.qsHeight }, isSplitShade = false, - modifier = Modifier.sysuiResTag("quick_settings_panel") + modifier = Modifier ) val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle() @@ -364,7 +364,8 @@ private fun SceneScope.QuickSettingsScene( isCustomizing = isCustomizing, customizingAnimationDuration = customizingAnimationDuration, lifecycleOwner = lifecycleOwner, - modifier = Modifier.align(Alignment.CenterHorizontally), + modifier = + Modifier.align(Alignment.CenterHorizontally).sysuiResTag("qs_footer_actions"), ) } NotificationScrollingStack( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index c0f46ef390a4..a0278a616857 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -71,6 +71,7 @@ import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius +import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.controls.ui.composable.MediaCarousel import com.android.systemui.media.controls.ui.controller.MediaCarouselController @@ -446,6 +447,7 @@ private fun SceneScope.SplitShade( Column( modifier = Modifier.fillMaxSize() + .sysuiResTag("expanded_qs_scroll_view") .weight(1f) .thenIf(!isCustomizerShowing) { Modifier.verticalNestedScrollToScene() @@ -484,6 +486,7 @@ private fun SceneScope.SplitShade( lifecycleOwner = lifecycleOwner, modifier = Modifier.align(Alignment.CenterHorizontally) + .sysuiResTag("qs_footer_actions") .then(brightnessMirrorShowingModifier), ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt index fe1ebf975303..3976c618c179 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt @@ -19,10 +19,13 @@ package com.android.systemui.volume.panel.component.anc.ui.composable import android.view.Gravity import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -32,15 +35,15 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue 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.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.LiveRegionMode import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.liveRegion import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -66,13 +69,6 @@ constructor( with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() } var gravity by remember { mutableIntStateOf(Gravity.CENTER_HORIZONTAL) } val isClickable = viewModel.isClickable(slice) - val onClick = - if (isClickable) { - { with(ancPopup) { show(null, gravity) } } - } else { - null - } - Column( modifier = modifier.onGloballyPositioned { @@ -81,20 +77,33 @@ constructor( verticalArrangement = Arrangement.spacedBy(12.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - SliceAndroidView( - modifier = - Modifier.height(64.dp) - .fillMaxWidth() - .semantics { - role = Role.Button + Box( + modifier = Modifier.height(64.dp), + ) { + SliceAndroidView( + modifier = modifier.fillMaxSize(), + slice = slice, + onWidthChanged = viewModel::onButtonSliceWidthChanged, + enableAccessibility = false, + ) + Button( + modifier = + modifier.fillMaxSize().padding(8.dp).semantics { + liveRegion = LiveRegionMode.Polite contentDescription = label - } - .clip(RoundedCornerShape(28.dp)), - slice = slice, - isEnabled = onClick != null, - onWidthChanged = viewModel::onButtonSliceWidthChanged, - onClick = onClick, - ) + }, + enabled = isClickable, + onClick = { with(ancPopup) { show(null, gravity) } }, + colors = + ButtonColors( + contentColor = Color.Transparent, + containerColor = Color.Transparent, + disabledContentColor = Color.Transparent, + disabledContainerColor = Color.Transparent, + ) + ) {} + } + Text( modifier = Modifier.clearAndSetSemantics {}.basicMarquee(), text = label, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt index fc5d212a0be7..23d50c577300 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt @@ -16,11 +16,12 @@ package com.android.systemui.volume.panel.component.anc.ui.composable -import android.annotation.SuppressLint import android.content.Context +import android.os.Bundle import android.view.ContextThemeWrapper -import android.view.MotionEvent import android.view.View +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityNodeInfo import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView @@ -32,14 +33,13 @@ import com.android.systemui.res.R fun SliceAndroidView( slice: Slice?, modifier: Modifier = Modifier, - isEnabled: Boolean = true, onWidthChanged: ((Int) -> Unit)? = null, - onClick: (() -> Unit)? = null, + enableAccessibility: Boolean = true, ) { AndroidView( modifier = modifier, factory = { context: Context -> - ClickableSliceView( + ComposeSliceView( ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel), ) .apply { @@ -47,17 +47,18 @@ fun SliceAndroidView( isScrollable = false importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO setShowTitleItems(true) - if (onWidthChanged != null) { - addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged)) - } } }, - update = { sliceView: ClickableSliceView -> + update = { sliceView: ComposeSliceView -> sliceView.slice = slice - sliceView.onClick = onClick - sliceView.isEnabled = isEnabled - sliceView.isClickable = isEnabled - } + sliceView.layoutListener = onWidthChanged?.let(::OnWidthChangedLayoutListener) + sliceView.enableAccessibility = enableAccessibility + }, + onRelease = { sliceView: ComposeSliceView -> + sliceView.layoutListener = null + sliceView.slice = null + sliceView.enableAccessibility = true + }, ) } @@ -83,26 +84,39 @@ class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) : } } -/** - * [SliceView] that prioritises [onClick] when its clicked instead of passing the event to the slice - * first. - */ -@SuppressLint("ViewConstructor") // only used in this class -private class ClickableSliceView(context: Context) : SliceView(context) { +private class ComposeSliceView(context: Context) : SliceView(context) { + + var enableAccessibility: Boolean = true + var layoutListener: OnLayoutChangeListener? = null + set(value) { + field?.let { removeOnLayoutChangeListener(it) } + field = value + field?.let { addOnLayoutChangeListener(it) } + } - var onClick: (() -> Unit)? = null + override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { + if (enableAccessibility) { + super.onInitializeAccessibilityNodeInfo(info) + } + } - init { - if (onClick != null) { - setOnClickListener {} + override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) { + if (enableAccessibility) { + super.onInitializeAccessibilityEvent(event) } } - override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { - return (isSliceViewClickable && onClick != null) || super.onInterceptTouchEvent(ev) + override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean { + return if (enableAccessibility) { + super.performAccessibilityAction(action, arguments) + } else { + false + } } - override fun onClick(v: View?) { - onClick?.takeIf { isSliceViewClickable }?.let { it() } ?: super.onClick(v) + override fun addChildrenForAccessibility(outChildren: ArrayList<View>?) { + if (enableAccessibility) { + super.addChildrenForAccessibility(outChildren) + } } } diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py index bac3553e7498..95a25c58bc67 100755 --- a/packages/SystemUI/flag_check.py +++ b/packages/SystemUI/flag_check.py @@ -12,19 +12,20 @@ following case-sensitive regex: %s The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags. - -As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the -flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|STAGING|TEAMFOOD|TRUNKFOOD|NEXTFOOD). +As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the flag. +For legacy flags use EXEMPT with your flag name. Some examples below: -Flag: NONE -Flag: NA -Flag: LEGACY ENABLE_ONE_SEARCH DISABLED -Flag: ACONFIG com.android.launcher3.enable_twoline_allapps DEVELOPMENT -Flag: ACONFIG com.android.launcher3.enable_twoline_allapps TRUNKFOOD +Flag: NONE Repohook Update +Flag: TEST_ONLY +Flag: EXEMPT resource only update +Flag: EXEMPT bugfix +Flag: EXEMPT refactor +Flag: com.android.launcher3.enable_twoline_allapps +Flag: com.google.android.apps.nexuslauncher.zero_state_web_data_loader -Check the git history for more examples. It's a regex matched field. +Check the git history for more examples. It's a regex matched field. See go/android-flag-directive for more details on various formats. """ def main(): @@ -63,28 +64,31 @@ def main(): return field = 'Flag' - none = '(NONE|NA|N\/A)' # NONE|NA|N/A - - typeExpression = '\s*(LEGACY|ACONFIG)' # [type:LEGACY|ACONFIG] + none = 'NONE' + testOnly = 'TEST_ONLY' + docsOnly = 'DOCS_ONLY' + exempt = 'EXEMPT' + justification = '<justification>' - # legacyFlagName contains only uppercase alphabets with '_' - Ex: ENABLE_ONE_SEARCH - # Aconfig Flag name format = "packageName"."flagName" + # Aconfig Flag name format = <packageName>.<flagName> # package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3 - # For now alphabets, digits, "_", "." characters are allowed in flag name and not adding stricter format check. + # For now alphabets, digits, "_", "." characters are allowed in flag name. + # Checks if there is "one dot" between packageName and flagName and not adding stricter format check #common_typos_disable - flagName = '([a-zA-z0-9_.])+' + flagName = '([a-zA-Z0-9.]+)([.]+)([a-zA-Z0-9_.]+)' - #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|STAGING|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)] - stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|STAGING|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)' + # None and Exempt needs justification + exemptRegex = fr'{exempt}\s*[a-zA-Z]+' + noneRegex = fr'{none}\s*[a-zA-Z]+' #common_typos_enable - readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|STAGING|TRUNKFOOD|NEXTFOOD' + readableRegexMsg = '\n\tFlag: '+none+' '+justification+'\n\tFlag: <packageName>.<flagName>\n\tFlag: ' +exempt+' '+justification+'\n\tFlag: '+testOnly+'\n\tFlag: '+docsOnly flagRegex = fr'^{field}: .*$' check_flag = re.compile(flagRegex) #Flag: # Ignore case for flag name format. - flagNameRegex = fr'(?i)^{field}:\s*({none}|{typeExpression}\s*{flagName}\s*{stateExpression})\s*' + flagNameRegex = fr'(?i)^{field}:\s*({noneRegex}|{flagName}|{testOnly}|{docsOnly}|{exemptRegex})\s*' check_flagName = re.compile(flagNameRegex) #Flag: <flag name format> flagError = False diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt index 47a00f408460..624f18d53a70 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt @@ -133,7 +133,7 @@ class ColorScheme( @ColorInt seed: Int, darkTheme: Boolean, style: Style - ) : this(seed, darkTheme, style, 0.5) + ) : this(seed, darkTheme, style, 0.0) @JvmOverloads constructor( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java index 11a42413c4ff..27bffd0818e7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java @@ -18,10 +18,8 @@ package com.android.systemui.ambient.touch; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.view.GestureDetector; import android.view.MotionEvent; @@ -30,6 +28,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -37,7 +36,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -51,89 +49,66 @@ public class ShadeTouchHandlerTest extends SysuiTestCase { CentralSurfaces mCentralSurfaces; @Mock + ShadeViewController mShadeViewController; + + @Mock TouchHandler.TouchSession mTouchSession; ShadeTouchHandler mTouchHandler; - @Captor - ArgumentCaptor<GestureDetector.OnGestureListener> mGestureListenerCaptor; - @Captor - ArgumentCaptor<InputChannelCompat.InputEventListener> mInputListenerCaptor; - private static final int TOUCH_HEIGHT = 20; @Before public void setup() { MockitoAnnotations.initMocks(this); - - mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), TOUCH_HEIGHT); - } - - // Verifies that a swipe down in the gesture region is captured by the shade touch handler. - @Test - public void testSwipeDown_captured() { - final boolean captured = swipe(Direction.DOWN); - - assertThat(captured).isTrue(); + mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), mShadeViewController, + TOUCH_HEIGHT); } - // Verifies that a swipe in the upward direction is not catpured. + /** + * Verify that touches aren't handled when the bouncer is showing. + */ @Test - public void testSwipeUp_notCaptured() { - final boolean captured = swipe(Direction.UP); - - // Motion events not captured as the swipe is going in the wrong direction. - assertThat(captured).isFalse(); + public void testInactiveOnBouncer() { + when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); + mTouchHandler.onSessionStart(mTouchSession); + verify(mTouchSession).pop(); } - // Verifies that a swipe down forwards captured touches to the shade window for handling. + /** + * Make sure {@link ShadeTouchHandler} + */ @Test - public void testSwipeDown_sentToShadeWindow() { - swipe(Direction.DOWN); + public void testTouchPilferingOnScroll() { + final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class); + final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class); - // Both motion events are sent for the shade window to process. - verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any()); - } + final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); - // Verifies that a swipe down is not forwarded to the shade window. - @Test - public void testSwipeUp_touchesNotSent() { - swipe(Direction.UP); + mTouchHandler.onSessionStart(mTouchSession); + verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture()); - // Motion events are not sent for the shade window to process as the swipe is going in the - // wrong direction. - verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any()); + assertThat(gestureListenerArgumentCaptor.getValue() + .onScroll(motionEvent1, motionEvent2, 1, 1)) + .isTrue(); } /** - * Simulates a swipe in the given direction and returns true if the touch was intercepted by the - * touch handler's gesture listener. - * <p> - * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge - * of the gesture region, {@link #TOUCH_HEIGHT}, and goes upward to 0. + * Ensure touches are propagated to the {@link ShadeViewController}. */ - private boolean swipe(Direction direction) { - Mockito.clearInvocations(mTouchSession); - mTouchHandler.onSessionStart(mTouchSession); - - verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture()); - verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture()); - - final float startY = direction == Direction.UP ? TOUCH_HEIGHT : 0; - final float endY = direction == Direction.UP ? 0 : TOUCH_HEIGHT; + @Test + public void testEventPropagation() { + final MotionEvent motionEvent = Mockito.mock(MotionEvent.class); - // Send touches to the input and gesture listener. - final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, startY, 0); - final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, endY, 0); - mInputListenerCaptor.getValue().onInputEvent(event1); - mInputListenerCaptor.getValue().onInputEvent(event2); - final boolean captured = mGestureListenerCaptor.getValue().onScroll(event1, event2, 0, - startY - endY); + final ArgumentCaptor<InputChannelCompat.InputEventListener> + inputEventListenerArgumentCaptor = + ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); - return captured; + mTouchHandler.onSessionStart(mTouchSession); + verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture()); + inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent); + verify(mShadeViewController).handleExternalTouch(motionEvent); } - private enum Direction { - DOWN, UP, - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index b4b812d60a1a..0ab09595d6b0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -265,7 +265,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { // Device is dreaming and on communal. - fakeKeyguardRepository.setDreaming(true) + updateDreaming(true) communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) @@ -282,7 +282,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { // Device is not dreaming and on communal. - fakeKeyguardRepository.setDreaming(false) + updateDreaming(false) communalInteractor.changeScene(CommunalScenes.Communal) // Scene stays as Communal @@ -297,7 +297,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { // Device is dreaming and on communal. - fakeKeyguardRepository.setDreaming(true) + updateDreaming(true) communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) @@ -309,7 +309,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { // Dream stops, timeout is cancelled and device stays on hub, because the regular // screen timeout will take effect at this point. - fakeKeyguardRepository.setDreaming(false) + updateDreaming(false) advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds) assertThat(scene).isEqualTo(CommunalScenes.Communal) } @@ -320,7 +320,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { with(kosmos) { testScope.runTest { // Device is on communal, but not dreaming. - fakeKeyguardRepository.setDreaming(false) + updateDreaming(false) communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) @@ -328,7 +328,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { // Wait a bit, but not long enough to timeout, then start dreaming. advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds) - fakeKeyguardRepository.setDreaming(true) + updateDreaming(true) assertThat(scene).isEqualTo(CommunalScenes.Communal) // Device times out after one screen timeout interval, dream doesn't reset timeout. @@ -338,11 +338,31 @@ class CommunalSceneStartableTest : SysuiTestCase() { } @Test + fun hubTimeout_dreamAfterInitialTimeout_goesToBlank() = + with(kosmos) { + testScope.runTest { + // Device is on communal. + communalInteractor.changeScene(CommunalScenes.Communal) + + // Device stays on the hub after the timeout since we're not dreaming. + advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2) + val scene by collectLastValue(communalInteractor.desiredScene) + assertThat(scene).isEqualTo(CommunalScenes.Communal) + + // Start dreaming. + updateDreaming(true) + + // Hub times out immediately. + assertThat(scene).isEqualTo(CommunalScenes.Blank) + } + } + + @Test fun hubTimeout_userActivityTriggered_resetsTimeout() = with(kosmos) { testScope.runTest { // Device is dreaming and on communal. - fakeKeyguardRepository.setDreaming(true) + updateDreaming(true) communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) @@ -371,7 +391,7 @@ class CommunalSceneStartableTest : SysuiTestCase() { fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2) // Device is dreaming and on communal. - fakeKeyguardRepository.setDreaming(true) + updateDreaming(true) communalInteractor.changeScene(CommunalScenes.Communal) val scene by collectLastValue(communalInteractor.desiredScene) @@ -395,6 +415,12 @@ class CommunalSceneStartableTest : SysuiTestCase() { runCurrent() } + private fun TestScope.updateDreaming(dreaming: Boolean) = + with(kosmos) { + fakeKeyguardRepository.setDreaming(dreaming) + runCurrent() + } + private suspend fun TestScope.enableCommunal() = with(kosmos) { setCommunalAvailable(true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index e3dd9aefa6d0..8bfa5cff8b97 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.DreamManager; import android.content.res.Resources; import android.graphics.Region; import android.os.Handler; @@ -45,8 +46,10 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.complication.ComplicationHostViewController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.BlurUtils; import kotlinx.coroutines.CoroutineDispatcher; @@ -115,6 +118,12 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { DreamOverlayStateController mStateController; @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor; + @Mock + ShadeInteractor mShadeInteractor; + @Mock + CommunalInteractor mCommunalInteractor; + @Mock + private DreamManager mDreamManager; DreamOverlayContainerViewController mController; @@ -146,7 +155,10 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { mAnimationsController, mStateController, mBouncerlessScrimController, - mKeyguardTransitionInteractor); + mKeyguardTransitionInteractor, + mShadeInteractor, + mCommunalInteractor, + mDreamManager); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java index e3c6deed1527..29fbee01a18b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java @@ -108,7 +108,7 @@ public class CommunalTouchHandlerTest extends SysuiTestCase { mTouchHandler.onSessionStart(mTouchSession); verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture()); inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent); - verify(mCentralSurfaces).handleExternalShadeWindowTouch(motionEvent); + verify(mCentralSurfaces).handleDreamTouch(motionEvent); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt index bf0939c6c46f..99cccb282264 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt @@ -19,9 +19,13 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues +import com.android.systemui.flags.DisableSceneContainer +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING @@ -29,36 +33,74 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertEquals import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertThrows +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi -@android.platform.test.annotations.EnabledOnRavenwood class KeyguardTransitionInteractorTest : SysuiTestCase() { val kosmos = testKosmos() val underTest = kosmos.keyguardTransitionInteractor val repository = kosmos.fakeKeyguardTransitionRepository val testScope = kosmos.testScope + private val sceneTransitions = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(Scenes.Lockscreen) + ) + + private val lsToGone = + ObservableTransitionState.Transition( + Scenes.Lockscreen, + Scenes.Gone, + flowOf(Scenes.Lockscreen), + flowOf(0f), + false, + flowOf(false) + ) + + private val goneToLs = + ObservableTransitionState.Transition( + Scenes.Gone, + Scenes.Lockscreen, + flowOf(Scenes.Lockscreen), + flowOf(0f), + false, + flowOf(false) + ) + + @Before + fun setUp() { + kosmos.sceneContainerRepository.setTransitionState(sceneTransitions) + } + @Test fun transitionCollectorsReceivesOnlyAppropriateEvents() = testScope.runTest { - val lockscreenToAodSteps by collectValues(underTest.transition(LOCKSCREEN, AOD)) - val aodToLockscreenSteps by collectValues(underTest.transition(AOD, LOCKSCREEN)) + val lockscreenToAodSteps by + collectValues(underTest.transition(Edge.create(LOCKSCREEN, AOD))) + val aodToLockscreenSteps by + collectValues(underTest.transition(Edge.create(AOD, LOCKSCREEN))) val steps = mutableListOf<TransitionStep>() steps.add(TransitionStep(AOD, GONE, 0f, STARTED)) @@ -482,6 +524,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @Test + @DisableSceneContainer fun isInTransitionToState() = testScope.runTest { val results by collectValues(underTest.isInTransitionToState(GONE)) @@ -586,7 +629,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { ) sendSteps( - TransitionStep(DOZING, GONE, 0f, STARTED), + TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED), ) assertThat(results) @@ -598,7 +641,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { ) sendSteps( - TransitionStep(DOZING, GONE, 0f, RUNNING), + TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING), ) assertThat(results) @@ -610,7 +653,7 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { ) sendSteps( - TransitionStep(DOZING, GONE, 0f, FINISHED), + TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED), ) assertThat(results) @@ -623,9 +666,9 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { ) sendSteps( - TransitionStep(GONE, DOZING, 0f, STARTED), - TransitionStep(GONE, DOZING, 0f, RUNNING), - TransitionStep(GONE, DOZING, 1f, FINISHED), + TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED), + TransitionStep(LOCKSCREEN, DOZING, 0f, RUNNING), + TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED), ) assertThat(results) @@ -638,8 +681,8 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { ) sendSteps( - TransitionStep(DOZING, GONE, 0f, STARTED), - TransitionStep(DOZING, GONE, 0f, RUNNING), + TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED), + TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING), ) assertThat(results) @@ -1404,6 +1447,143 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { ) } + @Test + @DisableSceneContainer + fun transition_no_conversion_with_flag_off() = + testScope.runTest { + val currentStates by + collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE))) + + val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED) + sendSteps(sendStep1) + + assertEquals(listOf(sendStep1), currentStates) + } + + @Test + @EnableSceneContainer + fun transition_conversion_with_flag_on() = + testScope.runTest { + val currentStates by + collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE))) + + val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED) + sendSteps(sendStep1) + + assertEquals(listOf<TransitionStep>(), currentStates) + } + + @Test + @EnableSceneContainer + fun transition_conversion_emits_values_with_sceneContainer_in_correct_state() = + testScope.runTest { + val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE))) + val currentStatesConverted by + collectValues(underTest.transition(Edge.create(LOCKSCREEN, UNDEFINED))) + + sceneTransitions.value = lsToGone + val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) + val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) + val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) + sendSteps(sendStep1, sendStep2, sendStep3) + + assertEquals(listOf(sendStep1, sendStep2), currentStates) + assertEquals(listOf(sendStep1, sendStep2), currentStatesConverted) + } + + @Test + @EnableSceneContainer + fun transition_conversion_emits_nothing_with_sceneContainer_in_wrong_state() = + testScope.runTest { + val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE))) + + sceneTransitions.value = goneToLs + val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) + val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) + val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) + sendSteps(sendStep1, sendStep2, sendStep3) + + assertEquals(listOf<TransitionStep>(), currentStates) + } + + @Test + @EnableSceneContainer + fun transition_conversion_emits_values_when_edge_within_lockscreen_scene() = + testScope.runTest { + val currentStates by + collectValues(underTest.transition(Edge.create(LOCKSCREEN, DOZING))) + + sceneTransitions.value = goneToLs + val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED) + val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED) + val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) + sendSteps(sendStep1, sendStep2, sendStep3) + + assertEquals(listOf(sendStep1, sendStep2), currentStates) + } + + @Test + @EnableSceneContainer + fun transition_conversion_emits_values_with_null_edge_within_lockscreen_scene() = + testScope.runTest { + val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, null))) + val currentStatesReversed by + collectValues(underTest.transition(Edge.create(null, LOCKSCREEN))) + + sceneTransitions.value = goneToLs + val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED) + val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED) + val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED) + val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED) + sendSteps(sendStep1, sendStep2, sendStep3, sendStep4) + + assertEquals(listOf(sendStep1, sendStep2, sendStep3), currentStates) + assertEquals(listOf(sendStep4), currentStatesReversed) + } + + @Test + @EnableSceneContainer + fun transition_conversion_emits_values_with_null_edge_out_of_lockscreen_scene() = + testScope.runTest { + val currentStates by collectValues(underTest.transition(Edge.create(null, UNDEFINED))) + val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE))) + + sceneTransitions.value = lsToGone + val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) + val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) + val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED) + val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED) + sendSteps(sendStep1, sendStep2, sendStep3, sendStep4) + + assertEquals(listOf(sendStep1, sendStep2), currentStates) + assertEquals(listOf(sendStep1, sendStep2), currentStatesMapped) + } + + @Test + @EnableSceneContainer + fun transition_conversion_does_not_emit_with_null_edge_with_wrong_stl_state() = + testScope.runTest { + val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE))) + + sceneTransitions.value = goneToLs + val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED) + val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED) + val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED) + val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED) + sendSteps(sendStep1, sendStep2, sendStep3, sendStep4) + + assertEquals(listOf<TransitionStep>(), currentStatesMapped) + } + + @Test + @EnableSceneContainer + fun transition_null_edges_throw() = + testScope.runTest { + assertThrows(IllegalStateException::class.java) { + underTest.transition(Edge.create(null, null)) + } + } + private suspend fun sendSteps(vararg steps: TransitionStep) { steps.forEach { repository.sendTransitionStep(it) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt index 0ac7ff5232a3..a0fed6bbfc2c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt @@ -23,11 +23,13 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.milliseconds @@ -50,11 +52,14 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() { @Before fun setUp() { underTest = - animationFlow.setup( - duration = 1000.milliseconds, - from = GONE, - to = DREAMING, - ) + animationFlow + .setup( + duration = 1000.milliseconds, + edge = Edge.create(from = Scenes.Gone, to = DREAMING), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GONE, to = DREAMING), + ) } @Test(expected = IllegalArgumentException::class) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt index d63293675034..7a9bd924afa4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt @@ -87,6 +87,7 @@ class BouncerToGoneFlowsTest(flags: FlagsParameterization) : SysuiTestCase() { } @Test + @BrokenWithSceneContainer(339465026) fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) @@ -137,6 +138,7 @@ class BouncerToGoneFlowsTest(flags: FlagsParameterization) : SysuiTestCase() { } @Test + @BrokenWithSceneContainer(339465026) fun scrimBehindAlpha_leaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) @@ -161,6 +163,7 @@ class BouncerToGoneFlowsTest(flags: FlagsParameterization) : SysuiTestCase() { } @Test + @BrokenWithSceneContainer(339465026) fun showAllNotifications_isTrue_whenLeaveShadeOpen() = testScope.runTest { val showAllNotifications by @@ -177,6 +180,7 @@ class BouncerToGoneFlowsTest(flags: FlagsParameterization) : SysuiTestCase() { } @Test + @BrokenWithSceneContainer(339465026) fun showAllNotifications_isFalse_whenLeaveShadeIsNotOpen() = testScope.runTest { val showAllNotifications by @@ -193,6 +197,7 @@ class BouncerToGoneFlowsTest(flags: FlagsParameterization) : SysuiTestCase() { } @Test + @BrokenWithSceneContainer(330311871) fun scrimBehindAlpha_doNotLeaveShadeOpen() = testScope.runTest { val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 838b2a79adff..20ffa3312fa6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -30,6 +30,8 @@ import com.android.systemui.communal.data.repository.communalRepository import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository +import com.android.systemui.flags.DisableSceneContainer +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.parameterizeSceneContainerFlag import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -37,7 +39,9 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shadeTestUtil import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.phone.dozeParameters @@ -49,6 +53,8 @@ import com.android.systemui.util.ui.stopAnimating import com.android.systemui.util.ui.value import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -75,6 +81,11 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() private val viewState = ViewStateAccessor() + private val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(Scenes.Lockscreen) + ) + companion object { @JvmStatic @Parameters(name = "{0}") @@ -96,6 +107,7 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, ) } + kosmos.sceneContainerRepository.setTransitionState(transitionState) } @Test @@ -309,6 +321,32 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() } @Test + @EnableSceneContainer + fun alpha_transitionToHub_isZero_scene_container() = + testScope.runTest { + val alpha by collectLastValue(underTest.alpha(viewState)) + + transitionState.value = + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Communal, + emptyFlow(), + emptyFlow(), + false, + emptyFlow() + ) + + keyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.UNDEFINED, + testScope, + ) + + assertThat(alpha).isEqualTo(0f) + } + + @Test + @DisableSceneContainer fun alpha_transitionToHub_isZero() = testScope.runTest { val alpha by collectLastValue(underTest.alpha(viewState)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt index 58c6817c4270..1c1fcc450d73 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt @@ -18,8 +18,10 @@ package com.android.systemui.keyguard.ui.viewmodel import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.BrokenWithSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.andSceneContainer import com.android.systemui.flags.fakeFeatureFlagsClassic @@ -30,11 +32,16 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.shadeTestUtil import com.android.systemui.testKosmos import com.google.common.collect.Range import com.google.common.truth.Truth import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -58,6 +65,11 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza private val keyguardRepository = kosmos.fakeKeyguardRepository private lateinit var underTest: LockscreenToPrimaryBouncerTransitionViewModel + private val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(Scenes.Lockscreen) + ) + companion object { @JvmStatic @Parameters(name = "{0}") @@ -76,6 +88,7 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza } @Test + @BrokenWithSceneContainer(330311871) fun deviceEntryParentViewAlpha_shadeExpanded() = testScope.runTest { val actual by collectLastValue(underTest.deviceEntryParentViewAlpha) @@ -107,6 +120,17 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza shadeExpanded(false) runCurrent() + kosmos.sceneContainerRepository.setTransitionState(transitionState) + transitionState.value = + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Bouncer, + emptyFlow(), + emptyFlow(), + false, + emptyFlow() + ) + runCurrent() // fade out repository.sendTransitionStep(step(0f, TransitionState.STARTED)) runCurrent() @@ -132,7 +156,9 @@ class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameteriza ): TransitionStep { return TransitionStep( from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, + to = + if (SceneContainerFlag.isEnabled) KeyguardState.UNDEFINED + else KeyguardState.PRIMARY_BOUNCER, value = value, transitionState = state, ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest" diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt index bd3b77a678db..365a7c3a296a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt @@ -45,16 +45,16 @@ import com.android.systemui.plugins.activityStarter import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.statusbar.policy.keyguardStateController import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @@ -121,7 +121,7 @@ class MediaControlInteractorTest : SysuiTestCase() { whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())) .thenReturn(true) - val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) } + val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) } val expandable = mock<Expandable>() underTest.startClickIntent(expandable, clickIntent) @@ -133,7 +133,7 @@ class MediaControlInteractorTest : SysuiTestCase() { fun startClickIntent_hideOverLockscreen() { whenever(keyguardStateController.isShowing).thenReturn(false) - val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) } + val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) } val expandable = mock<Expandable>() val activityController = mock<ActivityTransitionAnimator.Controller>() whenever(expandable.activityTransitionController(any())).thenReturn(activityController) @@ -150,7 +150,7 @@ class MediaControlInteractorTest : SysuiTestCase() { whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())) .thenReturn(true) - val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) } + val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) } underTest.startDeviceIntent(deviceIntent) @@ -163,7 +163,7 @@ class MediaControlInteractorTest : SysuiTestCase() { whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())) .thenReturn(true) - val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) } + val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(false) } underTest.startDeviceIntent(deviceIntent) @@ -174,7 +174,7 @@ class MediaControlInteractorTest : SysuiTestCase() { fun startDeviceIntent_hideOverLockscreen() { whenever(keyguardStateController.isShowing).thenReturn(false) - val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) } + val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) } underTest.startDeviceIntent(deviceIntent) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt index c5d7e1f490ee..285326421a14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -23,9 +23,8 @@ import android.app.NotificationManager.IMPORTANCE_HIGH import android.app.NotificationManager.IMPORTANCE_LOW import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.platform.test.flag.junit.SetFlagsRule -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntry @@ -44,25 +43,29 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.icon.ConversationIconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifierImpl import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.mock @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ConversationCoordinatorTest : SysuiTestCase() { // captured listeners and pluggables: @@ -72,22 +75,20 @@ class ConversationCoordinatorTest : SysuiTestCase() { private lateinit var peopleComparator: NotifComparator private lateinit var beforeRenderListListener: OnBeforeRenderListListener + private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier + private lateinit var peopleAlertingSection: NotifSection + @Mock private lateinit var pipeline: NotifPipeline @Mock private lateinit var conversationIconManager: ConversationIconManager - @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier - @Mock private lateinit var channel: NotificationChannel @Mock private lateinit var headerController: NodeController - private lateinit var entry: NotificationEntry - private lateinit var entryA: NotificationEntry - private lateinit var entryB: NotificationEntry private lateinit var coordinator: ConversationCoordinator - @Rule @JvmField public val setFlagsRule = SetFlagsRule() - @Before fun setUp() { MockitoAnnotations.initMocks(this) + peopleNotificationIdentifier = + PeopleNotificationIdentifierImpl(mock(), GroupMembershipManagerImpl()) coordinator = ConversationCoordinator( peopleNotificationIdentifier, @@ -95,7 +96,6 @@ class ConversationCoordinatorTest : SysuiTestCase() { HighPriorityProvider(peopleNotificationIdentifier, GroupMembershipManagerImpl()), headerController ) - whenever(channel.isImportantConversation).thenReturn(true) coordinator.attach(pipeline) @@ -110,49 +110,65 @@ class ConversationCoordinatorTest : SysuiTestCase() { if (!SortBySectionTimeFlag.isEnabled) peopleComparator = peopleAlertingSectioner.comparator!! - entry = NotificationEntryBuilder().setChannel(channel).build() + peopleAlertingSection = NotifSection(peopleAlertingSectioner, 0) + } - val section = NotifSection(peopleAlertingSectioner, 0) - entryA = - NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("A").build() - entryB = - NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("B").build() + @Test + fun priorityPeopleSectionerClaimsOnlyImportantConversations() { + val sectioner = coordinator.priorityPeopleSectioner + assertTrue(sectioner.isInSection(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON))) + assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_FULL_PERSON))) + assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_PERSON))) + assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_NON_PERSON))) + assertFalse(sectioner.isInSection(NotificationEntryBuilder().build())) } @Test fun testPromotesImportantConversations() { - // only promote important conversations - assertTrue(promoter.shouldPromoteToTopLevel(entry)) + assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON))) + assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON))) + assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_PERSON))) + assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_NON_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(NotificationEntryBuilder().build())) } @Test fun testPromotedImportantConversationsMakesSummaryUnimportant() { - val altChildA = NotificationEntryBuilder().setTag("A").build() - val altChildB = NotificationEntryBuilder().setTag("B").build() - val summary = NotificationEntryBuilder().setId(2).setChannel(channel).build() + val importantChannel = + mock<NotificationChannel>().also { + whenever(it.isImportantConversation).thenReturn(true) + } + val otherChannel = + mock<NotificationChannel>().also { + whenever(it.isImportantConversation).thenReturn(false) + } + val importantChild = + makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel) } + val altChildA = + makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("A") } + val altChildB = + makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("B") } + val summary = + makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel).setId(2) } val groupEntry = GroupEntryBuilder() .setParent(GroupEntry.ROOT_ENTRY) .setSummary(summary) - .setChildren(listOf(entry, altChildA, altChildB)) + .setChildren(listOf(importantChild, altChildA, altChildB)) .build() - assertTrue(promoter.shouldPromoteToTopLevel(entry)) + assertTrue(promoter.shouldPromoteToTopLevel(importantChild)) assertFalse(promoter.shouldPromoteToTopLevel(altChildA)) assertFalse(promoter.shouldPromoteToTopLevel(altChildB)) - NotificationEntryBuilder.setNewParent(entry, GroupEntry.ROOT_ENTRY) - GroupEntryBuilder.getRawChildren(groupEntry).remove(entry) - beforeRenderListListener.onBeforeRenderList(listOf(entry, groupEntry)) + NotificationEntryBuilder.setNewParent(importantChild, GroupEntry.ROOT_ENTRY) + GroupEntryBuilder.getRawChildren(groupEntry).remove(importantChild) + beforeRenderListListener.onBeforeRenderList(listOf(importantChild, groupEntry)) verify(conversationIconManager).setUnimportantConversations(eq(listOf(summary.key))) } @Test fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() { // GIVEN - val alertingEntry = - NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_DEFAULT).build() - whenever(peopleNotificationIdentifier.getPeopleNotificationType(alertingEntry)) - .thenReturn(TYPE_PERSON) + val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) } // put alerting people notifications in this section assertThat(peopleAlertingSectioner.isInSection(alertingEntry)).isTrue() @@ -162,10 +178,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testInAlertingPeopleSectionWhenTheImportanceIsLowerThanDefault() { // GIVEN - val silentEntry = - NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() - whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry)) - .thenReturn(TYPE_PERSON) + val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) } // THEN put silent people notifications in alerting section assertThat(peopleAlertingSectioner.isInSection(silentEntry)).isTrue() @@ -175,10 +188,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() { // GIVEN - val silentEntry = - NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() - whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry)) - .thenReturn(TYPE_PERSON) + val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) } // THEN put silent people notifications in this section assertThat(peopleSilentSectioner.isInSection(silentEntry)).isTrue() @@ -191,18 +201,14 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test fun testNotInPeopleSection() { // GIVEN - val entry = - NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() + val entry = makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_LOW) } val importantEntry = - NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_HIGH).build() - whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry)) - .thenReturn(TYPE_NON_PERSON) - whenever(peopleNotificationIdentifier.getPeopleNotificationType(importantEntry)) - .thenReturn(TYPE_NON_PERSON) + makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_HIGH) } // THEN - only put people notification either silent or alerting - if (!SortBySectionTimeFlag.isEnabled) + if (!SortBySectionTimeFlag.isEnabled) { assertThat(peopleSilentSectioner.isInSection(entry)).isFalse() + } assertThat(peopleAlertingSectioner.isInSection(importantEntry)).isFalse() } @@ -210,22 +216,16 @@ class ConversationCoordinatorTest : SysuiTestCase() { fun testInAlertingPeopleSectionWhenThereIsAnImportantChild() { // GIVEN val altChildA = - NotificationEntryBuilder().setTag("A").setImportance(IMPORTANCE_DEFAULT).build() - val altChildB = NotificationEntryBuilder().setTag("B").setImportance(IMPORTANCE_LOW).build() - val summary = - NotificationEntryBuilder() - .setId(2) - .setImportance(IMPORTANCE_LOW) - .setChannel(channel) - .build() + makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("A").setImportance(IMPORTANCE_DEFAULT) } + val altChildB = + makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("B").setImportance(IMPORTANCE_LOW) } + val summary = makeEntryOfPeopleType(TYPE_PERSON) { setId(2).setImportance(IMPORTANCE_LOW) } val groupEntry = GroupEntryBuilder() .setParent(GroupEntry.ROOT_ENTRY) .setSummary(summary) .setChildren(listOf(altChildA, altChildB)) .build() - whenever(peopleNotificationIdentifier.getPeopleNotificationType(summary)) - .thenReturn(TYPE_PERSON) // THEN assertThat(peopleAlertingSectioner.isInSection(groupEntry)).isTrue() } @@ -233,10 +233,12 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testComparatorPutsImportantPeopleFirst() { - whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) - .thenReturn(TYPE_IMPORTANT_PERSON) - whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB)) - .thenReturn(TYPE_PERSON) + val entryA = + makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { + setSection(peopleAlertingSection).setTag("A") + } + val entryB = + makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") } // only put people notifications in this section assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1) @@ -245,10 +247,10 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testComparatorEquatesPeopleWithSameType() { - whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) - .thenReturn(TYPE_PERSON) - whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB)) - .thenReturn(TYPE_PERSON) + val entryA = + makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("A") } + val entryB = + makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") } // only put people notifications in this section assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0) @@ -259,4 +261,23 @@ class ConversationCoordinatorTest : SysuiTestCase() { fun testNoSecondarySortForConversations() { assertThat(peopleAlertingSectioner.comparator).isNull() } + + private fun makeEntryOfPeopleType( + @PeopleNotificationType type: Int, + buildBlock: NotificationEntryBuilder.() -> Unit = {} + ): NotificationEntry { + val channel: NotificationChannel = mock() + whenever(channel.isImportantConversation).thenReturn(type == TYPE_IMPORTANT_PERSON) + val entry = + NotificationEntryBuilder() + .updateRanking { + it.setIsConversation(type != TYPE_NON_PERSON) + it.setShortcutInfo(if (type >= TYPE_FULL_PERSON) mock() else null) + it.setChannel(channel) + } + .also(buildBlock) + .build() + assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry)) + return entry + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java index 50ae98557de6..ce134e64bf57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -31,9 +31,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.compose.animation.scene.ObservableTransitionState; @@ -57,6 +57,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; @@ -75,7 +76,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.verification.VerificationMode; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class VisualStabilityCoordinatorTest extends SysuiTestCase { @@ -86,6 +87,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Mock private WakefulnessLifecycle mWakefulnessLifecycle; @Mock private StatusBarStateController mStatusBarStateController; @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener; + @Mock private SeenNotificationsInteractor mSeenNotificationsInteractor; @Mock private HeadsUpManager mHeadsUpManager; @Mock private VisibilityLocationProvider mVisibilityLocationProvider; @Mock private VisualStabilityProvider mVisualStabilityProvider; @@ -121,6 +123,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { mHeadsUpManager, mShadeAnimationInteractor, mJavaAdapter, + mSeenNotificationsInteractor, mStatusBarStateController, mVisibilityLocationProvider, mVisualStabilityProvider, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index da17366a8416..82e2bb719818 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues +import com.android.systemui.flags.BrokenWithSceneContainer import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags @@ -107,18 +108,25 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S val testScope = kosmos.testScope val configurationRepository get() = kosmos.fakeConfigurationRepository + val keyguardRepository get() = kosmos.fakeKeyguardRepository + val keyguardInteractor get() = kosmos.keyguardInteractor + val keyguardRootViewModel get() = kosmos.keyguardRootViewModel + val keyguardTransitionRepository get() = kosmos.fakeKeyguardTransitionRepository + val shadeTestUtil get() = kosmos.shadeTestUtil + val sharedNotificationContainerInteractor get() = kosmos.sharedNotificationContainerInteractor + val largeScreenHeaderHelper get() = kosmos.mockLargeScreenHeaderHelper @@ -814,6 +822,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S } @Test + @BrokenWithSceneContainer(330311871) fun alphaDoesNotUpdateWhileGoneTransitionIsRunning() = testScope.runTest { val viewState = ViewStateAccessor() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt index 64c9429cbe25..46df0c227f1c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt @@ -16,17 +16,16 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor -import android.os.Handler import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.volume.localMediaController import com.android.systemui.volume.mediaControllerRepository +import com.android.systemui.volume.mediaDeviceSessionInteractor import com.android.systemui.volume.mediaOutputInteractor import com.android.systemui.volume.panel.shared.model.filterData import com.android.systemui.volume.remoteMediaController @@ -55,12 +54,7 @@ class MediaDeviceSessionInteractorTest : SysuiTestCase() { listOf(localMediaController, remoteMediaController) ) - underTest = - MediaDeviceSessionInteractor( - testScope.testScheduler, - Handler(TestableLooper.get(kosmos.testCase).looper), - mediaControllerRepository, - ) + underTest = mediaDeviceSessionInteractor } } diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml index f644584f747a..5755dcd70a82 100644 --- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml +++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml @@ -47,6 +47,7 @@ android:layout_height="match_parent"> android:layout_gravity="center" android:contentDescription="@null" android:scaleType="fitXY" + android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="@+id/biometric_icon" app:layout_constraintEnd_toEndOf="@+id/biometric_icon" app:layout_constraintStart_toStartOf="@+id/biometric_icon" diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml index 46b8e4665a22..05f6faea464e 100644 --- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml +++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml @@ -229,6 +229,7 @@ android:layout_gravity="center" android:contentDescription="@null" android:scaleType="fitXY" + android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="@+id/biometric_icon" app:layout_constraintEnd_toEndOf="@+id/biometric_icon" app:layout_constraintStart_toStartOf="@+id/biometric_icon" diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml index d51fe5849133..fa4d9a868fd7 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml @@ -222,6 +222,7 @@ android:layout_gravity="center" android:contentDescription="@null" android:scaleType="fitXY" + android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="@+id/biometric_icon" app:layout_constraintEnd_toEndOf="@+id/biometric_icon" app:layout_constraintStart_toStartOf="@+id/biometric_icon" diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml index 2cb4b0233a90..49d3a8ec8ad8 100644 --- a/packages/SystemUI/res/layout/screenshot_shelf.xml +++ b/packages/SystemUI/res/layout/screenshot_shelf.xml @@ -135,11 +135,12 @@ android:id="@+id/screenshot_scrollable_preview" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:clipToOutline="true" android:scaleType="matrix" android:visibility="gone" app:layout_constraintStart_toStartOf="@id/screenshot_preview" app:layout_constraintTop_toTopOf="@id/screenshot_preview" - android:elevation="7dp"/> + android:elevation="3dp"/> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" @@ -170,6 +171,13 @@ </FrameLayout> </androidx.constraintlayout.widget.ConstraintLayout> <ImageView + android:id="@+id/screenshot_scrolling_scrim" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" + android:clickable="true" + android:importantForAccessibility="no"/> + <ImageView android:id="@+id/screenshot_flash" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 8ac1de898be8..c33b7ce1d002 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -99,7 +99,7 @@ public class PipSurfaceTransactionHelper { final float startScale = sourceRectHint.width() <= sourceRectHint.height() ? (float) destinationBounds.width() / sourceBounds.width() : (float) destinationBounds.height() / sourceBounds.height(); - scale = (1 - progress) * startScale + progress * endScale; + scale = Math.min((1 - progress) * startScale + progress * endScale, 1.0f); } final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale; final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale; diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index f33acf21e8a0..3f3bb0bc94b6 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -42,6 +42,7 @@ 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 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN @@ -544,10 +545,10 @@ constructor( internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job { return scope.launch { merge( - keyguardTransitionInteractor.transition(AOD, LOCKSCREEN).map { step -> - step.copy(value = 1f - step.value) + keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)).map { + it.copy(value = 1f - it.value) }, - keyguardTransitionInteractor.transition(LOCKSCREEN, AOD), + keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)), ).filter { it.transitionState != TransitionState.FINISHED } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 905a98c2e181..42838aeddd6b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -326,7 +326,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } if (KeyguardWmStateRefactor.isEnabled()) { - mKeyguardTransitionInteractor.startDismissKeyguardTransition(); + mKeyguardTransitionInteractor.startDismissKeyguardTransition( + "KeyguardSecurityContainerController#finish"); } } diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java index 9c7fc9dd307f..9ef9938ab8ad 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java @@ -23,8 +23,7 @@ import android.graphics.Region; import android.view.GestureDetector; import android.view.MotionEvent; -import androidx.annotation.NonNull; - +import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.phone.CentralSurfaces; import java.util.Optional; @@ -38,34 +37,29 @@ import javax.inject.Named; */ public class ShadeTouchHandler implements TouchHandler { private final Optional<CentralSurfaces> mSurfaces; + private final ShadeViewController mShadeViewController; private final int mInitiationHeight; - /** - * Tracks whether or not we are capturing a given touch. Will be null before and after a touch. - */ - private Boolean mCapture; - @Inject ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces, + ShadeViewController shadeViewController, @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) { mSurfaces = centralSurfaces; + mShadeViewController = shadeViewController; mInitiationHeight = initiationHeight; } @Override public void onSessionStart(TouchSession session) { - if (mSurfaces.isEmpty()) { + if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) { session.pop(); return; } - session.registerCallback(() -> mCapture = null); - session.registerInputListener(ev -> { + mShadeViewController.handleExternalTouch((MotionEvent) ev); + if (ev instanceof MotionEvent) { - if (mCapture != null && mCapture) { - mSurfaces.get().handleExternalShadeWindowTouch((MotionEvent) ev); - } if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) { session.pop(); } @@ -74,25 +68,15 @@ public class ShadeTouchHandler implements TouchHandler { session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() { @Override - public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX, + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (mCapture == null) { - // Only capture swipes that are going downwards. - mCapture = Math.abs(distanceY) > Math.abs(distanceX) && distanceY < 0; - if (mCapture) { - // Send the initial touches over, as the input listener has already - // processed these touches. - mSurfaces.get().handleExternalShadeWindowTouch(e1); - mSurfaces.get().handleExternalShadeWindowTouch(e2); - } - } - return mCapture; + return true; } @Override - public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX, + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - return mCapture; + return true; } }); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 9816896e3ea8..298b87d05f39 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -32,11 +32,18 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.StatusBarState @@ -131,6 +138,7 @@ open class UdfpsKeyguardViewControllerLegacy( override fun onUnlockedChanged() { updatePauseAuth() } + override fun onLaunchTransitionFadingAwayChanged() { launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway updatePauseAuth() @@ -211,7 +219,10 @@ open class UdfpsKeyguardViewControllerLegacy( suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job { return scope.launch { transitionInteractor - .transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD) + .transition( + edge = Edge.create(Scenes.Bouncer, AOD), + edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD) + ) .collect { transitionStep -> view.onDozeAmountChanged( transitionStep.value, @@ -225,8 +236,7 @@ open class UdfpsKeyguardViewControllerLegacy( @VisibleForTesting suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job { return scope.launch { - transitionInteractor.transition(KeyguardState.DREAMING, KeyguardState.AOD).collect { - transitionStep -> + transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep -> view.onDozeAmountChanged( transitionStep.value, transitionStep.value, @@ -239,23 +249,21 @@ open class UdfpsKeyguardViewControllerLegacy( @VisibleForTesting suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job { return scope.launch { - transitionInteractor - .transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD) - .collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } + transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect { + transitionStep -> + view.onDozeAmountChanged( + transitionStep.value, + transitionStep.value, + UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, + ) + } } } @VisibleForTesting suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job { return scope.launch { - transitionInteractor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED).collect { - transitionStep -> + transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep -> view.onDozeAmountChanged( 1f - transitionStep.value, 1f - transitionStep.value, @@ -268,8 +276,7 @@ open class UdfpsKeyguardViewControllerLegacy( @VisibleForTesting suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job { return scope.launch { - transitionInteractor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD).collect { - transitionStep -> + transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep -> view.onDozeAmountChanged( transitionStep.value, transitionStep.value, @@ -282,14 +289,18 @@ open class UdfpsKeyguardViewControllerLegacy( @VisibleForTesting suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job { return scope.launch { - transitionInteractor.transition(KeyguardState.GONE, KeyguardState.AOD).collect { - transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, + transitionInteractor + .transition( + edge = Edge.create(Scenes.Gone, AOD), + edgeWithoutSceneContainer = Edge.create(GONE, AOD) ) - } + .collect { transitionStep -> + view.onDozeAmountChanged( + transitionStep.value, + transitionStep.value, + ANIMATE_APPEAR_ON_SCREEN_OFF, + ) + } } } @@ -298,13 +309,10 @@ open class UdfpsKeyguardViewControllerLegacy( return scope.launch { transitionInteractor.dozeAmountTransition.collect { transitionStep -> if ( - transitionStep.from == KeyguardState.AOD && + transitionStep.from == AOD && transitionStep.transitionState == TransitionState.CANCELED ) { - if ( - transitionInteractor.startedKeyguardTransitionStep.first().to != - KeyguardState.AOD - ) { + if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) { // If the next started transition isn't transitioning back to AOD, force // doze amount to be 0f (as if the transition to the lockscreen completed). view.onDozeAmountChanged( @@ -557,6 +565,7 @@ open class UdfpsKeyguardViewControllerLegacy( private fun updateScaleFactor() { udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) } } + companion object { const val TAG = "UdfpsKeyguardViewController" } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java index 8efc66de24cd..ba236ba016ff 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java @@ -66,11 +66,11 @@ import com.android.systemui.screenshot.ui.binder.ActionButtonViewBinder; import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance; import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel; -import java.util.ArrayList; - import kotlin.Unit; import kotlin.jvm.functions.Function0; +import java.util.ArrayList; + /** * Handles the visual elements and animations for the clipboard overlay. */ @@ -109,6 +109,7 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { private View mDismissButton; private LinearLayout mActionContainer; private ClipboardOverlayCallbacks mClipboardCallbacks; + private ActionButtonViewBinder mActionButtonViewBinder = new ActionButtonViewBinder(); public ClipboardOverlayView(Context context) { this(context, null); @@ -152,14 +153,15 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { private void bindDefaultActionChips() { if (screenshotShelfUi2()) { - ActionButtonViewBinder.INSTANCE.bind(mRemoteCopyChip, + mActionButtonViewBinder.bind(mRemoteCopyChip, ActionButtonViewModel.Companion.withNextId( new ActionButtonAppearance( Icon.createWithResource(mContext, R.drawable.ic_baseline_devices_24).loadDrawable( mContext), null, - mContext.getString(R.string.clipboard_send_nearby_description)), + mContext.getString(R.string.clipboard_send_nearby_description), + true), new Function0<>() { @Override public Unit invoke() { @@ -169,12 +171,14 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { return null; } })); - ActionButtonViewBinder.INSTANCE.bind(mShareChip, + mActionButtonViewBinder.bind(mShareChip, ActionButtonViewModel.Companion.withNextId( new ActionButtonAppearance( Icon.createWithResource(mContext, R.drawable.ic_screenshot_share).loadDrawable(mContext), - null, mContext.getString(com.android.internal.R.string.share)), + null, + mContext.getString(com.android.internal.R.string.share), + true), new Function0<>() { @Override public Unit invoke() { @@ -512,9 +516,9 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { private View constructShelfActionChip(RemoteAction action, Runnable onFinish) { View chip = LayoutInflater.from(mContext).inflate( R.layout.shelf_action_chip, mActionContainer, false); - ActionButtonViewBinder.INSTANCE.bind(chip, ActionButtonViewModel.Companion.withNextId( + mActionButtonViewBinder.bind(chip, ActionButtonViewModel.Companion.withNextId( new ActionButtonAppearance(action.getIcon().loadDrawable(mContext), - action.getTitle(), action.getTitle()), new Function0<>() { + action.getTitle(), action.getTitle(), false), new Function0<>() { @Override public Unit invoke() { try { diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index f437032d0ddb..6f20a8daf00a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -17,7 +17,6 @@ package com.android.systemui.communal import android.provider.Settings -import android.service.dreams.Flags.dreamTracksFocus import com.android.compose.animation.scene.SceneKey import com.android.systemui.CoreStartable import com.android.systemui.communal.domain.interactor.CommunalInteractor @@ -43,6 +42,7 @@ import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine @@ -74,6 +74,10 @@ constructor( ) : CoreStartable { private var screenTimeout: Int = DEFAULT_SCREEN_TIMEOUT + private var timeoutJob: Job? = null + + private var isDreaming: Boolean = false + override fun start() { // Handle automatically switching based on keyguard state. keyguardTransitionInteractor.startedKeyguardTransitionStep @@ -113,47 +117,67 @@ constructor( } .launchIn(bgScope) - // Handle timing out back to the dream. + // The hub mode timeout should start as soon as the user enters hub mode. At the end of the + // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the + // dream is not running, nothing will happen. However if the dream starts again underneath + // hub mode after the initial timeout expires, such as if the device is docked or the dream + // app is updated by the Play store, a new timeout should be started. bgScope.launch { combine( communalInteractor.desiredScene, // Emit a value on start so the combine starts. communalInteractor.userActivity.emitOnStart() ) { scene, _ -> - // Time out should run whenever we're dreaming and the hub is open, even if not - // docked. + // Only timeout if we're on the hub is open. scene == CommunalScenes.Communal } - // mapLatest cancels the previous action block when new values arrive, so any - // already running timeout gets cancelled when conditions change or user interaction - // is detected. - .mapLatest { shouldTimeout -> - if (!shouldTimeout) { - return@mapLatest false + .collectLatest { shouldTimeout -> + cancelHubTimeout() + if (shouldTimeout) { + startHubTimeout() } - - delay(screenTimeout.milliseconds) - true } - .sample(keyguardInteractor.isDreaming, ::Pair) - .collect { (shouldTimeout, isDreaming) -> - if (isDreaming && shouldTimeout) { + } + bgScope.launch { + keyguardInteractor.isDreaming + .sample(communalInteractor.desiredScene, ::Pair) + .collectLatest { (isDreaming, scene) -> + this@CommunalSceneStartable.isDreaming = isDreaming + if (scene == CommunalScenes.Communal && isDreaming && timeoutJob == null) { + // If dreaming starts after timeout has expired, ex. if dream restarts under + // the hub, just close the hub immediately. communalInteractor.changeScene(CommunalScenes.Blank) } } } - if (dreamTracksFocus()) { - bgScope.launch { - communalInteractor.isIdleOnCommunal.collectLatest { - withContext(mainDispatcher) { - notificationShadeWindowController.setGlanceableHubShowing(it) - } + bgScope.launch { + communalInteractor.isIdleOnCommunal.collectLatest { + withContext(mainDispatcher) { + notificationShadeWindowController.setGlanceableHubShowing(it) } } } } + private fun cancelHubTimeout() { + timeoutJob?.cancel() + timeoutJob = null + } + + private fun startHubTimeout() { + if (timeoutJob == null) { + timeoutJob = + bgScope.launch { + delay(screenTimeout.milliseconds) + if (isDreaming) { + communalInteractor.changeScene(CommunalScenes.Blank) + } + timeoutJob = null + } + } + } + private suspend fun determineSceneAfterTransition( lastStartedTransition: TransitionStep, ): SceneKey? { diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt index 30a56a21e322..813fccffb62f 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt @@ -48,6 +48,7 @@ import com.android.systemui.keyguard.data.repository.FaceDetectTableLog import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions @@ -302,7 +303,7 @@ constructor( private fun listenForSchedulingWatchdog() { keyguardTransitionInteractor - .transition(to = KeyguardState.GONE) + .transition(Edge.create(to = KeyguardState.GONE)) .filter { it.transitionState == TransitionState.FINISHED } .onEach { // We deliberately want to run this in background because scheduleWatchdog does diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt index 6c6683a483c7..669cd94c1f21 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt @@ -38,6 +38,7 @@ import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.DevicePosture +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN @@ -126,9 +127,9 @@ constructor( .launchIn(applicationScope) merge( - keyguardTransitionInteractor.transition(AOD, LOCKSCREEN), - keyguardTransitionInteractor.transition(OFF, LOCKSCREEN), - keyguardTransitionInteractor.transition(DOZING, LOCKSCREEN), + keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)), + keyguardTransitionInteractor.transition(Edge.create(OFF, LOCKSCREEN)), + keyguardTransitionInteractor.transition(Edge.create(DOZING, LOCKSCREEN)), ) .filter { it.transitionState == TransitionState.STARTED } .sample(powerInteractor.detailedWakefulness) diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 6e043391c197..60006c68639d 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -16,6 +16,8 @@ package com.android.systemui.dreams; +import static android.service.dreams.Flags.dreamHandlesBeingObscured; + import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion; @@ -23,8 +25,10 @@ import static com.android.systemui.complication.ComplicationLayoutParams.POSITIO import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; +import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows; import android.animation.Animator; +import android.app.DreamManager; import android.content.res.Resources; import android.graphics.Region; import android.os.Handler; @@ -37,7 +41,9 @@ import com.android.dream.lowlight.LowLightTransitionCoordinator; import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.complication.ComplicationHostViewController; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.dagger.DreamOverlayModule; @@ -45,10 +51,12 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionChangeEvent; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.util.ViewController; import kotlinx.coroutines.CoroutineDispatcher; +import kotlinx.coroutines.flow.FlowKt; import java.util.Arrays; @@ -68,6 +76,8 @@ public class DreamOverlayContainerViewController extends private final DreamOverlayStateController mStateController; private final LowLightTransitionCoordinator mLowLightTransitionCoordinator; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final ShadeInteractor mShadeInteractor; + private final CommunalInteractor mCommunalInteractor; private final ComplicationHostViewController mComplicationHostViewController; @@ -87,9 +97,10 @@ public class DreamOverlayContainerViewController extends // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates). private final Handler mHandler; - private final CoroutineDispatcher mMainDispatcher; + private final CoroutineDispatcher mBackgroundDispatcher; private final int mDreamOverlayMaxTranslationY; private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; + private final DreamManager mDreamManager; private long mJitterStartTimeMillis; @@ -178,7 +189,7 @@ public class DreamOverlayContainerViewController extends LowLightTransitionCoordinator lowLightTransitionCoordinator, BlurUtils blurUtils, @Main Handler handler, - @Main CoroutineDispatcher mainDispatcher, + @Background CoroutineDispatcher backgroundDispatcher, @Main Resources resources, @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset, @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long @@ -188,18 +199,23 @@ public class DreamOverlayContainerViewController extends DreamOverlayAnimationsController animationsController, DreamOverlayStateController stateController, BouncerlessScrimController bouncerlessScrimController, - KeyguardTransitionInteractor keyguardTransitionInteractor) { + KeyguardTransitionInteractor keyguardTransitionInteractor, + ShadeInteractor shadeInteractor, + CommunalInteractor communalInteractor, + DreamManager dreamManager) { super(containerView); mDreamOverlayContentView = contentView; mStatusBarViewController = statusBarViewController; mBlurUtils = blurUtils; mDreamOverlayAnimationsController = animationsController; mStateController = stateController; + mCommunalInteractor = communalInteractor; mLowLightTransitionCoordinator = lowLightTransitionCoordinator; mBouncerlessScrimController = bouncerlessScrimController; mKeyguardTransitionInteractor = keyguardTransitionInteractor; + mShadeInteractor = shadeInteractor; mComplicationHostViewController = complicationHostViewController; mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize( @@ -211,11 +227,12 @@ public class DreamOverlayContainerViewController extends ViewGroup.LayoutParams.MATCH_PARENT)); mHandler = handler; - mMainDispatcher = mainDispatcher; + mBackgroundDispatcher = backgroundDispatcher; mMaxBurnInOffset = maxBurnInOffset; mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval; mMillisUntilFullJitter = millisUntilFullJitter; mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor; + mDreamManager = dreamManager; } @Override @@ -238,11 +255,21 @@ public class DreamOverlayContainerViewController extends mView.getRootSurfaceControl().setTouchableRegion(emptyRegion); emptyRegion.recycle(); - collectFlow( - mView, - mKeyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState), - isFinished -> mAnyBouncerShowing = isFinished, - mMainDispatcher); + if (dreamHandlesBeingObscured()) { + collectFlow( + mView, + FlowKt.distinctUntilChanged(combineFlows( + mKeyguardTransitionInteractor.isFinishedInStateWhere( + KeyguardState::isBouncerState), + mShadeInteractor.isAnyExpanded(), + mCommunalInteractor.isCommunalShowing(), + (anyBouncerShowing, shadeExpanded, communalShowing) -> { + mAnyBouncerShowing = anyBouncerShowing; + return anyBouncerShowing || shadeExpanded || communalShowing; + })), + mDreamManager::setDreamIsObscured, + mBackgroundDispatcher); + } // Start dream entry animations. Skip animations for low light clock. if (!mStateController.isLowLightActive()) { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java index fff0c58eecb8..1c047ddcd3d8 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java @@ -98,7 +98,7 @@ public class CommunalTouchHandler implements TouchHandler { // Notification shade window has its own logic to be visible if the hub is open, no need to // do anything here other than send touch events over. session.registerInputListener(ev -> { - surfaces.handleExternalShadeWindowTouch((MotionEvent) ev); + surfaces.handleDreamTouch((MotionEvent) ev); if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) { var unused = session.pop(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt index 221f790b1ab2..c5b3c5335fc8 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt @@ -23,6 +23,7 @@ import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel @@ -97,7 +98,7 @@ constructor( .distinctUntilChanged() val transitionEnded = - keyguardTransitionInteractor.transition(from = DREAMING).filter { step -> + keyguardTransitionInteractor.transition(Edge.create(from = DREAMING)).filter { step -> step.transitionState == TransitionState.FINISHED || step.transitionState == TransitionState.CANCELED } diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt index 9876fe4482c0..f04cbb87214f 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt @@ -477,7 +477,7 @@ constructor( } private inline fun PrintWriter.wrapSection(entry: DumpsysEntry, block: () -> Unit) { - Trace.beginSection(entry.name) + Trace.beginSection(entry.name.take(Trace.MAX_SECTION_NAME_LEN)) preamble(entry) val dumpTime = measureTimeMillis(block) footer(entry, dumpTime) diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 67c556409615..140434040ca7 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -29,11 +29,13 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor +import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject /** A class in which engineers can define flag dependencies */ @@ -49,6 +51,7 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token + PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token // SceneContainer dependencies SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt index d3f7e24bb87f..44f1c1e8305f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt @@ -17,19 +17,43 @@ package com.android.systemui.keyboard.shortcut.domain.interactor import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState +import com.android.systemui.model.SysUiState +import com.android.systemui.settings.DisplayTracker +import com.android.systemui.shared.system.QuickStepContract import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch @SysUISingleton class ShortcutHelperInteractor @Inject -constructor(private val repository: ShortcutHelperRepository) { +constructor( + private val displayTracker: DisplayTracker, + @Background private val backgroundScope: CoroutineScope, + private val sysUiState: SysUiState, + private val repository: ShortcutHelperRepository +) { val state: Flow<ShortcutHelperState> = repository.state - fun onUserLeave() { + fun onViewClosed() { repository.hide() + setSysUiStateFlagEnabled(false) + } + + fun onViewOpened() { + setSysUiStateFlagEnabled(true) + } + + private fun setSysUiStateFlagEnabled(enabled: Boolean) { + backgroundScope.launch { + sysUiState + .setFlag(QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING, enabled) + .commitUpdate(displayTracker.defaultDisplayId) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt index 934f9ee9e90d..ef4156da4f7b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt @@ -63,12 +63,13 @@ constructor( setUpSheetDismissListener() setUpDismissOnTouchOutside() observeFinishRequired() + viewModel.onViewOpened() } override fun onDestroy() { super.onDestroy() if (isFinishing) { - viewModel.onUserLeave() + viewModel.onViewClosed() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt index 7e48c6523122..c623f5c23fd9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt @@ -38,7 +38,11 @@ constructor( .distinctUntilChanged() .flowOn(backgroundDispatcher) - fun onUserLeave() { - interactor.onUserLeave() + fun onViewClosed() { + interactor.onViewClosed() + } + + fun onViewOpened() { + interactor.onViewOpened() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 2cda72809a36..81c2d92d29e8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1076,6 +1076,33 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } }; + /** + * For now, the keyguard-appearing animation is a no-op, because we assume that this is + * happening while the screen is already off or turning off. + * + * TODO(b/278086361): create an animation for keyguard appearing over a non-showWhenLocked + * activity. + */ + private final IRemoteAnimationRunner.Stub mAppearAnimationRunner = + new IRemoteAnimationRunner.Stub() { + @Override + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) { + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to finish transition", e); + } + } + + @Override + public void onAnimationCancelled() { + } + }; + private final IRemoteAnimationRunner mOccludeAnimationRunner = new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); @@ -1164,7 +1191,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, finishedCallback.onAnimationFinished(); mOccludeByDreamAnimator = null; } catch (RemoteException e) { - e.printStackTrace(); + Log.e(TAG, "Failed to finish transition", e); } } }); @@ -1279,7 +1306,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION); } catch (RemoteException e) { - e.printStackTrace(); + Log.e(TAG, "Failed to finish transition", e); } } }); @@ -1545,6 +1572,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mKeyguardTransitions.register( KeyguardService.wrap(this, getExitAnimationRunner()), + KeyguardService.wrap(this, getAppearAnimationRunner()), KeyguardService.wrap(this, getOccludeAnimationRunner()), KeyguardService.wrap(this, getOccludeByDreamAnimationRunner()), KeyguardService.wrap(this, getUnoccludeAnimationRunner())); @@ -2123,6 +2151,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, return validatingRemoteAnimationRunner(mExitAnimationRunner); } + public IRemoteAnimationRunner getAppearAnimationRunner() { + return validatingRemoteAnimationRunner(mAppearAnimationRunner); + } + public IRemoteAnimationRunner getOccludeAnimationRunner() { if (KeyguardWmStateRefactor.isEnabled()) { return validatingRemoteAnimationRunner(mWmOcclusionManager.getOccludeAnimationRunner()); @@ -3356,7 +3388,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } catch (RemoteException e) { mSurfaceBehindRemoteAnimationRequested = false; - e.printStackTrace(); + Log.e(TAG, "Failed to report keyguardGoingAway", e); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt index a65a8827fa48..6e364d180ebf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt @@ -29,6 +29,7 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.power.domain.interactor.PowerInteractor @@ -84,7 +85,7 @@ constructor( applicationScope.launch(bgDispatcher) { // We drop 1 to avoid triggering on initial collect(). - keyguardTransitionInteractor.transition(to = GONE).collect { transition -> + keyguardTransitionInteractor.transition(Edge.create(to = GONE)).collect { transition -> if (transition.transitionState == TransitionState.FINISHED) { onKeyguardGone() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt index 00f50023b263..1b342edb28fe 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt @@ -23,6 +23,7 @@ import android.view.RemoteAnimationTarget import android.view.WindowManager import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier import com.android.systemui.statusbar.policy.KeyguardStateController import java.util.concurrent.Executor @@ -40,6 +41,7 @@ constructor( private val activityTaskManagerService: IActivityTaskManager, private val keyguardStateController: KeyguardStateController, private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, ) { /** @@ -141,6 +143,14 @@ constructor( finishedCallback: IRemoteAnimationFinishedCallback ) { if (apps.isNotEmpty()) { + // Ensure that we've started a dismiss keyguard transition. WindowManager can start the + // going away animation on its own, if an activity launches and then requests dismissing + // the keyguard. In this case, this is the first and only signal we'll receive to start + // a transition to GONE. + keyguardTransitionInteractor.startDismissKeyguardTransition( + reason = "Going away remote animation started" + ) + goingAwayRemoteAnimationFinishedCallback = finishedCallback keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0]) } else { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index dad2d9692dbc..f1e98f3bbe6d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -263,7 +263,9 @@ constructor( } fun dismissKeyguard() { - scope.launch("$TAG#dismissKeyguard") { startTransitionTo(KeyguardState.GONE) } + scope.launch("$TAG#dismissKeyguard") { + startTransitionTo(KeyguardState.GONE, ownerReason = "#dismissKeyguard()") + } } private fun listenForLockscreenToGone() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 72857391793f..8065c0f4bc41 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -322,7 +322,11 @@ constructor( } } } - .distinctUntilChanged() + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = 0f, + ) val clockShouldBeCentered: Flow<Boolean> = repository.clockShouldBeCentered diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index e711edc0c302..75c4d6f6fea6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.keyguard.logging.KeyguardLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.log.core.LogLevel.VERBOSE import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag @@ -26,6 +27,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.launch private val TAG = KeyguardTransitionAuditLogger::class.simpleName!! @@ -41,6 +43,7 @@ constructor( private val logger: KeyguardLogger, private val powerInteractor: PowerInteractor, private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, + private val keyguardRootViewModel: KeyguardRootViewModel, private val shadeInteractor: ShadeInteractor, private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) { @@ -72,8 +75,8 @@ constructor( if (!SceneContainerFlag.isEnabled) { scope.launch { - sharedNotificationContainerViewModel.bounds.collect { - logger.log(TAG, VERBOSE, "Notif: bounds", it) + sharedNotificationContainerViewModel.bounds.debounce(20L).collect { + logger.log(TAG, VERBOSE, "Notif: bounds (debounced)", it) } } } @@ -113,6 +116,18 @@ constructor( } scope.launch { + keyguardInteractor.keyguardTranslationY.collect { + logger.log(TAG, VERBOSE, "keyguardTranslationY", it) + } + } + + scope.launch { + keyguardRootViewModel.burnInModel.debounce(20L).collect { + logger.log(TAG, VERBOSE, "BurnInModel (debounced)", it) + } + } + + scope.launch { keyguardInteractor.isKeyguardDismissible.collect { logger.log(TAG, VERBOSE, "isDismissible", it) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 30c6718adf1b..c65dc305b0cc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.domain.interactor import android.annotation.FloatRange import android.annotation.SuppressLint import android.util.Log +import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -31,10 +32,13 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.pairwise import java.util.UUID import javax.inject.Inject @@ -71,8 +75,9 @@ constructor( private val fromAlternateBouncerTransitionInteractor: dagger.Lazy<FromAlternateBouncerTransitionInteractor>, private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>, + private val sceneInteractor: dagger.Lazy<SceneInteractor>, ) { - private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>() + private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>() /** * Numerous flows are derived from, or care directly about, the transition value in and out of a @@ -128,11 +133,11 @@ constructor( scope.launch { repository.transitions.collect { // FROM->TO - transitionMap[Edge(it.from, it.to)]?.emit(it) + transitionMap[Edge.create(it.from, it.to)]?.emit(it) // FROM->(ANY) - transitionMap[Edge(it.from, null)]?.emit(it) + transitionMap[Edge.create(it.from, null)]?.emit(it) // (ANY)->TO - transitionMap[Edge(null, it.to)]?.emit(it) + transitionMap[Edge.create(null, it.to)]?.emit(it) } } @@ -152,26 +157,70 @@ constructor( } } - /** Given an [edge], return a SharedFlow to collect only relevant [TransitionStep]. */ + fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> { + return transition(if (SceneContainerFlag.isEnabled) edge else edgeWithoutSceneContainer) + } + + /** Given an [edge], return a Flow to collect only relevant [TransitionStep]s. */ @SuppressLint("SharedFlowCreation") - fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> { - return transitionMap.getOrPut(edge) { - MutableSharedFlow( - extraBufferCapacity = 10, - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) + fun transition(edge: Edge): Flow<TransitionStep> { + edge.verifyValidKeyguardStates() + val mappedEdge = getMappedEdge(edge) + + val flow: Flow<TransitionStep> = + transitionMap.getOrPut(mappedEdge) { + MutableSharedFlow( + extraBufferCapacity = 10, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + } + + return if (SceneContainerFlag.isEnabled) { + flow.filter { + val fromScene = + when (edge) { + is Edge.StateToState -> edge.from?.mapToSceneContainerScene() + is Edge.StateToScene -> edge.from.mapToSceneContainerScene() + is Edge.SceneToState -> edge.from + } + + val toScene = + when (edge) { + is Edge.StateToState -> edge.to?.mapToSceneContainerScene() + is Edge.StateToScene -> edge.to + is Edge.SceneToState -> edge.to.mapToSceneContainerScene() + } + + fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null + + return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) || + sceneInteractor.get().transitionState.value.isTransitioning(fromScene, toScene) + } + } else { + flow } } /** - * Receive all [TransitionStep] matching a filter of [from]->[to]. Allow nulls in order to match - * any transition, for instance (any)->GONE. + * Converts old KTF states to UNDEFINED when [SceneContainerFlag] is enabled. + * + * Does nothing otherwise. + * + * This method should eventually be removed when new code is only written for scene container. + * Even when all edges are ported today, there is still development on going in production that + * might utilize old states. */ - fun transition(from: KeyguardState? = null, to: KeyguardState? = null): Flow<TransitionStep> { - if (from == null && to == null) { - throw IllegalArgumentException("from and to cannot both be null") + private fun getMappedEdge(edge: Edge): Edge.StateToState { + if (!SceneContainerFlag.isEnabled) return edge as Edge.StateToState + return when (edge) { + is Edge.StateToState -> + Edge.create( + from = edge.from?.mapToSceneContainerState(), + to = edge.to?.mapToSceneContainerState() + ) + is Edge.SceneToState -> Edge.create(UNDEFINED, edge.to) + is Edge.StateToScene -> Edge.create(edge.from, UNDEFINED) } - return getOrCreateFlow(Edge(from = from, to = to)) } /** @@ -218,6 +267,8 @@ constructor( .map { step -> step.to } .shareIn(scope, SharingStarted.Eagerly, replay = 1) + val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal + /** The from state of the last [TransitionState.STARTED] transition. */ // TODO: is it performant to have several SharedFlows side by side instead of one? @SuppressLint("SharedFlowCreation") @@ -365,32 +416,37 @@ constructor( val isInTransitionToAnyState = isInTransitionWhere({ true }, { true }) fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> { - return getOrCreateFlow(Edge(from = fromState, to = null)) + return transition(Edge.create(from = fromState, to = null)) } fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> { - return getOrCreateFlow(Edge(from = null, to = toState)) + return transition(Edge.create(from = null, to = toState)) } /** * Called to start a transition that will ultimately dismiss the keyguard from the current * state. + * + * This is called exclusively by sources that can authoritatively say we should be unlocked, + * including KeyguardSecurityContainerController and WindowManager. */ - fun startDismissKeyguardTransition() { + fun startDismissKeyguardTransition(reason: String = "") { // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return - when (val startedState = startedKeyguardState.replayCache.last()) { + Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)") + when (val startedState = currentTransitionInfoInternal.value.to) { LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard() PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer() ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer() AOD -> fromAodTransitionInteractor.get().dismissAod() DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing() - else -> - Log.e( - "KeyguardTransitionInteractor", - "We don't know how to dismiss keyguard from state $startedState." + KeyguardState.GONE -> + Log.i( + TAG, + "Already transitioning to GONE; ignoring startDismissKeyguardTransition." ) + else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.") } } @@ -398,7 +454,7 @@ constructor( fun isInTransitionToState( state: KeyguardState, ): Flow<Boolean> { - return getOrCreateFlow(Edge(from = null, to = state)) + return transition(Edge.create(from = null, to = state)) .mapLatest { it.transitionState.isTransitioning() } .onStart { emit(false) } .distinctUntilChanged() @@ -407,12 +463,16 @@ constructor( /** * Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet * completed it. + * + * Provide [edgeWithoutSceneContainer] when the edge is different from what it is without it. If + * the edges are equal before and after the flag it is sufficient to provide just [edge]. */ - fun isInTransition( - from: KeyguardState, - to: KeyguardState, - ): Flow<Boolean> { - return getOrCreateFlow(Edge(from = from, to = to)) + fun isInTransition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<Boolean> { + return if (SceneContainerFlag.isEnabled) { + transition(edge) + } else { + transition(edgeWithoutSceneContainer ?: edge) + } .mapLatest { it.transitionState.isTransitioning() } .onStart { emit(false) } .distinctUntilChanged() @@ -424,7 +484,7 @@ constructor( fun isInTransitionFromState( state: KeyguardState, ): Flow<Boolean> { - return getOrCreateFlow(Edge(from = state, to = null)) + return transition(Edge.create(from = state, to = null)) .mapLatest { it.transitionState.isTransitioning() } .onStart { emit(false) } .distinctUntilChanged() @@ -475,7 +535,7 @@ constructor( * If you only care about a single state for both from and to, instead use the optimized * [isInTransition]. */ - fun isInTransitionWhere( + private fun isInTransitionWhere( fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean ): Flow<Boolean> { return repository.transitions @@ -527,4 +587,8 @@ constructor( @FloatRange(from = 0.0, to = 1.0) value: Float, state: TransitionState ) = repository.updateTransition(transitionId, value, state) + + companion object { + private val TAG = KeyguardTransitionInteractor::class.simpleName + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index dc35e4311d25..1e2db7c36432 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.BiometricUnlockMode +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.scene.domain.interactor.SceneInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt index 6e00aa7956e7..3baeb7682e12 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt @@ -131,7 +131,7 @@ constructor( val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, - from = transitionInteractor.getStartedState(), + from = transitionInteractor.currentTransitionInfo.value.to, to = state, animator = null, modeOnCanceled = TransitionModeOnCanceled.REVERSE @@ -150,7 +150,7 @@ constructor( private suspend fun handleTransition(transition: ObservableTransitionState.Transition) { if (transition.fromScene == Scenes.Lockscreen) { if (currentTransitionId != null) { - val currentToState = transitionInteractor.getStartedState() + val currentToState = transitionInteractor.currentTransitionInfo.value.to if (currentToState == UNDEFINED) { transitionKtfTo(transitionInteractor.getStartedFromState()) } @@ -169,6 +169,8 @@ constructor( } private suspend fun transitionKtfTo(state: KeyguardState) { + // TODO(b/330311871): This is based on a sharedFlow and thus might not be up-to-date and + // cause a race condition. (There is no known scenario that is currently affected.) val currentTransition = transitionInteractor.transitionState.value if (currentTransition.isFinishedIn(state)) { // This is already the state we want to be in @@ -199,7 +201,7 @@ constructor( } private suspend fun startTransitionFromLockscreen() { - val currentState = transitionInteractor.getStartedState() + val currentState = transitionInteractor.currentTransitionInfo.value.to val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt index a0f9be629132..4f516f586216 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt @@ -15,8 +15,98 @@ */ package com.android.systemui.keyguard.shared.model -/** FROM -> TO keyguard transition. null values are allowed to signify FROM -> *, or * -> TO */ -data class Edge( - val from: KeyguardState?, - val to: KeyguardState?, -) +import android.util.Log +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED +import com.android.systemui.scene.shared.flag.SceneContainerFlag + +/** + * Represents an edge either between two Keyguard Transition Framework states (KTF) or a KTF state + * and a scene container scene. Passing [null] in either [from] or [to] indicates a wildcard. + * + * Wildcards are not allowed for transitions involving a scene. Use [sceneInteractor] directly + * instead. Reason: [TransitionStep]s are not emitted for every edge leading into/out of a scene. + * For example: Lockscreen -> Gone would be emitted as LOCKSCREEN -> UNDEFINED but Bouncer -> Gone + * would not emit anything. + */ +sealed class Edge { + + fun verifyValidKeyguardStates() { + when (this) { + is StateToState -> verifyValidKeyguardStates(from, to) + is SceneToState -> verifyValidKeyguardStates(null, to) + is StateToScene -> verifyValidKeyguardStates(from, null) + } + } + + private fun verifyValidKeyguardStates(from: KeyguardState?, to: KeyguardState?) { + val mappedFrom = from?.mapToSceneContainerState() + val mappedTo = to?.mapToSceneContainerState() + + val fromChanged = from != mappedFrom + val toChanged = to != mappedTo + + if (SceneContainerFlag.isEnabled) { + if (fromChanged && toChanged) { + // TODO:(b/330311871) As we come close to having all current edges converted these + // error messages can be converted to throw such that future developers fail early + // when they introduce invalid edges. + Log.e( + TAG, + """ + The edge ${from?.name} => ${to?.name} was automatically converted to + ${mappedFrom?.name} => ${mappedTo?.name} but does not exist anymore in KTF. + Please remove or port this edge to scene container.""" + .trimIndent(), + ) + } else if ((fromChanged && to == null) || (toChanged && from == null)) { + Log.e( + TAG, + """ + The edge ${from?.name} => ${to?.name} was automatically converted to + ${mappedFrom?.name} => ${mappedTo?.name}. Wildcards are not allowed together + with UNDEFINED because it will only be tracking edges leading in and out of + the Lockscreen scene but miss others. Please remove or port this edge.""" + .trimIndent(), + Exception() + ) + } else if (fromChanged || toChanged) { + Log.w( + TAG, + """ + The edge ${from?.name} => ${to?.name} was automatically converted to + ${mappedFrom?.name} => ${mappedTo?.name} it probably exists but needs explicit + conversion. Please remove or port this edge to scene container.""" + .trimIndent(), + ) + } + } else { + if (from == UNDEFINED || to == UNDEFINED) { + Log.e( + TAG, + "UNDEFINED should not be used when scene container is disabled", + ) + } + } + } + + data class StateToState(val from: KeyguardState?, val to: KeyguardState?) : Edge() { + init { + check(!(from == null && to == null)) { "to and from can't both be null" } + } + } + + data class StateToScene(val from: KeyguardState, val to: SceneKey) : Edge() + + data class SceneToState(val from: SceneKey, val to: KeyguardState) : Edge() + + companion object { + private const val TAG = "Edge" + + fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to) + + fun create(from: KeyguardState, to: SceneKey) = StateToScene(from, to) + + fun create(from: SceneKey, to: KeyguardState) = SceneToState(from, to) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt index 6d96db34e23a..6a2bb5f51c5d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt @@ -15,6 +15,9 @@ */ package com.android.systemui.keyguard.shared.model +import com.android.compose.animation.scene.SceneKey +import com.android.systemui.scene.shared.model.Scenes + /** List of all possible states to transition to/from */ enum class KeyguardState { /** @@ -84,6 +87,40 @@ enum class KeyguardState { /** An activity is displaying over the keyguard. */ OCCLUDED; + fun mapToSceneContainerState(): KeyguardState { + return when (this) { + OFF, + DOZING, + DREAMING, + DREAMING_LOCKSCREEN_HOSTED, + AOD, + ALTERNATE_BOUNCER, + OCCLUDED, + LOCKSCREEN -> this + GLANCEABLE_HUB, + PRIMARY_BOUNCER, + GONE, + UNDEFINED -> UNDEFINED + } + } + + fun mapToSceneContainerScene(): SceneKey? { + return when (this) { + OFF, + DOZING, + DREAMING, + DREAMING_LOCKSCREEN_HOSTED, + AOD, + ALTERNATE_BOUNCER, + OCCLUDED, + LOCKSCREEN -> Scenes.Lockscreen + GLANCEABLE_HUB -> Scenes.Communal + PRIMARY_BOUNCER -> Scenes.Bouncer + GONE -> Scenes.Gone + UNDEFINED -> null + } + } + companion object { /** Whether the lockscreen is visible when we're FINISHED in the given state. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt index 735b10907c73..23aa21cfbf31 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.scene.shared.flag.SceneContainerFlag import javax.inject.Inject import kotlin.math.max import kotlin.math.min @@ -52,20 +53,20 @@ constructor( /** Invoke once per transition between FROM->TO states to get access to a shared flow. */ fun setup( duration: Duration, - from: KeyguardState?, - to: KeyguardState?, + edge: Edge, ): FlowBuilder { - if (from == null && to == null) { - throw IllegalArgumentException("from and to are both null") - } - - return FlowBuilder(duration, Edge(from, to)) + return FlowBuilder(duration, edge) } inner class FlowBuilder( private val transitionDuration: Duration, private val edge: Edge, ) { + fun setupWithoutSceneContainer(edge: Edge.StateToState): FlowBuilder { + if (SceneContainerFlag.isEnabled) return this + return setup(this.transitionDuration, edge) + } + /** * Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted * in the range of [0, 1]. View animations should begin and end within a subset of this @@ -117,7 +118,7 @@ constructor( if (!duration.isPositive()) { throw IllegalArgumentException("duration must be a positive number: $duration") } - if ((startTime + duration).compareTo(transitionDuration) > 0) { + if ((startTime + duration) > transitionDuration) { throw IllegalArgumentException( "startTime($startTime) + duration($duration) must be" + " <= transitionDuration($transitionDuration)" @@ -153,7 +154,7 @@ constructor( } return transitionInteractor - .getOrCreateFlow(edge) + .transition(edge) .map { step -> StateToValue( from = step.from, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt index 218967c338ea..7c745bc227f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt @@ -57,9 +57,9 @@ class ClockSizeTransition( addTransition(SmartspaceMoveTransition(config, clockViewModel)) } - open class VisibilityBoundsTransition() : Transition() { - var captureSmartspace: Boolean = false - + abstract class VisibilityBoundsTransition() : Transition() { + abstract val captureSmartspace: Boolean + protected val TAG = this::class.simpleName!! override fun captureEndValues(transition: TransitionValues) = captureValues(transition) override fun captureStartValues(transition: TransitionValues) = captureValues(transition) override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES @@ -76,7 +76,7 @@ class ClockSizeTransition( parent.findViewById<View>(sharedR.id.bc_smartspace_view) ?: parent.findViewById<View>(R.id.keyguard_slice_view) if (targetSSView == null) { - Log.e(TAG, "Failed to find smartspace equivalent target for animation") + Log.e(TAG, "Failed to find smartspace equivalent target under $parent") return } transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect() @@ -109,14 +109,12 @@ class ClockSizeTransition( var fromIsVis = fromVis == View.VISIBLE var fromAlpha = startValues.values[PROP_ALPHA] as Float val fromBounds = startValues.values[PROP_BOUNDS] as Rect - val fromSSBounds = - if (captureSmartspace) startValues.values[SMARTSPACE_BOUNDS] as Rect else null + val fromSSBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect? val toView = endValues.view val toVis = endValues.values[PROP_VISIBILITY] as Int val toBounds = endValues.values[PROP_BOUNDS] as Rect - val toSSBounds = - if (captureSmartspace) endValues.values[SMARTSPACE_BOUNDS] as Rect else null + val toSSBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect? val toIsVis = toVis == View.VISIBLE val toAlpha = if (toIsVis) 1f else 0f @@ -221,9 +219,6 @@ class ClockSizeTransition( private const val SMARTSPACE_BOUNDS = "ClockSizeTransition:SSBounds" private val TRANSITION_PROPERTIES = arrayOf(PROP_VISIBILITY, PROP_ALPHA, PROP_BOUNDS, SMARTSPACE_BOUNDS) - - private val DEBUG = false - private val TAG = VisibilityBoundsTransition::class.simpleName!! } } @@ -232,18 +227,24 @@ class ClockSizeTransition( val viewModel: KeyguardClockViewModel, val smartspaceViewModel: KeyguardSmartspaceViewModel, ) : VisibilityBoundsTransition() { + override val captureSmartspace = !viewModel.isLargeClockVisible.value + init { duration = CLOCK_IN_MILLIS startDelay = CLOCK_IN_START_DELAY_MILLIS interpolator = CLOCK_IN_INTERPOLATOR - captureSmartspace = - !viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled if (viewModel.isLargeClockVisible.value) { viewModel.currentClock.value?.let { + if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}") it.largeClock.layout.views.forEach { addTarget(it) } } + ?: run { + Log.e(TAG, "No large clock set, falling back") + addTarget(R.id.lockscreen_clock_view_large) + } } else { + if (DEBUG) Log.i(TAG, "Small Clock In") addTarget(R.id.lockscreen_clock_view) } } @@ -282,7 +283,6 @@ class ClockSizeTransition( val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN const val SMALL_CLOCK_IN_MOVE_SCALE = CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat() - private val TAG = ClockFaceInTransition::class.simpleName!! } } @@ -291,18 +291,24 @@ class ClockSizeTransition( val viewModel: KeyguardClockViewModel, val smartspaceViewModel: KeyguardSmartspaceViewModel, ) : VisibilityBoundsTransition() { + override val captureSmartspace = viewModel.isLargeClockVisible.value + init { duration = CLOCK_OUT_MILLIS interpolator = CLOCK_OUT_INTERPOLATOR - captureSmartspace = - viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled if (viewModel.isLargeClockVisible.value) { + if (DEBUG) Log.i(TAG, "Small Clock Out") addTarget(R.id.lockscreen_clock_view) } else { viewModel.currentClock.value?.let { + if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}") it.largeClock.layout.views.forEach { addTarget(it) } } + ?: run { + Log.e(TAG, "No large clock set, falling back") + addTarget(R.id.lockscreen_clock_view_large) + } } } @@ -339,7 +345,6 @@ class ClockSizeTransition( val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR const val SMALL_CLOCK_OUT_MOVE_SCALE = CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat() - private val TAG = ClockFaceOutTransition::class.simpleName!! } } @@ -348,6 +353,8 @@ class ClockSizeTransition( val config: IntraBlueprintTransition.Config, viewModel: KeyguardClockViewModel, ) : VisibilityBoundsTransition() { + override val captureSmartspace = false + init { duration = if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS @@ -367,4 +374,8 @@ class ClockSizeTransition( const val STATUS_AREA_MOVE_DOWN_MILLIS = 467L } } + + companion object { + val DEBUG = true + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt index 4fd92d70fb07..9da11ceb8c1f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -42,8 +44,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION, - from = KeyguardState.ALTERNATE_BOUNCER, - to = KeyguardState.AOD, + edge = Edge.create(from = ALTERNATE_BOUNCER, to = AOD), ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt index 9649af73eadb..55a48b6bd49b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -42,8 +44,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromAlternateBouncerTransitionInteractor.TO_DOZING_DURATION, - from = KeyguardState.ALTERNATE_BOUNCER, - to = KeyguardState.DOZING, + edge = Edge.create(from = ALTERNATE_BOUNCER, to = DOZING), ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt index 8c6be989d8d9..bb4fb7961ed0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt @@ -19,11 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.ScrimAlpha import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.SysuiStatusBarStateController import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -44,11 +46,14 @@ constructor( private val statusBarStateController: SysuiStatusBarStateController, ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_GONE_DURATION, - from = ALTERNATE_BOUNCER, - to = KeyguardState.GONE, - ) + animationFlow + .setup( + duration = TO_GONE_DURATION, + edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = ALTERNATE_BOUNCER, to = GONE), + ) fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> { var startAlpha = 1f diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt index 27febd38a97c..3f2ef29c9570 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt @@ -18,8 +18,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -40,8 +41,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_OCCLUDED_DURATION, - from = ALTERNATE_BOUNCER, - to = KeyguardState.OCCLUDED, + edge = Edge.create(from = ALTERNATE_BOUNCER, to = OCCLUDED), ) override val deviceEntryParentViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt index 759288136783..f0bccacadc57 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt @@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -37,11 +40,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - from = KeyguardState.ALTERNATE_BOUNCER, - to = KeyguardState.PRIMARY_BOUNCER, - ) + animationFlow + .setup( + duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, + edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Bouncer), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER), + ) override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt index 5cf100e78e6e..4128c529644d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt @@ -34,8 +34,8 @@ constructor( alternateBouncerInteractor: AlternateBouncerInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, ) { - private val deviceSupportsAlternateBouncer: Flow<Boolean> = - alternateBouncerInteractor.alternateBouncerSupported + val canShowAlternateBouncer: Flow<Boolean> = alternateBouncerInteractor.canShowAlternateBouncer + private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> = keyguardTransitionInteractor .transitionValue(KeyguardState.ALTERNATE_BOUNCER) @@ -43,8 +43,8 @@ constructor( .distinctUntilChanged() val alternateBouncerWindowRequired: Flow<Boolean> = - deviceSupportsAlternateBouncer.flatMapLatest { deviceSupportsAlternateBouncer -> - if (deviceSupportsAlternateBouncer) { + canShowAlternateBouncer.flatMapLatest { canShowAlternateBouncer -> + if (canShowAlternateBouncer) { isTransitioningToOrFromOrShowingAlternateBouncer } else { flowOf(false) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt index adc090de1d4a..8e8b09d5cd67 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt @@ -19,9 +19,12 @@ package com.android.systemui.keyguard.ui.viewmodel import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,11 +40,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = FromAodTransitionInteractor.TO_GONE_DURATION, - from = KeyguardState.AOD, - to = KeyguardState.GONE, - ) + animationFlow + .setup( + duration = FromAodTransitionInteractor.TO_GONE_DURATION, + edge = Edge.create(from = AOD, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = AOD, to = GONE), + ) /** * AOD -> GONE should fade out the lockscreen contents. This transition plays both during wake diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt index cbbb82039329..b267ecb42ac2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt @@ -19,9 +19,10 @@ package com.android.systemui.keyguard.ui.viewmodel import android.util.MathUtils import com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.StateToValue import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition @@ -39,7 +40,6 @@ import kotlinx.coroutines.flow.Flow class AodToLockscreenTransitionViewModel @Inject constructor( - deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, shadeInteractor: ShadeInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { @@ -47,8 +47,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, + edge = Edge.create(from = AOD, to = LOCKSCREEN), ) private var isShadeExpanded = false diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt index 445575f7e55d..2497defba5a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -36,8 +38,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION, - from = KeyguardState.AOD, - to = KeyguardState.OCCLUDED, + edge = Edge.create(from = AOD, to = OCCLUDED), ) /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt index 9a23007eea4a..35f05f55caa1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt @@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -37,11 +40,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - from = KeyguardState.AOD, - to = KeyguardState.PRIMARY_BOUNCER, - ) + animationFlow + .setup( + duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, + edge = Edge.create(from = AOD, to = Scenes.Bouncer), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER), + ) override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt index 570f37710c24..caa043622e18 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt @@ -19,11 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.ScrimAlpha import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.SysuiStatusBarStateController import dagger.Lazy @@ -73,8 +75,12 @@ constructor( return animationFlow .setup( duration = duration, - from = from, - to = GONE, + // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> scene + // transition + edge = Edge.create(from = from, to = Scenes.Gone) + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = from, to = GONE), ) .sharedFlow( duration = duration, @@ -96,11 +102,16 @@ constructor( var leaveShadeOpen: Boolean = false var willRunDismissFromKeyguard: Boolean = false val transitionAnimation = - animationFlow.setup( - duration = duration, - from = fromState, - to = GONE, - ) + animationFlow + .setup( + duration = duration, + // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> + // scene transition + edge = Edge.create(from = fromState, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = fromState, to = GONE), + ) return shadeInteractor.anyExpansion .map { it > 0f } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt index 8851a51f15b0..77ebfcea9c96 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt @@ -19,9 +19,12 @@ package com.android.systemui.keyguard.ui.viewmodel import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,11 +40,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_GONE_DURATION, - from = KeyguardState.DOZING, - to = KeyguardState.GONE, - ) + animationFlow + .setup( + duration = TO_GONE_DURATION, + edge = Edge.create(from = DOZING, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = DOZING, to = GONE), + ) fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> { var startAlpha = 1f diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt index 168d6e16daa2..a460d515e0b2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt @@ -18,7 +18,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -39,8 +41,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION, - from = KeyguardState.DOZING, - to = KeyguardState.LOCKSCREEN, + edge = Edge.create(from = DOZING, to = LOCKSCREEN), ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt index c0b11959cbd9..f33752fc04d4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -38,8 +40,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION, - from = KeyguardState.DOZING, - to = KeyguardState.OCCLUDED, + edge = Edge.create(from = DOZING, to = OCCLUDED), ) /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt index 4395c3436a71..7ddf641e9e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt @@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -38,11 +41,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_PRIMARY_BOUNCER_DURATION, - from = KeyguardState.DOZING, - to = KeyguardState.PRIMARY_BOUNCER, - ) + animationFlow + .setup( + duration = TO_PRIMARY_BOUNCER_DURATION, + edge = Edge.create(from = DOZING, to = Scenes.Bouncer), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER), + ) override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt index 67568e12a4a1..57ed455e7b13 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt @@ -18,7 +18,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -34,8 +36,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - to = KeyguardState.LOCKSCREEN, + edge = Edge.create(from = DREAMING_LOCKSCREEN_HOSTED, to = LOCKSCREEN), ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt index 0fa74752ea0d..754ed6cc3327 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -40,12 +42,12 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromDreamingTransitionInteractor.TO_AOD_DURATION, - from = KeyguardState.DREAMING, - to = KeyguardState.AOD, + edge = Edge.create(from = DREAMING, to = AOD), ) val deviceEntryBackgroundViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) + override val deviceEntryParentViewAlpha: Flow<Float> = deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt index a083c24e3d4d..00aa102ec5bb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt @@ -19,10 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -40,11 +43,14 @@ constructor( configurationInteractor: ConfigurationInteractor, ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_GLANCEABLE_HUB_DURATION, - from = KeyguardState.DREAMING, - to = KeyguardState.GLANCEABLE_HUB, - ) + animationFlow + .setup( + duration = TO_GLANCEABLE_HUB_DURATION, + edge = Edge.create(from = DREAMING, to = Scenes.Communal), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB), + ) val dreamOverlayTranslationX: Flow<Float> = configurationInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt index ec7b931161f6..1bdf6d29f6af 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt @@ -18,10 +18,13 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow -import kotlinx.coroutines.flow.Flow +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject +import kotlinx.coroutines.flow.Flow @SysUISingleton class DreamingToGoneTransitionViewModel @@ -31,13 +34,15 @@ constructor( ) { private val transitionAnimation = - animationFlow.setup( + animationFlow + .setup( duration = FromDreamingTransitionInteractor.TO_GONE_DURATION, - from = KeyguardState.DREAMING, - to = KeyguardState.GONE, + edge = Edge.create(from = DREAMING, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = DREAMING, to = GONE), ) /** Lockscreen views alpha */ val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) - -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt index f191aa7d7e4e..82381eb45f9a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt @@ -20,7 +20,9 @@ import com.android.app.animation.Interpolators.EMPHASIZED import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -45,8 +47,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - from = KeyguardState.DREAMING, - to = KeyguardState.LOCKSCREEN, + edge = Edge.create(from = DREAMING, to = LOCKSCREEN), ) /** Dream overlay y-translation on exit */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt index 3716458079cd..d594488208a1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt @@ -19,10 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -41,11 +44,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = FROM_GLANCEABLE_HUB_DURATION, - from = KeyguardState.GLANCEABLE_HUB, - to = KeyguardState.DREAMING, - ) + animationFlow + .setup( + duration = FROM_GLANCEABLE_HUB_DURATION, + edge = Edge.create(from = Scenes.Communal, to = DREAMING), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING), + ) val dreamOverlayAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt index e05b500620d5..046b95f0c6ae 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt @@ -20,10 +20,13 @@ import com.android.app.animation.Interpolators.EMPHASIZED import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.StateToValue import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -45,11 +48,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = - animationFlow.setup( - duration = TO_LOCKSCREEN_DURATION, - from = KeyguardState.GLANCEABLE_HUB, - to = KeyguardState.LOCKSCREEN, - ) + animationFlow + .setup( + duration = TO_LOCKSCREEN_DURATION, + edge = Edge.create(from = Scenes.Communal, to = LOCKSCREEN), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN), + ) val keyguardAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt index 300121facfd5..cd98bb00b9dc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt @@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_OCCLUDED_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -32,11 +35,12 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_OCCLUDED_DURATION, - from = KeyguardState.GLANCEABLE_HUB, - to = KeyguardState.OCCLUDED, - ) + animationFlow + .setup( + duration = TO_OCCLUDED_DURATION, + edge = Edge.create(from = Scenes.Communal, to = OCCLUDED), + ) + .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = OCCLUDED)) override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt index 3540bec5d3e7..74f7d75fa326 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt @@ -20,10 +20,13 @@ import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.StateToValue import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,11 +45,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_AOD_DURATION, - from = KeyguardState.GONE, - to = KeyguardState.AOD, - ) + animationFlow + .setup( + duration = TO_AOD_DURATION, + edge = Edge.create(from = Scenes.Gone, to = AOD), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GONE, to = AOD), + ) /** y-translation from the top of the screen for AOD */ fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt index 80a6bda65b99..70c0032a30b3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt @@ -19,9 +19,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DOZING_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -40,11 +43,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_DOZING_DURATION, - from = KeyguardState.GONE, - to = KeyguardState.DOZING, - ) + animationFlow + .setup( + duration = TO_DOZING_DURATION, + edge = Edge.create(from = Scenes.Gone, to = DOZING), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GONE, to = DOZING), + ) val lockscreenAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt index b52746364a8b..627f0de696d7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt @@ -18,8 +18,11 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.Flow @@ -36,11 +39,14 @@ constructor( ) { private val transitionAnimation = - animationFlow.setup( - duration = TO_DREAMING_DURATION, - from = KeyguardState.GONE, - to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, - ) + animationFlow + .setup( + duration = TO_DREAMING_DURATION, + edge = Edge.create(from = Scenes.Gone, to = DREAMING_LOCKSCREEN_HOSTED), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GONE, to = DREAMING_LOCKSCREEN_HOSTED), + ) /** Lockscreen views alpha - hide immediately */ val lockscreenAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt index 102242a4a7b0..f8b6e2819b9b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt @@ -19,8 +19,11 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.Flow @@ -34,11 +37,14 @@ constructor( ) { private val transitionAnimation = - animationFlow.setup( - duration = TO_DREAMING_DURATION, - from = KeyguardState.GONE, - to = KeyguardState.DREAMING, - ) + animationFlow + .setup( + duration = TO_DREAMING_DURATION, + edge = Edge.create(from = Scenes.Gone, to = DREAMING), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GONE, to = DREAMING), + ) /** Lockscreen views y-translation */ fun lockscreenTranslationY(translatePx: Int): Flow<Float> { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt index a2ce408955a1..08ec43f9ae5f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt @@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.Flow @@ -33,11 +36,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_LOCKSCREEN_DURATION, - from = KeyguardState.GONE, - to = KeyguardState.LOCKSCREEN - ) + animationFlow + .setup( + duration = TO_LOCKSCREEN_DURATION, + edge = Edge.create(from = Scenes.Gone, to = LOCKSCREEN), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = GONE, to = LOCKSCREEN), + ) val shortcutsAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index bbcea56799ea..f405b9d5a07c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -30,6 +30,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.BurnInModel +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.GONE @@ -38,6 +39,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.ui.StateToValue +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor import com.android.systemui.statusbar.phone.DozeParameters @@ -115,14 +117,18 @@ constructor( private val shadeInteractor: ShadeInteractor, ) { private var burnInJob: Job? = null - private val burnInModel = MutableStateFlow(BurnInModel()) + internal val burnInModel = MutableStateFlow(BurnInModel()) val burnInLayerVisibility: Flow<Int> = keyguardTransitionInteractor.startedKeyguardState .filter { it == AOD || it == LOCKSCREEN } .map { VISIBLE } - val goneToAodTransition = keyguardTransitionInteractor.transition(from = GONE, to = AOD) + val goneToAodTransition = + keyguardTransitionInteractor.transition( + edge = Edge.create(Scenes.Gone, AOD), + edgeWithoutSceneContainer = Edge.create(GONE, AOD) + ) private val goneToAodTransitionRunning: Flow<Boolean> = goneToAodTransition @@ -144,7 +150,10 @@ constructor( private val alphaOnShadeExpansion: Flow<Float> = combineTransform( - keyguardTransitionInteractor.isInTransition(from = LOCKSCREEN, to = GONE), + keyguardTransitionInteractor.isInTransition( + edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone), + edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE), + ), isOnLockscreen, shadeInteractor.qsExpansion, shadeInteractor.shadeExpansion, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt index 1f9f3043dfdf..8b5b347a763d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt @@ -20,7 +20,9 @@ import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -45,8 +47,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, + edge = Edge.create(from = LOCKSCREEN, to = AOD), ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt index c836f01e2ee9..27a1f7afb4e1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -40,8 +42,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_DOZING_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DOZING, + edge = Edge.create(from = LOCKSCREEN, to = DOZING), ) val lockscreenAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt index 19b9cf4733f9..778dbed90ec1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt @@ -18,7 +18,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -34,8 +36,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_HOSTED_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED, + edge = Edge.create(from = LOCKSCREEN, to = DREAMING_LOCKSCREEN_HOSTED), ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt index 13522a6742ac..579abeb7e092 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -40,8 +42,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_DREAMING_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DREAMING, + edge = Edge.create(from = LOCKSCREEN, to = DREAMING), ) /** Lockscreen views y-translation */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt index dae7897a2325..c7273b7cfd48 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt @@ -20,10 +20,13 @@ import com.android.app.animation.Interpolators.EMPHASIZED import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.StateToValue import com.android.systemui.res.R +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -45,11 +48,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = - animationFlow.setup( - duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GLANCEABLE_HUB, - ) + animationFlow + .setup( + duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION, + edge = Edge.create(from = LOCKSCREEN, to = Scenes.Communal), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = LOCKSCREEN, to = GLANCEABLE_HUB), + ) val keyguardAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt index f03625eda9b5..1314e8863c71 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt @@ -19,10 +19,13 @@ package com.android.systemui.keyguard.ui.viewmodel import android.util.MathUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow.FlowBuilder import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.SysuiStatusBarStateController import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -42,11 +45,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation: FlowBuilder = - animationFlow.setup( - duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.GONE, - ) + animationFlow + .setup( + duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION, + edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = LOCKSCREEN, to = GONE), + ) val shortcutsAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt index dd6652e69792..fcf8c14fc326 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt @@ -20,7 +20,9 @@ import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R @@ -45,8 +47,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_OCCLUDED_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, + edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = OCCLUDED), ) /** Lockscreen views alpha */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt index 0cfc75757b7d..23c44b0a38fb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt @@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -39,11 +42,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - ) + animationFlow + .setup( + duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, + edge = Edge.create(from = LOCKSCREEN, to = Scenes.Bouncer), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = LOCKSCREEN, to = PRIMARY_BOUNCER), + ) val shortcutsAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt index d7ba46b6e708..706a3c440723 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt @@ -19,7 +19,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -41,8 +43,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromOccludedTransitionInteractor.TO_AOD_DURATION, - from = KeyguardState.OCCLUDED, - to = KeyguardState.AOD, + edge = Edge.create(from = OCCLUDED, to = AOD), ) val deviceEntryBackgroundViewAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt index 91554e3e914a..af019300c764 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt @@ -18,7 +18,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -38,8 +40,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION, - from = KeyguardState.OCCLUDED, - to = KeyguardState.DOZING, + edge = Edge.create(from = OCCLUDED, to = DOZING), ) /** Lockscreen views alpha */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt index 73a4a9d4d2bb..47e202b8fcc3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt @@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -32,11 +35,12 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_GLANCEABLE_HUB_DURATION, - from = KeyguardState.OCCLUDED, - to = KeyguardState.GLANCEABLE_HUB, - ) + animationFlow + .setup( + duration = TO_GLANCEABLE_HUB_DURATION, + edge = Edge.create(OCCLUDED, Scenes.Communal) + ) + .setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, GLANCEABLE_HUB)) override val deviceEntryParentViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt index d2c9cfbd71b8..98dba393a545 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt @@ -17,8 +17,11 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,11 +36,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = - animationFlow.setup( - duration = DEFAULT_DURATION, - from = KeyguardState.OCCLUDED, - to = KeyguardState.GONE, - ) + animationFlow + .setup( + duration = DEFAULT_DURATION, + edge = Edge.create(from = OCCLUDED, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = OCCLUDED, to = GONE), + ) fun notificationAlpha(viewState: ViewStateAccessor): Flow<Float> { var currentAlpha = 0f diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt index a09d58ac381b..36c7d5b98dba 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt @@ -23,7 +23,9 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsIntera import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.res.R @@ -56,8 +58,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = TO_LOCKSCREEN_DURATION, - from = KeyguardState.OCCLUDED, - to = KeyguardState.LOCKSCREEN, + edge = Edge.create(from = OCCLUDED, to = LOCKSCREEN), ) /** Lockscreen views y-translation */ @@ -101,7 +102,7 @@ constructor( .filter { (wasOccluded, isOccluded) -> wasOccluded && !isOccluded && - keyguardTransitionInteractor.getCurrentState() == KeyguardState.OCCLUDED + keyguardTransitionInteractor.getCurrentState() == OCCLUDED } .map { 0f } ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt index cf6a533ed76b..1eecbd5fbda1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt @@ -17,7 +17,9 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import javax.inject.Inject @@ -34,8 +36,7 @@ constructor( private val transitionAnimation = animationFlow.setup( duration = 250.milliseconds, - from = KeyguardState.OFF, - to = KeyguardState.LOCKSCREEN, + edge = Edge.create(from = OFF, to = LOCKSCREEN), ) val shortcutsAlpha: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt index 942903bbabd7..009f85d4bcb9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt @@ -19,9 +19,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,11 +45,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION, - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.AOD, - ) + animationFlow + .setup( + duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION, + edge = Edge.create(from = Scenes.Bouncer, to = AOD), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD), + ) val deviceEntryBackgroundViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt index 13f651a9ff5d..e5bb46432226 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt @@ -19,9 +19,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_DOZING_DURATION -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -42,11 +45,14 @@ constructor( ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = TO_DOZING_DURATION, - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.DOZING, - ) + animationFlow + .setup( + duration = TO_DOZING_DURATION, + edge = Edge.create(from = Scenes.Bouncer, to = DOZING), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING), + ) val deviceEntryBackgroundViewAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt index b1fa7101804f..7ae455818952 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -20,11 +20,13 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.shared.model.ScrimAlpha import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.SysuiStatusBarStateController import dagger.Lazy import javax.inject.Inject @@ -49,11 +51,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) { private val transitionAnimation = - animationFlow.setup( - duration = TO_GONE_DURATION, - from = PRIMARY_BOUNCER, - to = GONE, - ) + animationFlow + .setup( + duration = TO_GONE_DURATION, + edge = Edge.create(from = PRIMARY_BOUNCER, to = Scenes.Gone), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE), + ) private var leaveShadeOpen: Boolean = false private var willRunDismissFromKeyguard: Boolean = false @@ -88,6 +93,7 @@ constructor( } else { createBouncerAlphaFlow(primaryBouncerInteractor::willRunDismissFromKeyguard) } + private fun createBouncerAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> { return transitionAnimation.sharedFlow( duration = 200.milliseconds, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt index 25750415e88f..7511101bf04e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt @@ -19,9 +19,12 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -39,11 +42,14 @@ constructor( animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { private val transitionAnimation = - animationFlow.setup( - duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION, - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.LOCKSCREEN, - ) + animationFlow + .setup( + duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION, + edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN), + ) + .setupWithoutSceneContainer( + edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN), + ) val shortcutsAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt index d1fee903e6f5..1a0f582fb100 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt @@ -21,9 +21,7 @@ import android.app.BroadcastOptions import android.app.PendingIntent import android.content.Context import android.content.Intent -import android.media.session.MediaController import android.media.session.MediaSession -import android.media.session.PlaybackState import android.provider.Settings import android.util.Log import com.android.internal.jank.Cuj @@ -42,7 +40,6 @@ import com.android.systemui.media.dialog.MediaOutputDialogManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.kotlin.pairwiseBy import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow @@ -70,19 +67,6 @@ constructor( .map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } } .distinctUntilChanged() - val isStartedPlaying: Flow<Boolean> = - mediaControl - .map { mediaControl -> - mediaControl?.token?.let { token -> - MediaController(applicationContext, token).playbackState?.let { - it.state == PlaybackState.STATE_PLAYING - } - } - ?: false - } - .pairwiseBy(initialValue = false) { wasPlaying, isPlaying -> !wasPlaying && isPlaying } - .distinctUntilChanged() - val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange fun removeMediaControl( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt index 73fb5583ab3e..fed93f037638 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt @@ -260,44 +260,50 @@ object MediaControlViewBinder { } SEMANTIC_ACTIONS_ALL.forEachIndexed { index, id -> - val button = viewHolder.getAction(id) - val actionViewModel = viewModel.actionButtons[index] - if (button.id == R.id.actionPrev) { - actionViewModel?.let { - viewController.setUpPrevButtonInfo(true, it.notVisibleValue) - } - } else if (button.id == R.id.actionNext) { - actionViewModel?.let { - viewController.setUpNextButtonInfo(true, it.notVisibleValue) - } + val buttonView = viewHolder.getAction(id) + val buttonModel = viewModel.actionButtons[index] + if (buttonView.id == R.id.actionPrev) { + viewController.setUpPrevButtonInfo( + buttonModel.isEnabled, + buttonModel.notVisibleValue + ) + } else if (buttonView.id == R.id.actionNext) { + viewController.setUpNextButtonInfo( + buttonModel.isEnabled, + buttonModel.notVisibleValue + ) } - actionViewModel?.let { action -> - val animHandler = (button.tag ?: AnimationBindHandler()) as AnimationBindHandler - animHandler.tryExecute { - if (animHandler.updateRebindId(action.rebindId)) { + val animHandler = (buttonView.tag ?: AnimationBindHandler()) as AnimationBindHandler + animHandler.tryExecute { + if (buttonModel.isEnabled) { + if (animHandler.updateRebindId(buttonModel.rebindId)) { animHandler.unregisterAll() - animHandler.tryRegister(action.icon) - animHandler.tryRegister(action.background) + animHandler.tryRegister(buttonModel.icon) + animHandler.tryRegister(buttonModel.background) bindButtonCommon( - button, + buttonView, viewHolder.multiRippleView, - action, + buttonModel, viewController, falsingManager, ) } - val visible = action.isVisibleWhenScrubbing || !viewController.isScrubbing - setSemanticButtonVisibleAndAlpha( - viewHolder.getAction(id), - viewController.expandedLayout, - viewController.collapsedLayout, - visible, - action.notVisibleValue, - action.showInCollapsed - ) + } else { + animHandler.unregisterAll() + clearButton(buttonView) } + val visible = + buttonModel.isEnabled && + (buttonModel.isVisibleWhenScrubbing || !viewController.isScrubbing) + setSemanticButtonVisibleAndAlpha( + viewHolder.getAction(id), + viewController.expandedLayout, + viewController.collapsedLayout, + visible, + buttonModel.notVisibleValue, + buttonModel.showInCollapsed + ) } - ?: clearButton(button) } } else { // Hide buttons that only appear for semantic actions @@ -309,22 +315,16 @@ object MediaControlViewBinder { // Set all generic buttons genericButtons.forEachIndexed { index, button -> if (index < viewModel.actionButtons.size) { - viewModel.actionButtons[index]?.let { action -> - bindButtonCommon( - button, - viewHolder.multiRippleView, - action, - viewController, - falsingManager, - ) - setVisibleAndAlpha(expandedSet, button.id, visible = true) - setVisibleAndAlpha( - collapsedSet, - button.id, - visible = action.showInCollapsed - ) - } - ?: clearButton(button) + val action = viewModel.actionButtons[index] + bindButtonCommon( + button, + viewHolder.multiRippleView, + action, + viewController, + falsingManager, + ) + setVisibleAndAlpha(expandedSet, button.id, visible = true) + setVisibleAndAlpha(collapsedSet, button.id, visible = action.showInCollapsed) } else { // Hide any unused buttons clearButton(button) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index b07253402645..188516cc7e36 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -46,6 +46,7 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN @@ -190,6 +191,7 @@ constructor( @VisibleForTesting lateinit var settingsButton: View private set + private val mediaContent: ViewGroup @VisibleForTesting var pageIndicator: PageIndicator private var needsReordering: Boolean = false @@ -360,8 +362,10 @@ constructor( ) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) mediaCarousel.repeatWhenAttached { - mediaCarouselViewModel.onAttached() - mediaCarouselScrollHandler.scrollToStart() + if (mediaFlags.isMediaControlsRefactorEnabled()) { + mediaCarouselViewModel.onAttached() + mediaCarouselScrollHandler.scrollToStart() + } repeatOnLifecycle(Lifecycle.State.STARTED) { listenForAnyStateToGoneKeyguardTransition(this) listenForAnyStateToLockscreenTransition(this) @@ -638,7 +642,7 @@ constructor( internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transition(to = GONE) + .transition(Edge.create(to = GONE)) .filter { it.transitionState == TransitionState.FINISHED } .collect { showMediaCarousel() @@ -651,7 +655,7 @@ constructor( internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor - .transition(to = LOCKSCREEN) + .transition(Edge.create(to = LOCKSCREEN)) .filter { it.transitionState == TransitionState.FINISHED } .collect { if (!allowMediaPlayerOnLockScreen) { @@ -1596,6 +1600,7 @@ internal object MediaPlayerData { // Whether should prioritize Smartspace card. internal var shouldPrioritizeSs: Boolean = false private set + internal var smartspaceMediaData: SmartspaceMediaData? = null private set diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt index 2b5985882a6e..38377088a2d7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt @@ -709,12 +709,6 @@ constructor( // For Turbulence noise. val loadingEffectView = mediaViewHolder.loadingEffectView - turbulenceNoiseAnimationConfig = - createTurbulenceNoiseConfig( - loadingEffectView, - turbulenceNoiseView, - colorSchemeTransition - ) noiseDrawCallback = object : PaintDrawCallback { override fun onDraw(paint: Paint) { @@ -809,6 +803,14 @@ constructor( fun setUpTurbulenceNoise() { if (!mediaFlags.isMediaControlsRefactorEnabled()) return + if (!this::turbulenceNoiseAnimationConfig.isInitialized) { + turbulenceNoiseAnimationConfig = + createTurbulenceNoiseConfig( + mediaViewHolder.loadingEffectView, + mediaViewHolder.turbulenceNoiseView, + colorSchemeTransition + ) + } if (Flags.shaderlibLoadingEffectRefactor()) { if (!this::loadingEffect.isInitialized) { loadingEffect = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt index bc364c36a298..1944f072e7dd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt @@ -20,6 +20,7 @@ import android.content.Context import android.content.pm.PackageManager import android.media.session.MediaController import android.media.session.MediaSession.Token +import android.media.session.PlaybackState import android.text.TextUtils import android.util.Log import androidx.constraintlayout.widget.ConstraintSet @@ -40,16 +41,14 @@ import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.monet.ColorScheme import com.android.systemui.monet.Style import com.android.systemui.res.R -import com.android.systemui.util.kotlin.sample import java.util.concurrent.Executor import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map /** Models UI state and handles user input for a media control. */ class MediaControlViewModel( @@ -60,31 +59,20 @@ class MediaControlViewModel( private val logger: MediaUiEventLogger, ) { - private val isAnyButtonClicked: MutableStateFlow<Boolean> = MutableStateFlow(false) - - private val playTurbulenceNoise: Flow<Boolean> = - interactor.mediaControl.sample( - combine(isAnyButtonClicked, interactor.isStartedPlaying) { - isButtonClicked, - isStartedPlaying -> - isButtonClicked && isStartedPlaying - } - .distinctUntilChanged() - ) - @OptIn(ExperimentalCoroutinesApi::class) val player: Flow<MediaPlayerViewModel?> = interactor.onAnyMediaConfigurationChange .flatMapLatest { - combine(playTurbulenceNoise, interactor.mediaControl) { - playTurbulenceNoise, - mediaControl -> - mediaControl?.let { toViewModel(it, playTurbulenceNoise) } + interactor.mediaControl.map { mediaControl -> + mediaControl?.let { toViewModel(it) } } } .distinctUntilChanged() .flowOn(backgroundDispatcher) + private var isPlaying = false + private var isAnyButtonClicked = false + private fun onDismissMediaData( token: Token?, uid: Int, @@ -95,10 +83,8 @@ class MediaControlViewModel( interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY) } - private suspend fun toViewModel( - model: MediaControlModel, - playTurbulenceNoise: Boolean - ): MediaPlayerViewModel? { + private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? { + val mediaController = model.token?.let { MediaController(applicationContext, it) } val wallpaperColors = MediaArtworkHelper.getWallpaperColor( applicationContext, @@ -118,8 +104,14 @@ class MediaControlViewModel( val gutsViewModel = toGutsViewModel(model, scheme) + // Set playing state + val wasPlaying = isPlaying + isPlaying = + mediaController?.playbackState?.let { it.state == PlaybackState.STATE_PLAYING } ?: false + // Resetting button clicks state. - isAnyButtonClicked.value = false + val wasButtonClicked = isAnyButtonClicked + isAnyButtonClicked = false return MediaPlayerViewModel( contentDescription = { gutsVisible -> @@ -144,7 +136,7 @@ class MediaControlViewModel( shouldAddGradient = wallpaperColors != null, colorScheme = scheme, canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons), - playTurbulenceNoise = playTurbulenceNoise, + playTurbulenceNoise = isPlaying && !wasPlaying && wasButtonClicked, useSemanticActions = model.semanticActionButtons != null, actionButtons = toActionViewModels(model), outputSwitcher = toOutputSwitcherViewModel(model), @@ -168,9 +160,7 @@ class MediaControlViewModel( seekBarViewModel.updateStaticProgress(model.resumeProgress) } else { backgroundExecutor.execute { - seekBarViewModel.updateController( - model.token?.let { MediaController(applicationContext, it) } - ) + seekBarViewModel.updateController(mediaController) } } } @@ -283,16 +273,17 @@ class MediaControlViewModel( ) } - private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel?> { + private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel> { val semanticActionButtons = model.semanticActionButtons?.let { mediaButton -> - with(mediaButton) { - val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton) - SEMANTIC_ACTIONS_ALL.map { buttonId -> - getActionById(buttonId)?.let { - toSemanticActionViewModel(model, it, buttonId, isScrubbingTimeEnabled) - } - } + val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton) + SEMANTIC_ACTIONS_ALL.map { buttonId -> + toSemanticActionViewModel( + model, + mediaButton.getActionById(buttonId), + buttonId, + isScrubbingTimeEnabled + ) } } val notifActionButtons = @@ -304,7 +295,7 @@ class MediaControlViewModel( private fun toSemanticActionViewModel( model: MediaControlModel, - mediaAction: MediaAction, + mediaAction: MediaAction?, buttonId: Int, canShowScrubbingTimeViews: Boolean ): MediaActionViewModel { @@ -312,9 +303,9 @@ class MediaControlViewModel( val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId) val shouldHideWhenScrubbing = canShowScrubbingTimeViews && hideWhenScrubbing return MediaActionViewModel( - icon = mediaAction.icon, - contentDescription = mediaAction.contentDescription, - background = mediaAction.background, + icon = mediaAction?.icon, + contentDescription = mediaAction?.contentDescription, + background = mediaAction?.background, isVisibleWhenScrubbing = !shouldHideWhenScrubbing, notVisibleValue = if ( @@ -326,11 +317,11 @@ class MediaControlViewModel( ConstraintSet.GONE }, showInCollapsed = showInCollapsed, - rebindId = mediaAction.rebindId, + rebindId = mediaAction?.rebindId, buttonId = buttonId, - isEnabled = mediaAction.action != null, + isEnabled = mediaAction?.action != null, onClicked = { id -> - mediaAction.action?.let { + mediaAction?.action?.let { onButtonClicked(id, model.uid, model.packageName, model.instanceId, it) } }, @@ -366,7 +357,7 @@ class MediaControlViewModel( ) { logger.logTapAction(id, uid, packageName, instanceId) // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT) - isAnyButtonClicked.value = true + isAnyButtonClicked = true action.run() } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt index d1014e83ea11..433434129b96 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt @@ -35,7 +35,7 @@ data class MediaPlayerViewModel( val canShowTime: Boolean, val playTurbulenceNoise: Boolean, val useSemanticActions: Boolean, - val actionButtons: List<MediaActionViewModel?>, + val actionButtons: List<MediaActionViewModel>, val outputSwitcher: MediaOutputSwitcherViewModel, val gutsMenu: GutsViewModel, val onClicked: (Expandable) -> Unit, diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java index 481b4761ccd9..67fe0e981b09 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java @@ -73,6 +73,10 @@ public class SysUiState implements Dumpable { return mFlags; } + public boolean isFlagEnabled(@SystemUiStateFlags long flag) { + return (mFlags & flag) != 0; + } + /** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */ public SysUiState setFlag(@SystemUiStateFlags long flag, boolean enabled) { final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index e4cb211a430f..0327ec760ace 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -115,7 +115,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder; -import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.sysui.ShellInterface; import dagger.Lazy; diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt index 4e290e699ac4..6694878cea74 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt @@ -20,7 +20,6 @@ import android.app.IActivityManager import android.app.NotificationManager import android.content.Context import android.content.Intent -import android.content.pm.LauncherApps import android.content.res.Resources import android.net.Uri import android.os.Handler @@ -63,7 +62,6 @@ constructor( private val panelInteractor: PanelInteractor, private val issueRecordingState: IssueRecordingState, private val iActivityManager: IActivityManager, - private val launcherApps: LauncherApps, ) : RecordingService( controller, @@ -85,7 +83,7 @@ constructor( when (intent?.action) { ACTION_START -> { TraceUtils.traceStart( - contentResolver, + this, DEFAULT_TRACE_TAGS, DEFAULT_BUFFER_SIZE, DEFAULT_IS_INCLUDING_WINSCOPE, @@ -104,11 +102,7 @@ constructor( } ACTION_STOP, ACTION_STOP_NOTIF -> { - // ViewCapture needs to save it's data before it is disabled, or else the data will - // be lost. This is expected to change in the near future, and when that happens - // this line should be removed. - launcherApps.saveViewCaptureData() - TraceUtils.traceStop(contentResolver) + TraceUtils.traceStop(this) issueRecordingState.isRecording = false } ACTION_SHARE -> { @@ -142,7 +136,7 @@ constructor( private fun shareRecording(screenRecording: Uri?) { val traces = - TraceUtils.traceDump(contentResolver, TRACE_FILE_NAME).getOrElse { + TraceUtils.traceDump(this, TRACE_FILE_NAME).getOrElse { Log.v( TAG, "Traces were not present. This can happen if users double" + diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt index caa67dff086f..1868b4a29f20 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt @@ -25,7 +25,6 @@ import android.content.Intent import android.os.UserHandle import android.util.Log import android.util.Pair -import android.view.View import android.view.Window import com.android.app.tracing.coroutines.launch import com.android.internal.app.ChooserActivity @@ -41,8 +40,8 @@ constructor( private val intentExecutor: ActionIntentExecutor, @Application private val applicationScope: CoroutineScope, @Assisted val window: Window, - @Assisted val transitionView: View, - @Assisted val onDismiss: (() -> Unit) + @Assisted val viewProxy: ScreenshotViewProxy, + @Assisted val finishDismiss: () -> Unit, ) { var isPendingSharedTransition = false @@ -50,6 +49,7 @@ constructor( fun startSharedTransition(intent: Intent, user: UserHandle, overrideTransition: Boolean) { isPendingSharedTransition = true + viewProxy.fadeForSharedTransition() val windowTransition = createWindowTransition() applicationScope.launch("$TAG#launchIntentAsync") { intentExecutor.launchIntent( @@ -70,7 +70,7 @@ constructor( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED ) pendingIntent.send(options.toBundle()) - onDismiss.invoke() + viewProxy.requestDismissal(null) } catch (e: PendingIntent.CanceledException) { Log.e(TAG, "Intent cancelled", e) } @@ -89,7 +89,7 @@ constructor( override fun hideSharedElements() { isPendingSharedTransition = false - onDismiss.invoke() + finishDismiss.invoke() } override fun onFinish() {} @@ -98,13 +98,20 @@ constructor( window, callbacks, null, - Pair.create(transitionView, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME) + Pair.create( + viewProxy.screenshotPreview, + ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME + ) ) } @AssistedFactory interface Factory { - fun create(window: Window, transitionView: View, onDismiss: (() -> Unit)): ActionExecutor + fun create( + window: Window, + viewProxy: ScreenshotViewProxy, + finishDismiss: (() -> Unit) + ): ActionExecutor } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt index a0cef529ecde..15638d3496e9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt @@ -97,6 +97,7 @@ object ActionIntentCreator { .putExtra(LongScreenshotActivity.EXTRA_SCREENSHOT_USER_HANDLE, owner) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) } private const val EXTRA_EDIT_SOURCE = "edit_source" diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt index 4cf18fb482d8..3d024a6a8ccf 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt @@ -157,6 +157,8 @@ constructor( override fun restoreNonScrollingUi() = view.restoreNonScrollingUi() + override fun fadeForSharedTransition() {} // unused + override fun stopInputListening() = view.stopInputListening() override fun requestFocus() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 2f026aee64d8..9ad6d0faea88 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -317,9 +317,9 @@ public class ScreenshotController { mConfigChanges.applyNewConfig(context.getResources()); reloadAssets(); - mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy.getScreenshotPreview(), + mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy, () -> { - requestDismissal(null); + finishDismiss(); return Unit.INSTANCE; }); @@ -623,9 +623,11 @@ public class ScreenshotController { (response) -> { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION, 0, response.getPackageName()); - if (screenshotShelfUi2() && mActionsProvider != null) { - mActionsProvider.onScrollChipReady( - () -> onScrollButtonClicked(owner, response)); + if (screenshotShelfUi2()) { + if (mActionsProvider != null) { + mActionsProvider.onScrollChipReady( + () -> onScrollButtonClicked(owner, response)); + } } else { mViewProxy.showScrollChip(response.getPackageName(), () -> onScrollButtonClicked(owner, response)); @@ -657,9 +659,7 @@ public class ScreenshotController { () -> { final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent( owner, mContext); - mActionIntentExecutor.launchIntentAsync(intent, owner, true, - ActivityOptions.makeCustomAnimation(mContext, 0, 0), null); - + mActionIntentExecutor.launchIntentAsync(intent, owner, true, null, null); }, mViewProxy::restoreNonScrollingUi, mViewProxy::startLongScreenshotTransition); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt index 1e66cd10cc61..3ac070a28b2b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt @@ -31,6 +31,7 @@ import android.view.WindowInsets import android.view.WindowManager import android.window.OnBackInvokedCallback import android.window.OnBackInvokedDispatcher +import androidx.appcompat.content.res.AppCompatResources import androidx.core.animation.doOnEnd import androidx.core.animation.doOnStart import com.android.internal.logging.UiEventLogger @@ -58,6 +59,7 @@ constructor( private val logger: UiEventLogger, private val viewModel: ScreenshotViewModel, private val windowManager: WindowManager, + shelfViewBinder: ScreenshotShelfViewBinder, private val thumbnailObserver: ThumbnailObserver, @Assisted private val context: Context, @Assisted private val displayId: Int @@ -69,7 +71,17 @@ constructor( override var callbacks: ScreenshotView.ScreenshotViewCallback? = null override var screenshot: ScreenshotData? = null set(value) { - viewModel.setScreenshotBitmap(value?.bitmap) + value?.let { + viewModel.setScreenshotBitmap(it.bitmap) + val badgeBg = + AppCompatResources.getDrawable(context, R.drawable.overlay_badge_background) + val user = it.userHandle + if (badgeBg != null && user != null) { + viewModel.setScreenshotBadge( + context.packageManager.getUserBadgedIcon(badgeBg, user) + ) + } + } field = value } @@ -78,15 +90,15 @@ constructor( override var isDismissing = false override var isPendingSharedTransition = false - private val animationController = ScreenshotAnimationController(view) + private val animationController = ScreenshotAnimationController(view, viewModel) init { - ScreenshotShelfViewBinder.bind( + shelfViewBinder.bind( view, viewModel, + animationController, LayoutInflater.from(context), onDismissalRequested = { event, velocity -> requestDismissal(event, velocity) }, - onDismissalCancelled = { animationController.getSwipeReturnAnimation().start() }, onUserInteraction = { callbacks?.onUserInteraction() } ) view.updateInsets(windowManager.currentWindowMetrics.windowInsets) @@ -176,24 +188,53 @@ constructor( override fun prepareScrollingTransition( response: ScrollCaptureResponse, - screenBitmap: Bitmap, + screenBitmap: Bitmap, // unused newScreenshot: Bitmap, screenshotTakenInPortrait: Boolean, onTransitionPrepared: Runnable, ) { - onTransitionPrepared.run() + viewModel.setScrollingScrimBitmap(newScreenshot) + viewModel.setScrollableRect(scrollableAreaOnScreen(response)) + animationController.fadeForLongScreenshotTransition() + view.post { onTransitionPrepared.run() } + } + + private fun scrollableAreaOnScreen(response: ScrollCaptureResponse): Rect { + val r = Rect(response.boundsInWindow) + val windowInScreen = response.windowBounds + r.offset(windowInScreen?.left ?: 0, windowInScreen?.top ?: 0) + r.intersect( + Rect( + 0, + 0, + context.resources.displayMetrics.widthPixels, + context.resources.displayMetrics.heightPixels + ) + ) + return r } override fun startLongScreenshotTransition( transitionDestination: Rect, onTransitionEnd: Runnable, - longScreenshot: ScrollCaptureController.LongScreenshot + longScreenshot: ScrollCaptureController.LongScreenshot, ) { - onTransitionEnd.run() - callbacks?.onDismiss() + val transitionAnimation = + animationController.runLongScreenshotTransition( + transitionDestination, + longScreenshot, + onTransitionEnd + ) + transitionAnimation.doOnEnd { callbacks?.onDismiss() } + transitionAnimation.start() } - override fun restoreNonScrollingUi() {} + override fun restoreNonScrollingUi() { + viewModel.setScrollableRect(null) + viewModel.setScrollingScrimBitmap(null) + animationController.restoreUI() + callbacks?.onUserInteraction() // reset the timeout + } override fun stopInputListening() {} @@ -216,6 +257,10 @@ constructor( ) } + override fun fadeForSharedTransition() { + animationController.fadeForSharedTransition() + } + private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) { val onBackInvokedCallback = OnBackInvokedCallback { debugLog(DEBUG_INPUT) { "Predictive Back callback dispatched" } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt index a4069d11f8fb..df93a5e56c22 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt @@ -63,6 +63,7 @@ interface ScreenshotViewProxy { longScreenshot: ScrollCaptureController.LongScreenshot ) fun restoreNonScrollingUi() + fun fadeForSharedTransition() fun stopInputListening() fun requestFocus() diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt index 06e88f46c5f1..a4906c12b487 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt @@ -20,6 +20,10 @@ import android.animation.Animator import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.animation.ValueAnimator +import android.content.res.ColorStateList +import android.graphics.BlendMode +import android.graphics.Color +import android.graphics.Matrix import android.graphics.PointF import android.graphics.Rect import android.util.MathUtils @@ -29,13 +33,21 @@ import android.widget.ImageView import androidx.core.animation.doOnEnd import androidx.core.animation.doOnStart import com.android.systemui.res.R +import com.android.systemui.screenshot.scroll.ScrollCaptureController +import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import kotlin.math.abs import kotlin.math.max import kotlin.math.sign -class ScreenshotAnimationController(private val view: ScreenshotShelfView) { +class ScreenshotAnimationController( + private val view: ScreenshotShelfView, + private val viewModel: ScreenshotViewModel +) { private var animator: Animator? = null private val screenshotPreview = view.requireViewById<ImageView>(R.id.screenshot_preview) + private val scrollingScrim = view.requireViewById<ImageView>((R.id.screenshot_scrolling_scrim)) + private val scrollTransitionPreview = + view.requireViewById<ImageView>(R.id.screenshot_scrollable_preview) private val flashView = view.requireViewById<View>(R.id.screenshot_flash) private val actionContainer = view.requireViewById<View>(R.id.actions_container_background) private val fastOutSlowIn = @@ -46,6 +58,14 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { view.requireViewById(R.id.screenshot_badge), view.requireViewById(R.id.screenshot_dismiss_button) ) + private val fadeUI = + listOf<View>( + view.requireViewById(R.id.screenshot_preview_border), + view.requireViewById(R.id.actions_container_background), + view.requireViewById(R.id.screenshot_badge), + view.requireViewById(R.id.screenshot_dismiss_button), + view.requireViewById(R.id.screenshot_message_container), + ) fun getEntranceAnimation( bounds: Rect, @@ -96,15 +116,108 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { } entranceAnimation.play(fadeInAnimator).after(previewAnimator) entranceAnimation.doOnStart { + viewModel.setIsAnimating(true) for (child in staticUI) { child.alpha = 0f } } + entranceAnimation.doOnEnd { viewModel.setIsAnimating(false) } this.animator = entranceAnimation return entranceAnimation } + fun fadeForSharedTransition() { + animator?.cancel() + val fadeAnimator = ValueAnimator.ofFloat(1f, 0f) + fadeAnimator.addUpdateListener { + for (view in fadeUI) { + view.alpha = it.animatedValue as Float + } + } + animator = fadeAnimator + fadeAnimator.start() + } + + fun runLongScreenshotTransition( + destRect: Rect, + longScreenshot: ScrollCaptureController.LongScreenshot, + onTransitionEnd: Runnable + ): Animator { + val animSet = AnimatorSet() + + val scrimAnim = ValueAnimator.ofFloat(0f, 1f) + scrimAnim.addUpdateListener { animation: ValueAnimator -> + scrollingScrim.setAlpha(1 - animation.animatedFraction) + } + scrollTransitionPreview.visibility = View.VISIBLE + if (true) { + scrollTransitionPreview.setImageBitmap(longScreenshot.toBitmap()) + val startX: Float = scrollTransitionPreview.x + val startY: Float = scrollTransitionPreview.y + val locInScreen: IntArray = scrollTransitionPreview.getLocationOnScreen() + destRect.offset(startX.toInt() - locInScreen[0], startY.toInt() - locInScreen[1]) + scrollTransitionPreview.pivotX = 0f + scrollTransitionPreview.pivotY = 0f + scrollTransitionPreview.setAlpha(1f) + val currentScale: Float = scrollTransitionPreview.width / longScreenshot.width.toFloat() + val matrix = Matrix() + matrix.setScale(currentScale, currentScale) + matrix.postTranslate( + longScreenshot.left * currentScale, + longScreenshot.top * currentScale + ) + scrollTransitionPreview.setImageMatrix(matrix) + val destinationScale: Float = destRect.width() / scrollTransitionPreview.width.toFloat() + val previewAnim = ValueAnimator.ofFloat(0f, 1f) + previewAnim.addUpdateListener { animation: ValueAnimator -> + val t = animation.animatedFraction + val currScale = MathUtils.lerp(1f, destinationScale, t) + scrollTransitionPreview.scaleX = currScale + scrollTransitionPreview.scaleY = currScale + scrollTransitionPreview.x = MathUtils.lerp(startX, destRect.left.toFloat(), t) + scrollTransitionPreview.y = MathUtils.lerp(startY, destRect.top.toFloat(), t) + } + val previewFadeAnim = ValueAnimator.ofFloat(1f, 0f) + previewFadeAnim.addUpdateListener { animation: ValueAnimator -> + scrollTransitionPreview.setAlpha(1 - animation.animatedFraction) + } + previewAnim.doOnEnd { onTransitionEnd.run() } + animSet.play(previewAnim).with(scrimAnim).before(previewFadeAnim) + } else { + // if we switched orientations between the original screenshot and the long screenshot + // capture, just fade out the scrim instead of running the preview animation + scrimAnim.doOnEnd { onTransitionEnd.run() } + animSet.play(scrimAnim) + } + animator = animSet + return animSet + } + + fun fadeForLongScreenshotTransition() { + scrollingScrim.imageTintBlendMode = BlendMode.SRC_ATOP + val anim = ValueAnimator.ofFloat(0f, .3f) + anim.addUpdateListener { + scrollingScrim.setImageTintList( + ColorStateList.valueOf(Color.argb(it.animatedValue as Float, 0f, 0f, 0f)) + ) + } + for (view in fadeUI) { + view.alpha = 0f + } + screenshotPreview.alpha = 0f + anim.setDuration(200) + anim.start() + } + + fun restoreUI() { + animator?.cancel() + for (view in fadeUI) { + view.alpha = 1f + } + screenshotPreview.alpha = 1f + } + fun getSwipeReturnAnimation(): Animator { animator?.cancel() val animator = ValueAnimator.ofFloat(view.translationX, 0f) @@ -114,6 +227,7 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { } fun getSwipeDismissAnimation(requestedVelocity: Float?): Animator { + animator?.cancel() val velocity = getAdjustedVelocity(requestedVelocity) val screenWidth = view.resources.displayMetrics.widthPixels // translation at which point the visible UI is fully off the screen (in the direction @@ -131,6 +245,8 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { view.alpha = 1f - it.animatedFraction } animator.duration = ((abs(distance / velocity))).toLong() + animator.doOnStart { viewModel.setIsAnimating(true) } + animator.doOnEnd { viewModel.setIsAnimating(false) } this.animator = animator return animator diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt index 2243ade27b2e..36aa39fa9fe4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt @@ -23,8 +23,9 @@ import android.widget.TextView import com.android.systemui.res.R import com.android.systemui.screenshot.ui.TransitioningIconDrawable import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel +import javax.inject.Inject -object ActionButtonViewBinder { +class ActionButtonViewBinder @Inject constructor() { /** Binds the given view to the given view-model */ fun bind(view: View, viewModel: ActionButtonViewModel) { val iconView = view.requireViewById<ImageView>(R.id.overlay_action_chip_icon) @@ -36,6 +37,10 @@ object ActionButtonViewBinder { // Note we never re-bind a view to a different ActionButtonViewModel, different view // models would remove/create separate views. drawable?.setIcon(viewModel.appearance.icon) + iconView.setImageDrawable(viewModel.appearance.icon) + if (!viewModel.appearance.tint) { + iconView.setImageTintList(null) + } textView.text = viewModel.appearance.label viewModel.appearance.customBackground?.also { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt index 89f904a0878f..442b3873be4d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt @@ -16,7 +16,11 @@ package com.android.systemui.screenshot.ui.binder +import android.content.res.Configuration import android.graphics.Bitmap +import android.graphics.Matrix +import android.graphics.Rect +import android.util.LayoutDirection import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -29,22 +33,26 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.screenshot.ScreenshotEvent +import com.android.systemui.screenshot.ui.ScreenshotAnimationController import com.android.systemui.screenshot.ui.ScreenshotShelfView import com.android.systemui.screenshot.ui.SwipeGestureListener import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel import com.android.systemui.screenshot.ui.viewmodel.AnimationState import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import com.android.systemui.util.children +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -object ScreenshotShelfViewBinder { +class ScreenshotShelfViewBinder +@Inject +constructor(private val buttonViewBinder: ActionButtonViewBinder) { fun bind( view: ScreenshotShelfView, viewModel: ScreenshotViewModel, + animationController: ScreenshotAnimationController, layoutInflater: LayoutInflater, onDismissalRequested: (event: ScreenshotEvent, velocity: Float?) -> Unit, - onDismissalCancelled: () -> Unit, onUserInteraction: () -> Unit ) { val swipeGestureListener = @@ -53,7 +61,7 @@ object ScreenshotShelfViewBinder { onDismiss = { onDismissalRequested(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, it) }, - onCancel = onDismissalCancelled + onCancel = { animationController.getSwipeReturnAnimation().start() } ) view.onTouchInterceptListener = { swipeGestureListener.onMotionEvent(it) } view.userInteractionCallback = onUserInteraction @@ -63,11 +71,15 @@ object ScreenshotShelfViewBinder { val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border) previewView.clipToOutline = true previewViewBlur.clipToOutline = true + val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions) val dismissButton = view.requireViewById<View>(R.id.screenshot_dismiss_button) dismissButton.visibility = if (viewModel.showDismissButton) View.VISIBLE else View.GONE dismissButton.setOnClickListener { onDismissalRequested(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, null) } + val scrollingScrim: ImageView = view.requireViewById(R.id.screenshot_scrolling_scrim) + val scrollablePreview: ImageView = view.requireViewById(R.id.screenshot_scrollable_preview) + val badgeView = view.requireViewById<ImageView>(R.id.screenshot_badge) // use immediate dispatcher to ensure screenshot bitmap is set before animation view.repeatWhenAttached(Dispatchers.Main.immediate) { @@ -87,11 +99,48 @@ object ScreenshotShelfViewBinder { } } launch { + viewModel.scrollingScrim.collect { bitmap -> + if (bitmap != null) { + scrollingScrim.setImageBitmap(bitmap) + scrollingScrim.visibility = View.VISIBLE + } else { + scrollingScrim.visibility = View.GONE + } + } + } + launch { + viewModel.scrollableRect.collect { rect -> + if (rect != null) { + setScrollablePreview( + scrollablePreview, + viewModel.preview.value, + rect + ) + } else { + scrollablePreview.visibility = View.GONE + } + } + } + launch { + viewModel.badge.collect { badge -> + badgeView.setImageDrawable(badge) + badgeView.visibility = if (badge != null) View.VISIBLE else View.GONE + } + } + launch { viewModel.previewAction.collect { onClick -> previewView.setOnClickListener { onClick?.invoke() } } } launch { + viewModel.isAnimating.collect { isAnimating -> + previewView.isClickable = !isAnimating + for (child in actionsContainer.children) { + child.isClickable = !isAnimating + } + } + } + launch { viewModel.actions.collect { actions -> updateActions( actions, @@ -151,14 +200,14 @@ object ScreenshotShelfViewBinder { val currentView: View? = actionsContainer.getChildAt(index) if (action.id == currentView?.tag) { // Same ID, update the display - ActionButtonViewBinder.bind(currentView, action) + buttonViewBinder.bind(currentView, action) } else { // Different ID. Removals have already happened so this must // mean that the new action must be inserted here. val actionButton = layoutInflater.inflate(R.layout.shelf_action_chip, actionsContainer, false) actionsContainer.addView(actionButton, index) - ActionButtonViewBinder.bind(actionButton, action) + buttonViewBinder.bind(actionButton, action) } } } @@ -181,4 +230,35 @@ object ScreenshotShelfViewBinder { screenshotPreview.layoutParams = params screenshotPreview.requestLayout() } + + private fun setScrollablePreview( + scrollablePreview: ImageView, + bitmap: Bitmap?, + scrollableRect: Rect + ) { + if (bitmap == null) { + return + } + val fixedSize = scrollablePreview.resources.getDimensionPixelSize(R.dimen.overlay_x_scale) + val inPortrait = + scrollablePreview.resources.configuration.orientation == + Configuration.ORIENTATION_PORTRAIT + val scale: Float = fixedSize / ((if (inPortrait) bitmap.width else bitmap.height).toFloat()) + val params = scrollablePreview.layoutParams + + params.width = (scale * scrollableRect.width()).toInt() + params.height = (scale * scrollableRect.height()).toInt() + val matrix = Matrix() + matrix.setScale(scale, scale) + matrix.postTranslate(-scrollableRect.left * scale, -scrollableRect.top * scale) + + scrollablePreview.translationX = + (scale * + if (scrollablePreview.layoutDirection == LayoutDirection.LTR) scrollableRect.left + else scrollableRect.right - (scrollablePreview.parent as View).width) + scrollablePreview.translationY = scale * scrollableRect.top + scrollablePreview.setImageMatrix(matrix) + scrollablePreview.setImageBitmap(bitmap) + scrollablePreview.setVisibility(View.VISIBLE) + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt index 2982ea011825..42ad326c6b45 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt @@ -25,5 +25,6 @@ constructor( val icon: Drawable?, val label: CharSequence?, val description: CharSequence, + val tint: Boolean = true, val customBackground: Drawable? = null, ) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt index 5f36f73f2135..3f99bc4597cb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt @@ -17,6 +17,8 @@ package com.android.systemui.screenshot.ui.viewmodel import android.graphics.Bitmap +import android.graphics.Rect +import android.graphics.drawable.Drawable import android.util.Log import android.view.accessibility.AccessibilityManager import kotlinx.coroutines.flow.MutableStateFlow @@ -25,6 +27,10 @@ import kotlinx.coroutines.flow.StateFlow class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) { private val _preview = MutableStateFlow<Bitmap?>(null) val preview: StateFlow<Bitmap?> = _preview + private val _scrollingScrim = MutableStateFlow<Bitmap?>(null) + val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim + private val _badge = MutableStateFlow<Drawable?>(null) + val badge: StateFlow<Drawable?> = _badge private val _previewAction = MutableStateFlow<(() -> Unit)?>(null) val previewAction: StateFlow<(() -> Unit)?> = _previewAction private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>()) @@ -32,6 +38,10 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED) val animationState: StateFlow<AnimationState> = _animationState + private val _isAnimating = MutableStateFlow(false) + val isAnimating: StateFlow<Boolean> = _isAnimating + private val _scrollableRect = MutableStateFlow<Rect?>(null) + val scrollableRect: StateFlow<Rect?> = _scrollableRect val showDismissButton: Boolean get() = accessibilityManager.isEnabled @@ -39,6 +49,14 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager _preview.value = bitmap } + fun setScrollingScrimBitmap(bitmap: Bitmap?) { + _scrollingScrim.value = bitmap + } + + fun setScreenshotBadge(badge: Drawable?) { + _badge.value = badge + } + fun setPreviewAction(onClick: () -> Unit) { _previewAction.value = onClick } @@ -107,11 +125,23 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager _animationState.value = state } + fun setIsAnimating(isAnimating: Boolean) { + _isAnimating.value = isAnimating + } + + fun setScrollableRect(rect: Rect?) { + _scrollableRect.value = rect + } + fun reset() { _preview.value = null + _scrollingScrim.value = null + _badge.value = null _previewAction.value = null _actions.value = listOf() _animationState.value = AnimationState.NOT_STARTED + _isAnimating.value = false + _scrollableRect.value = null } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java index 288ff09602f4..84156eeb9264 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java @@ -51,6 +51,16 @@ public class ToggleSeekBar extends SeekBar { return super.onTouchEvent(event); } + @Override + public boolean onHoverEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { + setHovered(true); + } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + setHovered(false); + } + return true; + } + public void setAccessibilityLabel(String label) { mAccessibilityLabel = label; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index ee7b4beec64e..22aa492dbfe8 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -123,15 +123,9 @@ constructor( private var anyBouncerShowing = false /** - * True if the shade is fully expanded and the user is not interacting with it anymore, meaning - * the hub should not receive any touch input. + * True if the shade is fully expanded, meaning the hub should not receive any touch input. * - * We need to not pause the touch handling lifecycle as soon as the shade opens because if the - * user swipes down, then back up without lifting their finger, the lifecycle will be paused - * then resumed, and resuming force-stops all active touch sessions. This means the shade will - * not receive the end of the gesture and will be stuck open. - * - * Based on [ShadeInteractor.isAnyFullyExpanded] and [ShadeInteractor.isUserInteracting]. + * Tracks [ShadeInteractor.isAnyFullyExpanded]. */ private var shadeShowing = false diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 7051d5fda159..6bb30c7b97f4 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -141,6 +141,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver; import com.android.systemui.keyguard.shared.ComposeLockscreen; +import com.android.systemui.keyguard.shared.model.Edge; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder; @@ -170,6 +171,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.shared.model.WakefulnessModel; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; +import com.android.systemui.scene.shared.model.Scenes; import com.android.systemui.shade.data.repository.FlingInfo; import com.android.systemui.shade.data.repository.ShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor; @@ -1130,8 +1132,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump controller.setup(mNotificationContainerParent)); // Dreaming->Lockscreen - collectFlow(mView, mKeyguardTransitionInteractor.transition(DREAMING, LOCKSCREEN), - mDreamingToLockscreenTransition, mMainDispatcher); + collectFlow( + mView, + mKeyguardTransitionInteractor.transition( + Edge.Companion.create(DREAMING, LOCKSCREEN)), + mDreamingToLockscreenTransition, + mMainDispatcher); collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(), setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); @@ -1141,7 +1147,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Gone -> Dreaming hosted in lockscreen collectFlow(mView, mKeyguardTransitionInteractor - .transition(GONE, DREAMING_LOCKSCREEN_HOSTED), + .transition(Edge.Companion.create(Scenes.Gone, DREAMING_LOCKSCREEN_HOSTED), + Edge.Companion.create(GONE, DREAMING_LOCKSCREEN_HOSTED)), mGoneToDreamingLockscreenHostedTransition, mMainDispatcher); collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(), setTransitionAlpha(mNotificationStackScrollLayoutController), @@ -1149,16 +1156,17 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Lockscreen -> Dreaming hosted in lockscreen collectFlow(mView, mKeyguardTransitionInteractor - .transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED), + .transition(Edge.Companion.create(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)), mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher); // Dreaming hosted in lockscreen -> Lockscreen collectFlow(mView, mKeyguardTransitionInteractor - .transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN), + .transition(Edge.Companion.create(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)), mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher); // Occluded->Lockscreen - collectFlow(mView, mKeyguardTransitionInteractor.transition(OCCLUDED, LOCKSCREEN), + collectFlow(mView, mKeyguardTransitionInteractor.transition( + Edge.Companion.create(OCCLUDED, LOCKSCREEN)), mOccludedToLockscreenTransition, mMainDispatcher); if (!MigrateClocksToBlueprint.isEnabled()) { collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), @@ -1169,7 +1177,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } // Lockscreen->Dreaming - collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING), + collectFlow(mView, mKeyguardTransitionInteractor.transition( + Edge.Companion.create(LOCKSCREEN, DREAMING)), mLockscreenToDreamingTransition, mMainDispatcher); if (!MigrateClocksToBlueprint.isEnabled()) { collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(), @@ -1181,7 +1190,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); // Gone->Dreaming - collectFlow(mView, mKeyguardTransitionInteractor.transition(GONE, DREAMING), + collectFlow(mView, mKeyguardTransitionInteractor.transition( + Edge.Companion.create(Scenes.Gone, DREAMING), + Edge.Companion.create(GONE, DREAMING)), mGoneToDreamingTransition, mMainDispatcher); if (!MigrateClocksToBlueprint.isEnabled()) { collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(), @@ -1192,7 +1203,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); // Lockscreen->Occluded - collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, OCCLUDED), + collectFlow(mView, mKeyguardTransitionInteractor.transition( + Edge.Companion.create(LOCKSCREEN, OCCLUDED)), mLockscreenToOccludedTransition, mMainDispatcher); if (!MigrateClocksToBlueprint.isEnabled()) { collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index b50a3cd442d3..6efa6334b968 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -49,6 +49,7 @@ 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.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.res.R; @@ -137,11 +138,6 @@ public class NotificationShadeWindowViewController implements Dumpable { private final PanelExpansionInteractor mPanelExpansionInteractor; private final ShadeExpansionStateManager mShadeExpansionStateManager; - /** - * If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been - * intercepted and all future touch events for the gesture should be processed by this view. - */ - private boolean mExternalTouchIntercepted = false; private boolean mIsTrackingBarGesture = false; private boolean mIsOcclusionTransitionRunning = false; private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener; @@ -225,7 +221,8 @@ public class NotificationShadeWindowViewController implements Dumpable { mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView); bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container)); - collectFlow(mView, keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING), + collectFlow(mView, keyguardTransitionInteractor.transition( + Edge.Companion.create(LOCKSCREEN, DREAMING)), mLockscreenToDreamingTransition); collectFlow( mView, @@ -258,28 +255,11 @@ public class NotificationShadeWindowViewController implements Dumpable { } /** - * Handle a touch event while dreaming or on the hub by forwarding the event to the content - * view. - * <p> - * Since important logic for handling touches lives in the dispatch/intercept phases, we - * simulate going through all of these stages before sending onTouchEvent if intercepted. - * + * Handle a touch event while dreaming by forwarding the event to the content view. * @param event The event to forward. */ - public void handleExternalTouch(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - mExternalTouchIntercepted = false; - } - - if (!mView.dispatchTouchEvent(event)) { - return; - } - if (!mExternalTouchIntercepted) { - mExternalTouchIntercepted = mView.onInterceptTouchEvent(event); - } - if (mExternalTouchIntercepted) { - mView.onTouchEvent(event); - } + public void handleDreamTouch(MotionEvent event) { + mView.dispatchTouchEvent(event); } /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index 6df8ac4c2145..8b56dbd1c277 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -207,6 +207,11 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum /** Indicates QS is at its max height */ private boolean mFullyExpanded; + /** + * Indicates QS is at its maximum height, AND takes up the whole screen (i.e. not in split + * shade). + */ + private boolean mFullScreen; private boolean mExpandedWhenExpandingStarted; private boolean mAnimatingHiddenFromCollapsed; private boolean mVisible; @@ -967,27 +972,35 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum } private void setQsFullScreen(boolean qsFullScreen) { + if (mFullScreen == qsFullScreen) { + return; // no change + } + mFullScreen = qsFullScreen; + mShadeRepository.setLegacyQsFullscreen(qsFullScreen); mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen); - if (!SceneContainerFlag.isEnabled()) { - mNotificationStackScrollLayoutController.setScrollingEnabled( - mBarState != KEYGUARD && (!qsFullScreen || mExpansionFromOverscroll)); - } } void updateQsState() { + boolean qsExpanded = getExpanded(); + if (!FooterViewRefactor.isEnabled()) { // Update full screen state; note that this will be true if the QS panel is only // partially expanded, and that is fixed with the footer view refactor. - setQsFullScreen(/* qsFullScreen = */ getExpanded() && !mSplitShadeEnabled); + setQsFullScreen(/* qsFullScreen = */ qsExpanded && !mSplitShadeEnabled); + } + + if (!SceneContainerFlag.isEnabled()) { + mNotificationStackScrollLayoutController.setScrollingEnabled( + mBarState != KEYGUARD && (!mFullScreen || mExpansionFromOverscroll)); } if (mQsStateUpdateListener != null) { - mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling); + mQsStateUpdateListener.onQsStateUpdated(qsExpanded, mStackScrollerOverscrolling); } if (mQs == null) return; - mQs.setExpanded(getExpanded()); + mQs.setExpanded(qsExpanded); } /** update expanded state of QS */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index 3d0fd89dce88..af2c1979ff77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -31,8 +31,10 @@ import com.android.systemui.statusbar.notification.dagger.PeopleHeader import com.android.systemui.statusbar.notification.icon.ConversationIconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE +import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE import javax.inject.Inject /** @@ -81,6 +83,13 @@ class ConversationCoordinator @Inject constructor( } } + val priorityPeopleSectioner = + object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) { + override fun isInSection(entry: ListEntry): Boolean { + return getPeopleType(entry) == TYPE_IMPORTANT_PERSON + } + } + // TODO(b/330193582): Rename to just "People" val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) { override fun isInSection(entry: ListEntry): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 933eb207e76d..42bf4e7d0787 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -31,15 +31,20 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.expansionChanges +import com.android.systemui.statusbar.notification.collection.GroupEntry +import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype +import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.asIndenting @@ -106,6 +111,10 @@ constructor( } private fun attachUnseenFilter(pipeline: NotifPipeline) { + if (NotificationMinimalismPrototype.V2.isEnabled) { + pipeline.addPromoter(unseenNotifPromoter) + pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotif) + } pipeline.addFinalizeFilter(unseenNotifFilter) pipeline.addCollectionListener(collectionListener) scope.launch { trackUnseenFilterSettingChanges() } @@ -263,7 +272,10 @@ constructor( } private fun unseenFeatureEnabled(): Flow<Boolean> { - if (NotificationMinimalismPrototype.V1.isEnabled) { + if ( + NotificationMinimalismPrototype.V1.isEnabled || + NotificationMinimalismPrototype.V2.isEnabled + ) { return flowOf(true) } return secureSettings @@ -334,6 +346,57 @@ constructor( } } + private fun pickOutTopUnseenNotif(list: List<ListEntry>) { + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return + // Only ever elevate a top unseen notification on keyguard, not even locked shade + if (statusBarStateController.state != StatusBarState.KEYGUARD) { + seenNotificationsInteractor.setTopUnseenNotification(null) + return + } + // On keyguard pick the top-ranked unseen or ongoing notification to elevate + seenNotificationsInteractor.setTopUnseenNotification( + list + .asSequence() + .flatMap { + when (it) { + is NotificationEntry -> listOfNotNull(it) + is GroupEntry -> it.children + else -> error("unhandled type of $it") + } + } + .filter { shouldIgnoreUnseenCheck(it) || it in unseenNotifications } + .minByOrNull { it.ranking.rank } + ) + } + + @VisibleForTesting + internal val unseenNotifPromoter = + object : NotifPromoter("$TAG-unseen") { + override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean = + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false + else + seenNotificationsInteractor.isTopUnseenNotification(child) && + NotificationMinimalismPrototype.V2.ungroupTopUnseen + } + + val unseenNotifSectioner = + object : NotifSectioner("Unseen", BUCKET_FOREGROUND_SERVICE) { + override fun isInSection(entry: ListEntry): Boolean { + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false + if ( + seenNotificationsInteractor.isTopUnseenNotification(entry.representativeEntry) + ) { + return true + } + if (entry !is GroupEntry) { + return false + } + return entry.children.any { + seenNotificationsInteractor.isTopUnseenNotification(it) + } + } + } + @VisibleForTesting internal val unseenNotifFilter = object : NotifFilter("$TAG-unseen") { @@ -351,7 +414,9 @@ constructor( * allow seen notifications to appear in the locked shade. */ private fun isOnKeyguard(): Boolean = - if ( + if (NotificationMinimalismPrototype.V2.isEnabled) { + false // disable this feature under this prototype + } else if ( NotificationMinimalismPrototype.V1.isEnabled && NotificationMinimalismPrototype.V1.showOnLockedShade ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index 36c12a719570..4506385a2fb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -24,47 +24,51 @@ import com.android.systemui.statusbar.notification.collection.SortBySectionTimeF import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider +import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor +import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject /** - * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the - * Coordinators can register their respective callbacks. + * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the Coordinators can + * register their respective callbacks. */ interface NotifCoordinators : Coordinator, PipelineDumpable @CoordinatorScope -class NotifCoordinatorsImpl @Inject constructor( - sectionStyleProvider: SectionStyleProvider, - featureFlags: FeatureFlags, - dataStoreCoordinator: DataStoreCoordinator, - hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator, - hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator, - keyguardCoordinator: KeyguardCoordinator, - rankingCoordinator: RankingCoordinator, - colorizedFgsCoordinator: ColorizedFgsCoordinator, - deviceProvisionedCoordinator: DeviceProvisionedCoordinator, - bubbleCoordinator: BubbleCoordinator, - headsUpCoordinator: HeadsUpCoordinator, - gutsCoordinator: GutsCoordinator, - conversationCoordinator: ConversationCoordinator, - debugModeCoordinator: DebugModeCoordinator, - groupCountCoordinator: GroupCountCoordinator, - groupWhenCoordinator: GroupWhenCoordinator, - mediaCoordinator: MediaCoordinator, - preparationCoordinator: PreparationCoordinator, - remoteInputCoordinator: RemoteInputCoordinator, - rowAlertTimeCoordinator: RowAlertTimeCoordinator, - rowAppearanceCoordinator: RowAppearanceCoordinator, - stackCoordinator: StackCoordinator, - shadeEventCoordinator: ShadeEventCoordinator, - smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator, - viewConfigCoordinator: ViewConfigCoordinator, - visualStabilityCoordinator: VisualStabilityCoordinator, - sensitiveContentCoordinator: SensitiveContentCoordinator, - dismissibilityCoordinator: DismissibilityCoordinator, - dreamCoordinator: DreamCoordinator, - statsLoggerCoordinator: NotificationStatsLoggerCoordinator, +class NotifCoordinatorsImpl +@Inject +constructor( + sectionStyleProvider: SectionStyleProvider, + featureFlags: FeatureFlags, + dataStoreCoordinator: DataStoreCoordinator, + hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator, + hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator, + keyguardCoordinator: KeyguardCoordinator, + rankingCoordinator: RankingCoordinator, + colorizedFgsCoordinator: ColorizedFgsCoordinator, + deviceProvisionedCoordinator: DeviceProvisionedCoordinator, + bubbleCoordinator: BubbleCoordinator, + headsUpCoordinator: HeadsUpCoordinator, + gutsCoordinator: GutsCoordinator, + conversationCoordinator: ConversationCoordinator, + debugModeCoordinator: DebugModeCoordinator, + groupCountCoordinator: GroupCountCoordinator, + groupWhenCoordinator: GroupWhenCoordinator, + mediaCoordinator: MediaCoordinator, + preparationCoordinator: PreparationCoordinator, + remoteInputCoordinator: RemoteInputCoordinator, + rowAlertTimeCoordinator: RowAlertTimeCoordinator, + rowAppearanceCoordinator: RowAppearanceCoordinator, + stackCoordinator: StackCoordinator, + shadeEventCoordinator: ShadeEventCoordinator, + smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator, + viewConfigCoordinator: ViewConfigCoordinator, + visualStabilityCoordinator: VisualStabilityCoordinator, + sensitiveContentCoordinator: SensitiveContentCoordinator, + dismissibilityCoordinator: DismissibilityCoordinator, + dreamCoordinator: DreamCoordinator, + statsLoggerCoordinator: NotificationStatsLoggerCoordinator, ) : NotifCoordinators { private val mCoreCoordinators: MutableList<CoreCoordinator> = ArrayList() @@ -114,6 +118,12 @@ class NotifCoordinatorsImpl @Inject constructor( // Manually add Ordered Sections mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService + if (NotificationMinimalismPrototype.V2.isEnabled) { + mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS) + } + if (PriorityPeopleSection.isEnabled) { + mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People + } mOrderedSections.add(conversationCoordinator.peopleAlertingSectioner) // People Alerting if (!SortBySectionTimeFlag.isEnabled) { mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent @@ -124,22 +134,26 @@ class NotifCoordinatorsImpl @Inject constructor( sectionStyleProvider.setMinimizedSections(setOf(rankingCoordinator.minimizedSectioner)) if (SortBySectionTimeFlag.isEnabled) { - sectionStyleProvider.setSilentSections(listOf( + sectionStyleProvider.setSilentSections( + listOf( rankingCoordinator.silentSectioner, rankingCoordinator.minimizedSectioner, - )) + ) + ) } else { - sectionStyleProvider.setSilentSections(listOf( + sectionStyleProvider.setSilentSections( + listOf( conversationCoordinator.peopleSilentSectioner, rankingCoordinator.silentSectioner, rankingCoordinator.minimizedSectioner, - )) + ) + ) } } /** - * Sends the pipeline to each coordinator when the pipeline is ready to accept - * [Pluggable]s, [NotifCollectionListener]s and [NotifLifetimeExtender]s. + * Sends the pipeline to each coordinator when the pipeline is ready to accept [Pluggable]s, + * [NotifCollectionListener]s and [NotifLifetimeExtender]s. */ override fun attach(pipeline: NotifPipeline) { for (c in mCoreCoordinators) { @@ -155,10 +169,11 @@ class NotifCoordinatorsImpl @Inject constructor( * As part of the NotifPipeline dumpable, dumps the list of coordinators; sections are omitted * as they are dumped in the RenderStageManager instead. */ - override fun dumpPipeline(d: PipelineDumper) = with(d) { - dump("core coordinators", mCoreCoordinators) - dump("normal coordinators", mCoordinators) - } + override fun dumpPipeline(d: PipelineDumper) = + with(d) { + dump("core coordinators", mCoreCoordinators) + dump("normal coordinators", mCoordinators) + } companion object { private const val TAG = "NotifCoordinators" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 350e88ea9df5..caa6c17ac3d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -38,6 +38,8 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; +import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.Compile; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -63,6 +65,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { public static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE); private final DelayableExecutor mDelayableExecutor; private final HeadsUpManager mHeadsUpManager; + private final SeenNotificationsInteractor mSeenNotificationsInteractor; private final ShadeAnimationInteractor mShadeAnimationInteractor; private final StatusBarStateController mStatusBarStateController; private final JavaAdapter mJavaAdapter; @@ -101,6 +104,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { HeadsUpManager headsUpManager, ShadeAnimationInteractor shadeAnimationInteractor, JavaAdapter javaAdapter, + SeenNotificationsInteractor seenNotificationsInteractor, StatusBarStateController statusBarStateController, VisibilityLocationProvider visibilityLocationProvider, VisualStabilityProvider visualStabilityProvider, @@ -109,6 +113,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { mHeadsUpManager = headsUpManager; mShadeAnimationInteractor = shadeAnimationInteractor; mJavaAdapter = javaAdapter; + mSeenNotificationsInteractor = seenNotificationsInteractor; mVisibilityLocationProvider = visibilityLocationProvider; mVisualStabilityProvider = visualStabilityProvider; mWakefulnessLifecycle = wakefulnessLifecycle; @@ -142,8 +147,15 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { private final NotifStabilityManager mNotifStabilityManager = new NotifStabilityManager("VisualStabilityCoordinator") { private boolean canMoveForHeadsUp(NotificationEntry entry) { - return entry != null && mHeadsUpManager.isHeadsUpEntry(entry.getKey()) - && !mVisibilityLocationProvider.isInVisibleLocation(entry); + if (entry == null) { + return false; + } + boolean isTopUnseen = NotificationMinimalismPrototype.V2.isEnabled() + && mSeenNotificationsInteractor.isTopUnseenNotification(entry); + if (isTopUnseen || mHeadsUpManager.isHeadsUpEntry(entry.getKey())) { + return !mVisibilityLocationProvider.isInVisibleLocation(entry); + } + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt index 5c844bcf749c..e2c9e02672d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt @@ -41,6 +41,9 @@ class ActiveNotificationListRepository @Inject constructor() { /** Stats about the list of notifications attached to the shade */ val notifStats = MutableStateFlow(NotifStats.empty) + + /** The key of the top unseen notification */ + val topUnseenNotificationKey = MutableStateFlow<String?>(null) } /** Represents the notification list, comprised of groups and individual notifications. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt index 1f7ab962c3f2..42828d99c7e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import javax.inject.Inject import kotlinx.coroutines.flow.StateFlow @@ -36,4 +38,15 @@ constructor( fun setHasFilteredOutSeenNotifications(value: Boolean) { notificationListRepository.hasFilteredOutSeenNotifications.value = value } + + /** Set the entry that is identified as the top unseen notification. */ + fun setTopUnseenNotification(entry: NotificationEntry?) { + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return + notificationListRepository.topUnseenNotificationKey.value = entry?.key + } + + /** Determine if the given notification is the top unseen notification. */ + fun isTopUnseenNotification(entry: NotificationEntry?): Boolean = + if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false + else entry != null && notificationListRepository.topUnseenNotificationKey.value == entry.key } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java index 89aa3ab9c84d..9e0dd8fc4d92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationPrio import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_HEADS_UP; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PEOPLE; +import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PRIORITY_PEOPLE; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT; import android.annotation.Nullable; @@ -130,7 +131,8 @@ public interface NotificationPanelLogger { case BUCKET_HEADS_UP: return Notifications.Notification.SECTION_HEADS_UP; case BUCKET_FOREGROUND_SERVICE: return Notifications.Notification.SECTION_FOREGROUND_SERVICE; - case BUCKET_PEOPLE: return Notifications.Notification.SECTION_PEOPLE; + case BUCKET_PEOPLE, BUCKET_PRIORITY_PEOPLE: + return Notifications.Notification.SECTION_PEOPLE; case BUCKET_ALERTING: return Notifications.Notification.SECTION_ALERTING; case BUCKET_SILENT: return Notifications.Notification.SECTION_SILENT; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 747cb3c8d934..edd2961fe119 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -101,6 +101,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; +import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; import com.android.systemui.statusbar.notification.stack.AmbientState; @@ -590,7 +591,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mMenuRow.setAppName(mAppName); } if (mIsSummaryWithChildren) { - if (!AsyncGroupHeaderViewInflation.isEnabled()) { + if (AsyncGroupHeaderViewInflation.isEnabled()) { + mChildrenContainer.updateGroupHeaderExpandState(); + } else { // We create the header from the background thread instead mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation()); @@ -843,6 +846,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } /** + * + * @return true when compact version of Heads Up is on the screen. + */ + public boolean isCompactConversationHeadsUpOnScreen() { + final NotificationViewWrapper viewWrapper = + getVisibleNotificationViewWrapper(); + + return viewWrapper instanceof NotificationCompactMessagingTemplateViewWrapper; + } + /** * @see NotificationChildrenContainer#setUntruncatedChildCount(int) */ public void setUntruncatedChildCount(int childCount) { @@ -2788,7 +2801,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - protected void expandNotification() { + /** + * Triggers expand click listener to expand the notification. + */ + public void expandNotification() { mExpandClickListener.onClick(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt index ce87d2f46d90..3a5f3b201c97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt @@ -24,7 +24,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow /** * Compact Heads up Notifications template that doesn't set feedback icon and audibly alert icons */ -class NotificationCompactHeadsUpTemplateViewWrapper( +open class NotificationCompactHeadsUpTemplateViewWrapper( ctx: Context, view: View, row: ExpandableNotificationRow diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt new file mode 100644 index 000000000000..bb40b5622159 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification.row.wrapper + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import com.android.internal.R +import com.android.internal.widget.CachingIconView +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow + +/** Wraps a notification containing a messaging or conversation template */ +class NotificationCompactMessagingTemplateViewWrapper +constructor(ctx: Context, view: View, row: ExpandableNotificationRow) : + NotificationCompactHeadsUpTemplateViewWrapper(ctx, view, row) { + + private val compactMessagingView: ViewGroup = requireNotNull(view as? ViewGroup) + + private var conversationIconView: CachingIconView? = null + private var expandBtn: View? = null + override fun onContentUpdated(row: ExpandableNotificationRow?) { + resolveViews() + super.onContentUpdated(row) + } + + private fun resolveViews() { + conversationIconView = compactMessagingView.requireViewById(R.id.conversation_icon) + expandBtn = compactMessagingView.requireViewById(R.id.expand_button) + } + + override fun updateTransformedTypes() { + super.updateTransformedTypes() + + addViewsTransformingToSimilar( + conversationIconView, + expandBtn, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 4244542f1f61..22b95efa5a95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -74,6 +74,8 @@ public abstract class NotificationViewWrapper implements TransformableView { return new NotificationCallTemplateViewWrapper(ctx, v, row); } else if ("compactHUN".equals((v.getTag()))) { return new NotificationCompactHeadsUpTemplateViewWrapper(ctx, v, row); + } else if ("compactMessagingHUN".equals((v.getTag()))) { + return new NotificationCompactMessagingTemplateViewWrapper(ctx, v, row); } if (row.getEntry().getSbn().getNotification().isStyle( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt index 8889a10f5ad1..bf37036ee018 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt @@ -24,6 +24,11 @@ import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading or using the minimalism prototype flag state. */ @Suppress("NOTHING_TO_INLINE") object NotificationMinimalismPrototype { + + val version: Int by lazy { + SystemProperties.getInt("persist.notification_minimalism_prototype.version", 2) + } + object V1 { /** The aconfig flag name */ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE @@ -35,7 +40,7 @@ object NotificationMinimalismPrototype { /** Is the heads-up cycling animation enabled */ @JvmStatic inline val isEnabled - get() = Flags.notificationMinimalismPrototype() + get() = Flags.notificationMinimalismPrototype() && version == 1 /** * the prototype will now show seen notifications on the locked shade by default, but this @@ -76,4 +81,45 @@ object NotificationMinimalismPrototype { @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) } + object V2 { + const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the heads-up cycling animation enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.notificationMinimalismPrototype() && version == 2 + + /** + * The prototype will (by default) use a promoter to ensure that the top unseen notification + * is not grouped, but this property read allows that behavior to be disabled. + */ + val ungroupTopUnseen: Boolean + get() = + if (isUnexpectedlyInLegacyMode()) false + else + SystemProperties.getBoolean( + "persist.notification_minimalism_prototype.ungroup_top_unseen", + true + ) + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an + * eng build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception + * if the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt new file mode 100644 index 000000000000..472fd9564963 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.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.systemui.statusbar.notification.shared + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for com.android.systemui.Flags.FLAG_PRIORITY_PEOPLE_SECTION */ +@Suppress("NOTHING_TO_INLINE") +object PriorityPeopleSection { + const val FLAG_NAME = Flags.FLAG_PRIORITY_PEOPLE_SECTION + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Are sections sorted by time? */ + @JvmStatic + inline val isEnabled + get() = Flags.priorityPeopleSection() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 92c597cf384e..48796d8f8f2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -439,6 +439,15 @@ public class NotificationChildrenContainer extends ViewGroup Trace.endSection(); } + /** + * Update the expand state of the group header. + */ + public void updateGroupHeaderExpandState() { + if (mGroupHeaderWrapper != null) { + mGroupHeaderWrapper.setExpanded(mChildrenExpanded); + } + } + private void removeGroupHeader() { if (mGroupHeader == null) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt index 31f4857e4b04..fc28a99ef4ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt @@ -3,15 +3,22 @@ package com.android.systemui.statusbar.notification.stack import android.annotation.IntDef /** - * For now, declare the available notification buckets (sections) here so that other - * presentation code can decide what to do based on an entry's buckets + * For now, declare the available notification buckets (sections) here so that other presentation + * code can decide what to do based on an entry's buckets */ @Retention(AnnotationRetention.SOURCE) @IntDef( - prefix = ["BUCKET_"], - value = [ - BUCKET_UNKNOWN, BUCKET_MEDIA_CONTROLS, BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, - BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT + prefix = ["BUCKET_"], + value = + [ + BUCKET_UNKNOWN, + BUCKET_MEDIA_CONTROLS, + BUCKET_HEADS_UP, + BUCKET_FOREGROUND_SERVICE, + BUCKET_PRIORITY_PEOPLE, + BUCKET_PEOPLE, + BUCKET_ALERTING, + BUCKET_SILENT ] ) annotation class PriorityBucket @@ -20,6 +27,7 @@ const val BUCKET_UNKNOWN = 0 const val BUCKET_MEDIA_CONTROLS = 1 const val BUCKET_HEADS_UP = 2 const val BUCKET_FOREGROUND_SERVICE = 3 +const val BUCKET_PRIORITY_PEOPLE = 7 const val BUCKET_PEOPLE = 4 const val BUCKET_ALERTING = 5 const val BUCKET_SILENT = 6 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index a44674542b5c..4b0b1e0029f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -72,6 +72,9 @@ constructor( */ private var maxNotificationsExcludesMedia = false + /** Whether we allow keyguard to show less important notifications above the shelf. */ + private var limitLockScreenToImportant = false + /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Float>() @@ -85,6 +88,14 @@ constructor( updateResources() } + private fun allowedByPolicy(stackHeight: StackHeight): Boolean = + if (limitLockScreenToImportant && stackHeight.includesLessImportantNotification) { + log { "\tallowedByPolicy = false" } + false + } else { + true + } + /** * Returns whether notifications and (shelf if visible) can fit in total space available. * [shelfSpace] is extra vertical space allowed for the shelf to overlap the lock icon. @@ -183,11 +194,12 @@ constructor( log { "\tGet maxNotifWithoutSavingSpace ---" } val maxNotifWithoutSavingSpace = stackHeightSequence.lastIndexWhile { heightResult -> - canStackFitInSpace( - heightResult, - notifSpace = notifSpace, - shelfSpace = shelfSpace - ) == FitResult.FIT + allowedByPolicy(heightResult) && + canStackFitInSpace( + heightResult, + notifSpace = notifSpace, + shelfSpace = shelfSpace + ) == FitResult.FIT } // How many notifications we can show at heightWithoutLockscreenConstraints @@ -212,11 +224,12 @@ constructor( saveSpaceOnLockscreen = true maxNotifications = stackHeightSequence.lastIndexWhile { heightResult -> - canStackFitInSpace( - heightResult, - notifSpace = notifSpace, - shelfSpace = shelfSpace - ) != FitResult.NO_FIT + allowedByPolicy(heightResult) && + canStackFitInSpace( + heightResult, + notifSpace = notifSpace, + shelfSpace = shelfSpace + ) != FitResult.NO_FIT } log { "\t--- maxNotifications=$maxNotifications" } } @@ -318,7 +331,10 @@ constructor( // Float height of shelf (0 if shelf is not showing), and space before the shelf that // changes during the lockscreen <=> full shade transition. - val shelfHeightWithSpaceBefore: Float + val shelfHeightWithSpaceBefore: Float, + + /** Whether this stack height includes less at least one important notification. */ + val includesLessImportantNotification: Boolean ) private fun computeHeightPerNotificationLimit( @@ -331,12 +347,15 @@ constructor( var previous: ExpandableView? = null val onLockscreen = onLockscreen() + var includesLessImportantNotification = false + // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState). yield( StackHeight( notifsHeight = 0f, notifsHeightSavingSpace = 0f, - shelfHeightWithSpaceBefore = shelfHeight + shelfHeightWithSpaceBefore = shelfHeight, + includesLessImportantNotification = includesLessImportantNotification, ) ) @@ -362,6 +381,19 @@ constructor( spaceBeforeShelf + shelfHeight } + if (limitLockScreenToImportant && !includesLessImportantNotification) { + val bucket = (currentNotification as? ExpandableNotificationRow)?.entry?.bucket + includesLessImportantNotification = + when (bucket) { + null, + BUCKET_MEDIA_CONTROLS, + BUCKET_HEADS_UP, + BUCKET_FOREGROUND_SERVICE, + BUCKET_PRIORITY_PEOPLE -> false + else -> true + } + } + log { "\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " + "notifsHeightSavingSpace=$notifsWithCollapsedHun" + @@ -371,7 +403,8 @@ constructor( StackHeight( notifsHeight = notifications, notifsHeightSavingSpace = notifsWithCollapsedHun, - shelfHeightWithSpaceBefore = shelfWithSpaceBefore + shelfHeightWithSpaceBefore = shelfWithSpaceBefore, + includesLessImportantNotification = includesLessImportantNotification, ) ) } @@ -382,11 +415,16 @@ constructor( infiniteIfNegative( if (NotificationMinimalismPrototype.V1.isEnabled) { NotificationMinimalismPrototype.V1.maxNotifs + } else if (NotificationMinimalismPrototype.V2.isEnabled) { + 1 } else { resources.getInteger(R.integer.keyguard_max_notification_count) } ) - maxNotificationsExcludesMedia = NotificationMinimalismPrototype.V1.isEnabled + maxNotificationsExcludesMedia = + NotificationMinimalismPrototype.V1.isEnabled || + NotificationMinimalismPrototype.V2.isEnabled + limitLockScreenToImportant = NotificationMinimalismPrototype.V2.isEnabled dividerHeight = max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat()) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 0ba7b3c214c6..3393321f887b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -26,6 +26,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER import com.android.systemui.keyguard.shared.model.KeyguardState.AOD @@ -64,6 +65,7 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor @@ -295,8 +297,7 @@ constructor( return combine( isOnLockscreenWithoutShade, keyguardTransitionInteractor.isInTransition( - from = LOCKSCREEN, - to = AOD, + edge = Edge.create(from = LOCKSCREEN, to = AOD) ), ::Pair ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 7d9742849a15..8fb552f167bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -283,12 +283,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner, CoreStartable void awakenDreams(); /** - * Handle a touch event while dreaming or on the glanceable hub when the touch was initiated - * within a prescribed swipeable area. This method is provided for cases where swiping in - * certain areas should be handled by CentralSurfaces instead (e.g. swiping hub open, opening - * the notification shade over dream or hub). + * Handle a touch event while dreaming when the touch was initiated within a prescribed + * swipeable area. This method is provided for cases where swiping in certain areas of a dream + * should be handled by CentralSurfaces instead (e.g. swiping communal hub open). */ - void handleExternalShadeWindowTouch(MotionEvent event); + void handleDreamTouch(MotionEvent event); boolean isBouncerShowing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt index d5e66ff660c6..8af7ee8389e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt @@ -79,7 +79,7 @@ abstract class CentralSurfacesEmptyImpl : CentralSurfaces { override fun updateScrimController() {} override fun shouldIgnoreTouch() = false override fun isDeviceInteractive() = false - override fun handleExternalShadeWindowTouch(event: MotionEvent?) {} + override fun handleDreamTouch(event: MotionEvent?) {} override fun awakenDreams() {} override fun isBouncerShowing() = false override fun isBouncerShowingScrimmed() = false 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 aa55f375b2eb..5b0b46ed18d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2932,8 +2932,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }; @Override - public void handleExternalShadeWindowTouch(MotionEvent event) { - getNotificationShadeWindowViewController().handleExternalTouch(event); + public void handleDreamTouch(MotionEvent event) { + getNotificationShadeWindowViewController().handleDreamTouch(event); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index f219b9d3c185..2b26e3f12ef7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -54,7 +54,6 @@ import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.disableflags.DisableStateTracker; @@ -133,9 +132,6 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private View mSystemIconsContainer; private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory; - // TODO(b/273443374): remove - private NotificationMediaManager mNotificationMediaManager; - private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @Override @@ -302,7 +298,6 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat @Main Executor mainExecutor, @Background Executor backgroundExecutor, KeyguardLogger logger, - NotificationMediaManager notificationMediaManager, StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory ) { super(view); @@ -357,7 +352,6 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /* mask2= */ DISABLE2_SYSTEM_ICONS, this::updateViewState ); - mNotificationMediaManager = notificationMediaManager; mStatusOverlayHoverListenerFactory = statusOverlayHoverListenerFactory; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 74182fc4d2c1..fe001b35e958 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -64,6 +64,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.shared.model.Edge; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.ScrimAlpha; import com.android.systemui.keyguard.shared.model.TransitionState; @@ -71,6 +72,7 @@ import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.res.R; +import com.android.systemui.scene.shared.model.Scenes; import com.android.systemui.scrim.ScrimView; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; @@ -454,23 +456,32 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump }; // PRIMARY_BOUNCER->GONE - collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(PRIMARY_BOUNCER, GONE), + collectFlow(behindScrim, mKeyguardTransitionInteractor.transition( + Edge.Companion.create(PRIMARY_BOUNCER, GONE)), mBouncerToGoneTransition, mMainDispatcher); collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(), mScrimAlphaConsumer, mMainDispatcher); // ALTERNATE_BOUNCER->GONE - collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(ALTERNATE_BOUNCER, GONE), + collectFlow(behindScrim, mKeyguardTransitionInteractor.transition( + Edge.Companion.create(ALTERNATE_BOUNCER, Scenes.Gone), + Edge.Companion.create(ALTERNATE_BOUNCER, GONE)), mBouncerToGoneTransition, mMainDispatcher); collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(), mScrimAlphaConsumer, mMainDispatcher); // LOCKSCREEN<->GLANCEABLE_HUB + collectFlow( + behindScrim, + mKeyguardTransitionInteractor.transition( + Edge.Companion.create(LOCKSCREEN, Scenes.Communal), + Edge.Companion.create(LOCKSCREEN, GLANCEABLE_HUB)), + mGlanceableHubConsumer, + mMainDispatcher); collectFlow(behindScrim, - mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB), - mGlanceableHubConsumer, mMainDispatcher); - collectFlow(behindScrim, - mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN), + mKeyguardTransitionInteractor.transition( + Edge.Companion.create(Scenes.Communal, LOCKSCREEN), + Edge.Companion.create(GLANCEABLE_HUB, LOCKSCREEN)), mGlanceableHubConsumer, mMainDispatcher); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index b71564627223..fa88be5b638b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1549,7 +1549,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } if (KeyguardWmStateRefactor.isEnabled()) { - mKeyguardTransitionInteractor.startDismissKeyguardTransition(); + mKeyguardTransitionInteractor.startDismissKeyguardTransition( + "SBKVM#keyguardAuthenticated"); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index a6284e3f62ab..4505a1d2c548 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -196,7 +196,22 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, // The group isn't expanded, let's make sure it's visible! mGroupExpansionManager.toggleGroupExpansion(row.getEntry()); } - row.setUserExpanded(true); + + if (android.app.Flags.compactHeadsUpNotificationReply() + && row.isCompactConversationHeadsUpOnScreen()) { + // Notification can be system expanded true and it is set user expanded in + // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType doesn't + // change. To expand huning notification properly, we need set userExpanded false. + if (!row.isPinned() && row.isExpanded()) { + row.setUserExpanded(false); + } + // expand notification emits expanded information to HUN listener. + row.expandNotification(); + } else { + // Note: Since Normal HUN has remote input view in it, we don't expect to hit + // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN. + row.setUserExpanded(true); + } row.getPrivateLayout().setOnExpandedVisibleListener(runnable); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt index cc87e8a45d13..0a6e95eee127 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED @@ -81,12 +82,12 @@ constructor( ) : CollapsedStatusBarViewModel { override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> = keyguardTransitionInteractor - .isInTransition(LOCKSCREEN, OCCLUDED) + .isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED)) .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false) override val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> = keyguardTransitionInteractor - .transition(LOCKSCREEN, DREAMING) + .transition(Edge.create(from = LOCKSCREEN, to = DREAMING)) .filter { it.transitionState == TransitionState.STARTED } .map {} diff --git a/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java new file mode 100644 index 000000000000..efeb2f919b56 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java @@ -0,0 +1,322 @@ +/* + * 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.theme; + +import com.google.ux.material.libmonet.dynamiccolor.ContrastCurve; +import com.google.ux.material.libmonet.dynamiccolor.DynamicColor; +import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors; +import com.google.ux.material.libmonet.dynamiccolor.ToneDeltaPair; +import com.google.ux.material.libmonet.dynamiccolor.TonePolarity; + +class CustomDynamicColors { + private final MaterialDynamicColors mMdc; + + CustomDynamicColors(boolean isExtendedFidelity) { + this.mMdc = new MaterialDynamicColors(isExtendedFidelity); + } + + // CLOCK COLORS + + public DynamicColor widgetBackground() { + return new DynamicColor( + /* name= */ "widget_background", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> s.isDark ? 20.0 : 95.0, + /* isBackground= */ true, + /* background= */ null, + /* secondBackground= */ null, + /* contrastCurve= */ null, + /* toneDeltaPair= */ null); + } + + public DynamicColor clockHour() { + return new DynamicColor( + /* name= */ "clock_hour", + /* palette= */ (s) -> s.secondaryPalette, + /* tone= */ (s) -> s.isDark ? 30.0 : 60.0, + /* isBackground= */ false, + /* background= */ (s) -> widgetBackground(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 4.0, 5.0, 15.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(clockHour(), clockMinute(), 10.0, TonePolarity.DARKER, + false)); + } + + public DynamicColor clockMinute() { + return new DynamicColor( + /* name= */ "clock_minute", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> s.isDark ? 40.0 : 90.0, + /* isBackground= */ false, + /* background= */ (s) -> widgetBackground(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 6.5, 10.0, 15.0), + /* toneDeltaPair= */ null); + } + + public DynamicColor clockSecond() { + return new DynamicColor( + /* name= */ "clock_second", + /* palette= */ (s) -> s.tertiaryPalette, + /* tone= */ (s) -> s.isDark ? 40.0 : 90.0, + /* isBackground= */ false, + /* background= */ (s) -> widgetBackground(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0), + /* toneDeltaPair= */ null); + } + + public DynamicColor weatherTemp() { + return new DynamicColor( + /* name= */ "clock_second", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> s.isDark ? 55.0 : 80.0, + /* isBackground= */ false, + /* background= */ (s) -> widgetBackground(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0), + /* toneDeltaPair= */ null); + } + + // THEME APP ICONS + + public DynamicColor themeApp() { + return new DynamicColor( + /* name= */ "theme_app", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> s.isDark ? 90.0 : 30.0, // Adjusted values + /* isBackground= */ true, + /* background= */ null, + /* secondBackground= */ null, + /* contrastCurve= */ null, + /* toneDeltaPair= */ null); + } + + public DynamicColor onThemeApp() { + return new DynamicColor( + /* name= */ "on_theme_app", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> s.isDark ? 40.0 : 80.0, // Adjusted values + /* isBackground= */ false, + /* background= */ (s) -> themeApp(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 10.0), + /* toneDeltaPair= */ null); + } + + public DynamicColor themeAppRing() { + return new DynamicColor( + /* name= */ "theme_app_ring", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> 70.0, + /* isBackground= */ true, + /* background= */ null, + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0), + /* toneDeltaPair= */ null); + } + + public DynamicColor themeNotif() { + return new DynamicColor( + /* name= */ "theme_notif", + /* palette= */ (s) -> s.tertiaryPalette, + /* tone= */ (s) -> s.isDark ? 80.0 : 90.0, + /* isBackground= */ false, + /* background= */ (s) -> themeAppRing(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(themeNotif(), themeAppRing(), 10.0, TonePolarity.NEARER, + false)); + } + + // SUPER G COLORS + + public DynamicColor brandA() { + return new DynamicColor( + /* name= */ "brand_a", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> s.isDark ? 40.0 : 80.0, + /* isBackground= */ true, + /* background= */ (s) -> mMdc.surfaceContainerLow(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 17.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(brandA(), brandB(), 10.0, TonePolarity.NEARER, false)); + } + + public DynamicColor brandB() { + return new DynamicColor( + /* name= */ "brand_b", + /* palette= */ (s) -> s.secondaryPalette, + /* tone= */ (s) -> s.isDark ? 70.0 : 98.0, + /* isBackground= */ true, + /* background= */ (s) -> mMdc.surfaceContainerLow(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 3.0, 6.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(brandB(), brandC(), 10.0, TonePolarity.NEARER, false)); + } + + public DynamicColor brandC() { + return new DynamicColor( + /* name= */ "brand_c", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> s.isDark ? 50.0 : 60.0, + /* isBackground= */ false, + /* background= */ (s) -> mMdc.surfaceContainerLow(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 9.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(brandC(), brandD(), 10.0, TonePolarity.NEARER, false)); + } + + public DynamicColor brandD() { + return new DynamicColor( + /* name= */ "brand_d", + /* palette= */ (s) -> s.tertiaryPalette, + /* tone= */ (s) -> s.isDark ? 59.0 : 90.0, + /* isBackground= */ false, + /* background= */ (s) -> mMdc.surfaceContainerLow(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 13.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(brandD(), brandA(), 10.0, TonePolarity.NEARER, false)); + } + + // QUICK SETTING TIILES + + public DynamicColor underSurface() { + return new DynamicColor( + /* name= */ "under_surface", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> 0.0, + /* isBackground= */ true, + /* background= */ null, + /* secondBackground= */ null, + /* contrastCurve= */ null, + /* toneDeltaPair= */ null); + } + + public DynamicColor shadeActive() { + return new DynamicColor( + /* name= */ "shade_active", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> 90.0, + /* isBackground= */ false, + /* background= */ (s) -> underSurface(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.5, 7.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(shadeActive(), shadeInactive(), 30.0, TonePolarity.LIGHTER, + false)); + } + + public DynamicColor onShadeActive() { + return new DynamicColor( + /* name= */ "on_shade_active", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> 10.0, + /* isBackground= */ false, + /* background= */ (s) -> shadeActive(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(onShadeActive(), onShadeActiveVariant(), 20.0, + TonePolarity.NEARER, false)); + } + + public DynamicColor onShadeActiveVariant() { + return new DynamicColor( + /* name= */ "on_shade_active_variant", + /* palette= */ (s) -> s.primaryPalette, + /* tone= */ (s) -> 30.0, + /* isBackground= */ false, + /* background= */ (s) -> shadeActive(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(onShadeActiveVariant(), onShadeActive(), 20.0, + TonePolarity.NEARER, false)); + } + + public DynamicColor shadeInactive() { + return new DynamicColor( + /* name= */ "shade_inactive", + /* palette= */ (s) -> s.neutralPalette, + /* tone= */ (s) -> 20.0, + /* isBackground= */ true, + /* background= */ (s) -> underSurface(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0), + /* toneDeltaPair= */(s) -> new ToneDeltaPair(shadeInactive(), shadeDisabled(), 15.0, + TonePolarity.LIGHTER, false)); + } + + public DynamicColor onShadeInactive() { + return new DynamicColor( + /* name= */ "on_shade_inactive", + /* palette= */ (s) -> s.neutralVariantPalette, + /* tone= */ (s) -> 90.0, + /* isBackground= */ true, + /* background= */ (s) -> shadeInactive(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(onShadeInactive(), onShadeInactiveVariant(), 10.0, + TonePolarity.NEARER, false)); + } + + public DynamicColor onShadeInactiveVariant() { + return new DynamicColor( + /* name= */ "on_shade_inactive_variant", + /* palette= */ (s) -> s.neutralVariantPalette, + /* tone= */ (s) -> 80.0, + /* isBackground= */ false, + /* background= */ (s) -> shadeInactive(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0), + /* toneDeltaPair= */ + (s) -> new ToneDeltaPair(onShadeInactiveVariant(), onShadeInactive(), 10.0, + TonePolarity.NEARER, false)); + } + + public DynamicColor shadeDisabled() { + return new DynamicColor( + /* name= */ "shade_disabled", + /* palette= */ (s) -> s.neutralPalette, + /* tone= */ (s) -> 4.0, + /* isBackground= */ false, + /* background= */ (s) -> underSurface(), + /* secondBackground= */ null, + /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0), + /* toneDeltaPair= */ null); + } + + public DynamicColor overviewBackground() { + return new DynamicColor( + /* name= */ "overview_background", + /* palette= */ (s) -> s.neutralVariantPalette, + /* tone= */ (s) -> s.isDark ? 80.0 : 35.0, + /* isBackground= */ true, + /* background= */ null, + /* secondBackground= */ null, + /* contrastCurve= */null, + /* toneDeltaPair= */ null); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt index a983d2f9b78e..35187597bd83 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt +++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt @@ -103,5 +103,33 @@ class DynamicColors { Pair.create("on_tertiary_fixed_variant", mdc.onTertiaryFixedVariant()), ) } + + @JvmStatic + fun getCustomColorsMapped(isExtendedFidelity: Boolean): List<Pair<String, DynamicColor>> { + val customMdc = CustomDynamicColors(isExtendedFidelity) + return arrayListOf( + Pair.create("widget_background", customMdc.widgetBackground()), + Pair.create("clock_hour", customMdc.clockHour()), + Pair.create("clock_minute", customMdc.clockMinute()), + Pair.create("clock_second", customMdc.weatherTemp()), + Pair.create("theme_app", customMdc.themeApp()), + Pair.create("on_theme_app", customMdc.onThemeApp()), + Pair.create("theme_app_ring", customMdc.themeAppRing()), + Pair.create("on_theme_app_ring", customMdc.themeNotif()), + Pair.create("brand_a", customMdc.brandA()), + Pair.create("brand_b", customMdc.brandB()), + Pair.create("brand_c", customMdc.brandC()), + Pair.create("brand_d", customMdc.brandD()), + Pair.create("under_surface", customMdc.underSurface()), + Pair.create("shade_active", customMdc.shadeActive()), + Pair.create("on_shade_active", customMdc.onShadeActive()), + Pair.create("on_shade_active_variant", customMdc.onShadeActiveVariant()), + Pair.create("shade_inactive", customMdc.shadeInactive()), + Pair.create("on_shade_inactive", customMdc.onShadeInactive()), + Pair.create("on_shade_inactive_variant", customMdc.onShadeInactiveVariant()), + Pair.create("shade_disabled", customMdc.shadeDisabled()), + Pair.create("overview_background", customMdc.overviewBackground()) + ) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 5c3bbb7ca253..d256c4acd955 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -56,6 +56,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; @@ -84,6 +85,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.SecureSettings; +import com.google.ux.material.libmonet.dynamiccolor.DynamicColor; import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors; import org.json.JSONException; @@ -631,29 +633,33 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { protected FabricatedOverlay createDynamicOverlay() { FabricatedOverlay overlay = newFabricatedOverlay("dynamic"); - assignDynamicPaletteToOverlay(overlay, true /* isDark */); - assignDynamicPaletteToOverlay(overlay, false /* isDark */); - assignFixedColorsToOverlay(overlay); + //Themed Colors + assignColorsToOverlay(overlay, DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled), + false); + // Fixed Colors + assignColorsToOverlay(overlay, DynamicColors.getFixedColorsMapped(mIsFidelityEnabled), + true); + //Custom Colors + assignColorsToOverlay(overlay, DynamicColors.getCustomColorsMapped(mIsFidelityEnabled), + false); return overlay; } - private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) { - String suffix = isDark ? "dark" : "light"; - ColorScheme scheme = isDark ? mDarkColorScheme : mLightColorScheme; - DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled).forEach(p -> { - String resourceName = "android:color/system_" + p.first + "_" + suffix; - int colorValue = p.second.getArgb(scheme.getMaterialScheme()); - overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue, - null /* configuration */); - }); - } + private void assignColorsToOverlay(FabricatedOverlay overlay, + List<Pair<String, DynamicColor>> colors, Boolean isFixed) { + colors.forEach(p -> { + String prefix = "android:color/system_" + p.first; - private void assignFixedColorsToOverlay(FabricatedOverlay overlay) { - DynamicColors.getFixedColorsMapped(mIsFidelityEnabled).forEach(p -> { - String resourceName = "android:color/system_" + p.first; - int colorValue = p.second.getArgb(mLightColorScheme.getMaterialScheme()); - overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue, - null /* configuration */); + if (isFixed) { + overlay.setResourceValue(prefix, TYPE_INT_COLOR_ARGB8, + p.second.getArgb(mLightColorScheme.getMaterialScheme()), null); + return; + } + + overlay.setResourceValue(prefix + "_light", TYPE_INT_COLOR_ARGB8, + p.second.getArgb(mLightColorScheme.getMaterialScheme()), null); + overlay.setResourceValue(prefix + "_dark", TYPE_INT_COLOR_ARGB8, + p.second.getArgb(mDarkColorScheme.getMaterialScheme()), null); }); } diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt index bfed0c44f6b7..0a1724c189c8 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt @@ -70,8 +70,10 @@ constructor( val isGuestUserResetting: Boolean = repository.isGuestUserResetting init { - resumeSessionReceiver.register() - resetOrExitSessionReceiver.register() + if (applicationContext.userId == UserHandle.USER_SYSTEM) { + resumeSessionReceiver.register() + resetOrExitSessionReceiver.register() + } } /** Notifies that the device has finished booting. */ diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt index 46ce5f2623de..1ec86a4d1dfc 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt @@ -97,3 +97,12 @@ fun <T> collectFlow( fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> { return combine(flow1, flow2, bifunction) } + +fun <A, B, C, R> combineFlows( + flow1: Flow<A>, + flow2: Flow<B>, + flow3: Flow<C>, + trifunction: (A, B, C) -> R +): Flow<R> { + return combine(flow1, flow2, flow3, trifunction) +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt index 155102c9b9a7..369610882959 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt @@ -27,6 +27,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractor +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractorImpl import dagger.Binds import dagger.Module import dagger.Provides @@ -41,6 +43,11 @@ interface MediaDevicesModule { impl: LocalMediaRepositoryFactoryImpl ): LocalMediaRepositoryFactory + @Binds + fun bindMediaControllerInteractor( + impl: MediaControllerInteractorImpl + ): MediaControllerInteractor + companion object { @Provides diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt index 1f037c0280e3..4812765d4afe 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settingslib.volume.data.repository +package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor import android.media.MediaMetadata import android.media.session.MediaController @@ -22,79 +22,75 @@ import android.media.session.MediaSession import android.media.session.PlaybackState import android.os.Bundle import android.os.Handler +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel +import javax.inject.Inject import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.launch -/** [MediaController.Callback] flow representation. */ -fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> { - return callbackFlow { - val callback = MediaControllerCallbackProducer(this) - registerCallback(callback, handler) - awaitClose { unregisterCallback(callback) } - } -} - -/** Models particular change event received by [MediaController.Callback]. */ -sealed interface MediaControllerChange { - - data object SessionDestroyed : MediaControllerChange - - data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange - - data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange +interface MediaControllerInteractor { - data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange - - data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) : - MediaControllerChange - - data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange - - data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange + /** [MediaController.Callback] flow representation. */ + fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> +} - data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange +@SysUISingleton +class MediaControllerInteractorImpl +@Inject +constructor( + @Background private val backgroundHandler: Handler, +) : MediaControllerInteractor { + + override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> { + return conflatedCallbackFlow { + val callback = MediaControllerCallbackProducer(this) + mediaController.registerCallback(callback, backgroundHandler) + awaitClose { mediaController.unregisterCallback(callback) } + } + } } private class MediaControllerCallbackProducer( - private val producingScope: ProducerScope<MediaControllerChange> + private val producingScope: ProducerScope<MediaControllerChangeModel> ) : MediaController.Callback() { override fun onSessionDestroyed() { - send(MediaControllerChange.SessionDestroyed) + send(MediaControllerChangeModel.SessionDestroyed) } override fun onSessionEvent(event: String, extras: Bundle?) { - send(MediaControllerChange.SessionEvent(event, extras)) + send(MediaControllerChangeModel.SessionEvent(event, extras)) } override fun onPlaybackStateChanged(state: PlaybackState?) { - send(MediaControllerChange.PlaybackStateChanged(state)) + send(MediaControllerChangeModel.PlaybackStateChanged(state)) } override fun onMetadataChanged(metadata: MediaMetadata?) { - send(MediaControllerChange.MetadataChanged(metadata)) + send(MediaControllerChangeModel.MetadataChanged(metadata)) } override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) { - send(MediaControllerChange.QueueChanged(queue)) + send(MediaControllerChangeModel.QueueChanged(queue)) } override fun onQueueTitleChanged(title: CharSequence?) { - send(MediaControllerChange.QueueTitleChanged(title)) + send(MediaControllerChangeModel.QueueTitleChanged(title)) } override fun onExtrasChanged(extras: Bundle?) { - send(MediaControllerChange.ExtrasChanged(extras)) + send(MediaControllerChangeModel.ExtrasChanged(extras)) } override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) { - send(MediaControllerChange.AudioInfoChanged(info)) + send(MediaControllerChangeModel.AudioInfoChanged(info)) } - private fun send(change: MediaControllerChange) { + private fun send(change: MediaControllerChangeModel) { producingScope.launch { producingScope.send(change) } } } 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 dc73344fafe6..599bd73abb69 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 @@ -18,11 +18,9 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.media.session.MediaController import android.media.session.PlaybackState -import android.os.Handler -import com.android.settingslib.volume.data.repository.MediaControllerChange import com.android.settingslib.volume.data.repository.MediaControllerRepository -import com.android.settingslib.volume.data.repository.stateChanges 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 @@ -45,38 +43,39 @@ class MediaDeviceSessionInteractor @Inject constructor( @Background private val backgroundCoroutineContext: CoroutineContext, - @Background private val backgroundHandler: Handler, + private val mediaControllerInteractor: MediaControllerInteractor, private val mediaControllerRepository: MediaControllerRepository, ) { /** [PlaybackState] changes for the [MediaDeviceSession]. */ fun playbackState(session: MediaDeviceSession): Flow<PlaybackState?> { return stateChanges(session) { - emit(MediaControllerChange.PlaybackStateChanged(it.playbackState)) + emit(MediaControllerChangeModel.PlaybackStateChanged(it.playbackState)) } - .filterIsInstance(MediaControllerChange.PlaybackStateChanged::class) + .filterIsInstance(MediaControllerChangeModel.PlaybackStateChanged::class) .map { it.state } } /** [MediaController.PlaybackInfo] changes for the [MediaDeviceSession]. */ fun playbackInfo(session: MediaDeviceSession): Flow<MediaController.PlaybackInfo?> { return stateChanges(session) { - emit(MediaControllerChange.AudioInfoChanged(it.playbackInfo)) + emit(MediaControllerChangeModel.AudioInfoChanged(it.playbackInfo)) } - .filterIsInstance(MediaControllerChange.AudioInfoChanged::class) + .filterIsInstance(MediaControllerChangeModel.AudioInfoChanged::class) .map { it.info } } private fun stateChanges( session: MediaDeviceSession, - onStart: suspend FlowCollector<MediaControllerChange>.(controller: MediaController) -> Unit, - ): Flow<MediaControllerChange?> = + onStart: + suspend FlowCollector<MediaControllerChangeModel>.(controller: MediaController) -> Unit, + ): Flow<MediaControllerChangeModel?> = mediaControllerRepository.activeSessions .flatMapLatest { controllers -> val controller: MediaController = findControllerForSession(controllers, session) ?: return@flatMapLatest flowOf(null) - controller.stateChanges(backgroundHandler).onStart { onStart(controller) } + mediaControllerInteractor.stateChanges(controller).onStart { onStart(controller) } } .flowOn(backgroundCoroutineContext) 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 b00829e48404..9fbd79accf80 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 @@ -19,12 +19,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.content.pm.PackageManager import android.media.VolumeProvider import android.media.session.MediaController -import android.os.Handler import android.util.Log 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.settingslib.volume.data.repository.stateChanges import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions @@ -61,7 +59,7 @@ constructor( @VolumePanelScope private val coroutineScope: CoroutineScope, @Background private val backgroundCoroutineContext: CoroutineContext, mediaControllerRepository: MediaControllerRepository, - @Background private val backgroundHandler: Handler, + private val mediaControllerInteractor: MediaControllerInteractor, ) { private val activeMediaControllers: Flow<MediaControllers> = @@ -194,7 +192,10 @@ constructor( return flowOf(null) } - return stateChanges(backgroundHandler).map { this }.onStart { emit(this@stateChanges) } + return mediaControllerInteractor + .stateChanges(this) + .map { this } + .onStart { emit(this@stateChanges) } } private data class MediaControllers( diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt new file mode 100644 index 000000000000..ef5a44a7a2fd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.domain.model + +import android.media.MediaMetadata +import android.media.session.MediaController +import android.media.session.MediaSession +import android.media.session.PlaybackState +import android.os.Bundle + +/** Models particular change event received by [MediaController.Callback]. */ +sealed interface MediaControllerChangeModel { + + data object SessionDestroyed : MediaControllerChangeModel + + data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChangeModel + + data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChangeModel + + data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChangeModel + + data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) : + MediaControllerChangeModel + + data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChangeModel + + data class ExtrasChanged(val extras: Bundle?) : MediaControllerChangeModel + + data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : + MediaControllerChangeModel +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index 1568e8c011a1..2e29bbd33f92 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -20,6 +20,7 @@ import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.app.WallpaperManager.SetWallpaperFlags; +import static com.android.systemui.Flags.fixImageWallpaperCrashSurfaceAlreadyReleased; import static com.android.window.flags.Flags.offloadColorExtraction; import android.annotation.Nullable; @@ -128,8 +129,17 @@ public class ImageWallpaper extends WallpaperService { * and if the count is 0, unload the bitmap */ private int mBitmapUsages = 0; + + /** + * Main lock for long operations (loading the bitmap or processing colors). + */ private final Object mLock = new Object(); + /** + * Lock for SurfaceHolder operations. Should only be acquired after the main lock. + */ + private final Object mSurfaceLock = new Object(); + CanvasEngine() { super(); setFixedSizeAllowed(true); @@ -223,6 +233,12 @@ public class ImageWallpaper extends WallpaperService { if (DEBUG) { Log.i(TAG, "onSurfaceDestroyed"); } + if (fixImageWallpaperCrashSurfaceAlreadyReleased()) { + synchronized (mSurfaceLock) { + mSurfaceHolder = null; + } + return; + } mLongExecutor.execute(this::onSurfaceDestroyedSynchronized); } @@ -259,7 +275,7 @@ public class ImageWallpaper extends WallpaperService { } private void drawFrameInternal() { - if (mSurfaceHolder == null) { + if (mSurfaceHolder == null && !fixImageWallpaperCrashSurfaceAlreadyReleased()) { Log.i(TAG, "attempt to draw a frame without a valid surface"); return; } @@ -268,6 +284,19 @@ public class ImageWallpaper extends WallpaperService { if (!isBitmapLoaded()) { loadWallpaperAndDrawFrameInternal(); } else { + if (fixImageWallpaperCrashSurfaceAlreadyReleased()) { + synchronized (mSurfaceLock) { + if (mSurfaceHolder == null) { + Log.i(TAG, "Surface released before the image could be drawn"); + return; + } + mBitmapUsages++; + drawFrameOnCanvas(mBitmap); + reportEngineShown(false); + unloadBitmapIfNotUsedInternal(); + return; + } + } mBitmapUsages++; drawFrameOnCanvas(mBitmap); reportEngineShown(false); @@ -328,9 +357,14 @@ public class ImageWallpaper extends WallpaperService { mBitmap.recycle(); } mBitmap = null; - - final Surface surface = getSurfaceHolder().getSurface(); - surface.hwuiDestroy(); + if (fixImageWallpaperCrashSurfaceAlreadyReleased()) { + synchronized (mSurfaceLock) { + if (mSurfaceHolder != null) mSurfaceHolder.getSurface().hwuiDestroy(); + } + } else { + final Surface surface = getSurfaceHolder().getSurface(); + surface.hwuiDestroy(); + } mWallpaperManager.forgetLoadedWallpaper(); Trace.endSection(); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 6f550ba70045..5702a8c61e7a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -21,14 +21,18 @@ import android.view.View import android.view.ViewTreeObserver import android.widget.FrameLayout import androidx.test.filters.SmallTest -import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.Edge +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.log.core.LogLevel @@ -68,8 +72,9 @@ import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import com.android.systemui.Flags as AConfigFlags +import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @SmallTest @@ -319,26 +324,16 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForDozeAmountTransition_updatesClockDozeAmount() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever( - keyguardTransitionInteractor.transition( - KeyguardState.LOCKSCREEN, - KeyguardState.AOD - ) - ) + whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD))) .thenReturn(transitionStep) - whenever( - keyguardTransitionInteractor.transition( - KeyguardState.AOD, - KeyguardState.LOCKSCREEN - ) - ) + whenever(keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN))) .thenReturn(transitionStep) val job = underTest.listenForDozeAmountTransition(this) transitionStep.value = TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, + from = LOCKSCREEN, + to = AOD, value = 0.4f, transitionState = TransitionState.RUNNING, ) @@ -353,14 +348,14 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD)) + whenever(keyguardTransitionInteractor.transitionStepsToState(AOD)) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToAodTransition(this) transitionStep.value = TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, + from = GONE, + to = AOD, transitionState = TransitionState.STARTED, ) yield() @@ -374,16 +369,16 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN)) - .thenReturn(transitionStep) + whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN)) + .thenReturn(transitionStep) val job = underTest.listenForAnyStateToLockscreenTransition(this) transitionStep.value = - TransitionStep( - from = KeyguardState.OCCLUDED, - to = KeyguardState.LOCKSCREEN, - transitionState = TransitionState.STARTED, - ) + TransitionStep( + from = OCCLUDED, + to = LOCKSCREEN, + transitionState = TransitionState.STARTED, + ) yield() verify(animations, times(2)).doze(0f) @@ -395,37 +390,37 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD)) + whenever(keyguardTransitionInteractor.transitionStepsToState(AOD)) .thenReturn(transitionStep) val job = underTest.listenForAnyStateToAodTransition(this) transitionStep.value = TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, + from = LOCKSCREEN, + to = AOD, transitionState = TransitionState.STARTED, ) yield() verify(animations, never()).doze(1f) - job.cancel() - } + job.cancel() + } @Test fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN)) - .thenReturn(transitionStep) + whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN)) + .thenReturn(transitionStep) val job = underTest.listenForAnyStateToLockscreenTransition(this) transitionStep.value = - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, - transitionState = TransitionState.STARTED, - ) + TransitionStep( + from = AOD, + to = LOCKSCREEN, + transitionState = TransitionState.STARTED, + ) yield() verify(animations, never()).doze(0f) @@ -437,16 +432,16 @@ class ClockEventControllerTest : SysuiTestCase() { fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() = runBlocking(IMMEDIATE) { val transitionStep = MutableStateFlow(TransitionStep()) - whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.DOZING)) - .thenReturn(transitionStep) + whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING)) + .thenReturn(transitionStep) val job = underTest.listenForAnyStateToDozingTransition(this) transitionStep.value = - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.DOZING, - transitionState = TransitionState.STARTED, - ) + TransitionStep( + from = LOCKSCREEN, + to = DOZING, + transitionState = TransitionState.STARTED, + ) yield() verify(animations, times(2)).doze(1f) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt index 86533086f67a..44a8904f50da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt @@ -26,6 +26,8 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope +import com.android.systemui.model.sysUiState +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -47,7 +49,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { private val testScope = kosmos.testScope private val testHelper = kosmos.shortcutHelperTestHelper - + private val sysUiState = kosmos.sysUiState private val viewModel = kosmos.shortcutHelperViewModel @Test @@ -90,12 +92,12 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { } @Test - fun shouldShow_falseAfterViewDestroyed() = + fun shouldShow_falseAfterViewClosed() = testScope.runTest { val shouldShow by collectLastValue(viewModel.shouldShow) testHelper.toggle(deviceId = 567) - viewModel.onUserLeave() + viewModel.onViewClosed() assertThat(shouldShow).isFalse() } @@ -108,7 +110,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { testHelper.hideForSystem() testHelper.toggle(deviceId = 987) testHelper.showFromActivity() - viewModel.onUserLeave() + viewModel.onViewClosed() testHelper.hideFromActivity() testHelper.hideForSystem() testHelper.toggle(deviceId = 456) @@ -127,4 +129,27 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { val shouldShowNew by collectLastValue(viewModel.shouldShow) assertThat(shouldShowNew).isEqualTo(shouldShow) } + + @Test + fun sysUiStateFlag_disabledByDefault() = + testScope.runTest { + assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse() + } + + @Test + fun sysUiStateFlag_trueAfterViewOpened() = + testScope.runTest { + viewModel.onViewOpened() + + assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isTrue() + } + + @Test + fun sysUiStateFlag_falseAfterViewClosed() = + testScope.runTest { + viewModel.onViewOpened() + viewModel.onViewClosed() + + assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt index b50d248d6940..7afed9db8604 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt @@ -19,6 +19,7 @@ import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.utils.GlobalWindowManager @@ -74,7 +75,7 @@ class ResourceTrimmerTest : SysuiTestCase() { globalWindowManager, testScope.backgroundScope, kosmos.testDispatcher, - featureFlags + featureFlags, ) resourceTrimmer.start() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt index d0d9891a953f..8a5af09f52ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt @@ -40,7 +40,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.Before -import org.junit.Ignore import org.junit.runner.RunWith @SmallTest @@ -1149,7 +1148,6 @@ class LockscreenSceneTransitionInteractorTest : SysuiTestCase() { * * In STL there is no guarantee that transitions settle in Idle before continuing. */ - @Ignore("Suffers from a race condition that will be fixed in followup CL") @Test fun transition_from_ls_scene_interrupted_by_other_from_ls_transition() = testScope.runTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt index 9b2db3e5316c..1f132989b169 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt @@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -41,10 +42,9 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { private lateinit var executor: FakeExecutor @Mock private lateinit var activityTaskManagerService: IActivityTaskManager - @Mock private lateinit var keyguardStateController: KeyguardStateController - @Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier + @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Before fun setUp() { @@ -57,6 +57,7 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { activityTaskManagerService = activityTaskManagerService, keyguardStateController = keyguardStateController, keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator, + keyguardTransitionInteractor = keyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt index 1396b20a800d..391831a61579 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt @@ -18,15 +18,19 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.filters.SmallTest +import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository +import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.policy.keyguardStateController import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -34,6 +38,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.kotlin.whenever @ExperimentalCoroutinesApi @RunWith(JUnit4::class) @@ -49,13 +54,35 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() = testScope.runTest { mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) + val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) + givenCanShowAlternateBouncer() fingerprintPropertyRepository.supportsUdfps() transitionRepository.sendTransitionSteps( listOf( + stepToLockscreen(0f, TransitionState.STARTED), + stepToLockscreen(.4f), + stepToLockscreen(1f, TransitionState.FINISHED), + ), + testScope, + ) + assertThat(canShowAlternateBouncer).isTrue() + transitionRepository.sendTransitionSteps( + listOf( + stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED), + stepFromLockscreenToAlternateBouncer(.4f), + stepFromLockscreenToAlternateBouncer(.6f), + ), + testScope, + ) + assertThat(canShowAlternateBouncer).isTrue() + assertThat(alternateBouncerWindowRequired).isTrue() + + transitionRepository.sendTransitionSteps( + listOf( stepFromAlternateBouncer(0f, TransitionState.STARTED), - stepFromAlternateBouncer(.4f), + stepFromAlternateBouncer(.2f), stepFromAlternateBouncer(.6f), ), testScope, @@ -77,13 +104,21 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) + givenCanShowAlternateBouncer() fingerprintPropertyRepository.supportsUdfps() transitionRepository.sendTransitionSteps( listOf( - stepFromAlternateBouncer(0f, TransitionState.STARTED), - stepFromAlternateBouncer(.4f), - stepFromAlternateBouncer(.6f), - stepFromAlternateBouncer(1f), + stepToLockscreen(0f, TransitionState.STARTED), + stepToLockscreen(.4f), + stepToLockscreen(1f, TransitionState.FINISHED), + ), + testScope, + ) + transitionRepository.sendTransitionSteps( + listOf( + stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED), + stepFromLockscreenToAlternateBouncer(.4f), + stepFromLockscreenToAlternateBouncer(.6f), ), testScope, ) @@ -96,13 +131,23 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) + givenCanShowAlternateBouncer() fingerprintPropertyRepository.supportsUdfps() transitionRepository.sendTransitionSteps( listOf( + stepFromLockscreenToDozing(0f, TransitionState.STARTED), + stepFromLockscreenToDozing(.4f), + stepFromLockscreenToDozing(.6f), + stepFromLockscreenToDozing(1f, TransitionState.FINISHED), + ), + testScope, + ) + assertThat(alternateBouncerWindowRequired).isFalse() + transitionRepository.sendTransitionSteps( + listOf( stepFromDozingToLockscreen(0f, TransitionState.STARTED), stepFromDozingToLockscreen(.4f), stepFromDozingToLockscreen(.6f), - stepFromDozingToLockscreen(1f), ), testScope, ) @@ -115,19 +160,39 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) + givenCanShowAlternateBouncer() fingerprintPropertyRepository.supportsRearFps() transitionRepository.sendTransitionSteps( listOf( - stepFromAlternateBouncer(0f, TransitionState.STARTED), - stepFromAlternateBouncer(.4f), - stepFromAlternateBouncer(.6f), - stepFromAlternateBouncer(1f), + stepToLockscreen(0f, TransitionState.STARTED), + stepToLockscreen(.4f), + stepToLockscreen(1f, TransitionState.FINISHED), + ), + testScope, + ) + transitionRepository.sendTransitionSteps( + listOf( + stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED), + stepFromLockscreenToAlternateBouncer(.4f), + stepFromLockscreenToAlternateBouncer(.6f), ), testScope, ) assertThat(alternateBouncerWindowRequired).isFalse() } + private fun stepToLockscreen( + value: Float, + state: TransitionState = TransitionState.RUNNING + ): TransitionStep { + return step( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + value = value, + transitionState = state, + ) + } + private fun stepFromAlternateBouncer( value: Float, state: TransitionState = TransitionState.RUNNING @@ -140,6 +205,18 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { ) } + private fun stepFromLockscreenToAlternateBouncer( + value: Float, + state: TransitionState = TransitionState.RUNNING + ): TransitionStep { + return step( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.ALTERNATE_BOUNCER, + value = value, + transitionState = state, + ) + } + private fun stepFromDozingToLockscreen( value: Float, state: TransitionState = TransitionState.RUNNING @@ -152,6 +229,18 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { ) } + private fun stepFromLockscreenToDozing( + value: Float, + state: TransitionState = TransitionState.RUNNING + ): TransitionStep { + return step( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.DOZING, + value = value, + transitionState = state, + ) + } + private fun step( from: KeyguardState, to: KeyguardState, @@ -166,4 +255,16 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { ownerName = "AlternateBouncerViewModelTest" ) } + + /** + * Given the alternate bouncer parameters are set so that the alternate bouncer can show, aside + * from the fingerprint modality. + */ + private fun givenCanShowAlternateBouncer() { + kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(false) + kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) + kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) + whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) + whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt index 265ade3fac0f..5986f4a9a9aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt @@ -33,9 +33,6 @@ import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor @@ -50,6 +47,9 @@ import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever private const val KEY = "TEST_KEY" private const val KEY_ALT = "TEST_KEY_2" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt index 99bf2db2ff98..3372f06dec22 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt @@ -66,9 +66,6 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.After @@ -90,6 +87,9 @@ import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoSession import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.any +import org.mockito.kotlin.capture +import org.mockito.kotlin.eq import org.mockito.quality.Strictness private const val KEY = "KEY" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt index 35eefd91bc8e..caaa42fc364c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt @@ -40,9 +40,6 @@ import com.android.systemui.media.controls.util.MediaUiEventLogger import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor @@ -60,6 +57,9 @@ import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever private const val KEY = "TEST_KEY" private const val KEY_ALT = "TEST_KEY_2" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt index 5791826ab1f9..3bf4173cd7c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt @@ -71,10 +71,6 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.utils.os.FakeHandler @@ -101,6 +97,10 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoSession import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.any +import org.mockito.kotlin.capture +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever import org.mockito.quality.Strictness private const val KEY = "KEY" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt index befe64c1f411..d2701dd0d3a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt @@ -51,7 +51,6 @@ import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFacto import com.android.systemui.res.R import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.After @@ -72,6 +71,7 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.eq private const val KEY = "TEST_KEY" private const val KEY_OLD = "TEST_KEY_OLD" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt index 030bca25c518..31a243591b60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt @@ -27,7 +27,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.media.controls.MediaTestUtils import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import org.junit.After import org.junit.Before @@ -38,12 +37,13 @@ import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock -import org.mockito.Mockito.any import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.any +import org.mockito.kotlin.eq private const val PACKAGE = "PKG" private const val KEY = "TEST_KEY" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt index cdbf9d757ec1..6ca0bef17404 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt @@ -31,10 +31,6 @@ import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -52,6 +48,10 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.any +import org.mockito.kotlin.capture +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever private const val KEY = "KEY" private const val PACKAGE = "PKG" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt index 3bb8b8fcd775..05c882eae811 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt @@ -54,15 +54,13 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.PageIndicator import com.android.systemui.res.R +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.eq import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SecureSettings @@ -92,6 +90,9 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.capture +import org.mockito.kotlin.eq private val DATA = MediaTestUtils.emptyMediaData diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt index 0c9fee9e2741..6d7976e6e51d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt @@ -98,11 +98,6 @@ import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimat import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.KotlinArgumentCaptor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -125,6 +120,9 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq private const val KEY = "TEST_KEY" private const val PACKAGE = "PKG" @@ -247,8 +245,7 @@ public class MediaControlPanelTest : SysuiTestCase() { // Set up package manager mocks val icon = context.getDrawable(R.drawable.ic_android) whenever(packageManager.getApplicationIcon(anyString())).thenReturn(icon) - whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java))) - .thenReturn(icon) + whenever(packageManager.getApplicationIcon(any<ApplicationInfo>())).thenReturn(icon) whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt())) .thenReturn(applicationInfo) whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE) @@ -644,7 +641,7 @@ public class MediaControlPanelTest : SysuiTestCase() { bgExecutor.runAllReady() mainExecutor.runAllReady() - verify(albumView).setImageDrawable(any(Drawable::class.java)) + verify(albumView).setImageDrawable(any<Drawable>()) } @Test @@ -657,7 +654,7 @@ public class MediaControlPanelTest : SysuiTestCase() { bgExecutor.runAllReady() mainExecutor.runAllReady() - verify(albumView).setImageDrawable(any(Drawable::class.java)) + verify(albumView).setImageDrawable(any<Drawable>()) } @Test @@ -675,12 +672,12 @@ public class MediaControlPanelTest : SysuiTestCase() { player.bindPlayer(state0, PACKAGE) bgExecutor.runAllReady() mainExecutor.runAllReady() - verify(albumView).setImageDrawable(any(Drawable::class.java)) + verify(albumView).setImageDrawable(any<Drawable>()) // Run Metadata update so that later states don't update val captor = argumentCaptor<Animator.AnimatorListener>() verify(mockAnimator, times(2)).addListener(captor.capture()) - captor.value.onAnimationEnd(mockAnimator) + captor.lastValue.onAnimationEnd(mockAnimator) assertThat(titleText.getText()).isEqualTo(TITLE) assertThat(artistText.getText()).isEqualTo(ARTIST) @@ -696,13 +693,13 @@ public class MediaControlPanelTest : SysuiTestCase() { player.bindPlayer(state2, PACKAGE) bgExecutor.runAllReady() mainExecutor.runAllReady() - verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java)) + verify(albumView, times(2)).setImageDrawable(any<Drawable>()) // Fourth binding to new image runs transition due to color scheme change player.bindPlayer(state3, PACKAGE) bgExecutor.runAllReady() mainExecutor.runAllReady() - verify(albumView, times(3)).setImageDrawable(any(Drawable::class.java)) + verify(albumView, times(3)).setImageDrawable(any<Drawable>()) } @Test @@ -974,7 +971,7 @@ public class MediaControlPanelTest : SysuiTestCase() { val captor = argumentCaptor<SeekBarObserver>() verify(seekBarData).observeForever(captor.capture()) - val seekBarObserver = captor.value!! + val seekBarObserver = captor.lastValue // Then the seekbar is set to animate assertThat(seekBarObserver.animationEnabled).isTrue() @@ -1086,27 +1083,19 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(mockAvd0.isRunning()).thenReturn(false) val captor = ArgumentCaptor.forClass(Animatable2.AnimationCallback::class.java) verify(mockAvd0, times(1)).registerAnimationCallback(captor.capture()) - verify(mockAvd1, never()) - .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java)) - verify(mockAvd2, never()) - .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java)) + verify(mockAvd1, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>()) + verify(mockAvd2, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>()) captor.getValue().onAnimationEnd(mockAvd0) // Validate correct state was bound assertThat(actionPlayPause.contentDescription).isEqualTo("loading") assertThat(actionPlayPause.getBackground()).isNull() - verify(mockAvd0, times(1)) - .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java)) - verify(mockAvd1, times(1)) - .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java)) - verify(mockAvd2, times(1)) - .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java)) - verify(mockAvd0, times(1)) - .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java)) - verify(mockAvd1, times(1)) - .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java)) - verify(mockAvd2, never()) - .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java)) + verify(mockAvd0, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>()) + verify(mockAvd1, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>()) + verify(mockAvd2, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>()) + verify(mockAvd0, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>()) + verify(mockAvd1, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>()) + verify(mockAvd2, never()).unregisterAnimationCallback(any<Animatable2.AnimationCallback>()) } @Test @@ -1118,7 +1107,7 @@ public class MediaControlPanelTest : SysuiTestCase() { // Capture animation handler val captor = argumentCaptor<Animator.AnimatorListener>() verify(mockAnimator, times(2)).addListener(captor.capture()) - val handler = captor.value + val handler = captor.lastValue // Validate text views unchanged but animation started assertThat(titleText.getText()).isEqualTo("") @@ -1147,7 +1136,7 @@ public class MediaControlPanelTest : SysuiTestCase() { // Capture animation handler val captor = argumentCaptor<Animator.AnimatorListener>() verify(mockAnimator, times(2)).addListener(captor.capture()) - val handler = captor.value + val handler = captor.lastValue // Validate text views unchanged but animation started assertThat(titleText.getText()).isEqualTo("") @@ -1179,7 +1168,7 @@ public class MediaControlPanelTest : SysuiTestCase() { // Capture animation handler val captor = argumentCaptor<Animator.AnimatorListener>() verify(mockAnimator, times(2)).addListener(captor.capture()) - val handler = captor.value + val handler = captor.lastValue handler.onAnimationEnd(mockAnimator) assertThat(artistText.getText()).isEqualTo("ARTIST_0") @@ -1775,10 +1764,9 @@ public class MediaControlPanelTest : SysuiTestCase() { player.attachPlayer(viewHolder) player.bindPlayer(mediaData, KEY) - val callback: () -> Unit = {} - val captor = KotlinArgumentCaptor(callback::class.java) + val captor = argumentCaptor<() -> Unit>() verify(seekBarViewModel).logSeek = captor.capture() - captor.value.invoke() + captor.lastValue.invoke() verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId)) } @@ -1801,7 +1789,7 @@ public class MediaControlPanelTest : SysuiTestCase() { // THEN it sends the PendingIntent without dismissing keyguard first, // and does not use the Intent directly (see b/271845008) captor.value.onClick(viewHolder.player) - verify(pendingIntent).send(any(Bundle::class.java)) + verify(pendingIntent).send(any<Bundle>()) verify(pendingIntent, never()).getIntent() verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any()) } @@ -2219,8 +2207,8 @@ public class MediaControlPanelTest : SysuiTestCase() { mainExecutor.runAllReady() verify(recCardTitle).setTextColor(any<Int>()) - verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java)) - verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java)) + verify(recAppIconItem, times(3)).setImageDrawable(any<Drawable>()) + verify(coverItem, times(3)).setImageDrawable(any<Drawable>()) verify(coverItem, times(3)).imageMatrix = any() } @@ -2547,7 +2535,7 @@ public class MediaControlPanelTest : SysuiTestCase() { seamless.callOnClick() // Then we send the pending intent as is, without modifying the original intent - verify(pendingIntent).send(any(Bundle::class.java)) + verify(pendingIntent).send(any<Bundle>()) verify(pendingIntent, never()).getIntent() } @@ -2579,13 +2567,16 @@ public class MediaControlPanelTest : SysuiTestCase() { return Icon.createWithBitmap(bmp) } - private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener = - withArgCaptor { - verify(seekBarViewModel).setScrubbingChangeListener(capture()) - } + private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener { + val captor = argumentCaptor<SeekBarViewModel.ScrubbingChangeListener>() + verify(seekBarViewModel).setScrubbingChangeListener(captor.capture()) + return captor.lastValue + } - private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener = withArgCaptor { - verify(seekBarViewModel).setEnabledChangeListener(capture()) + private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener { + val captor = argumentCaptor<SeekBarViewModel.EnabledChangeListener>() + verify(seekBarViewModel).setEnabledChangeListener(captor.capture()) + return captor.lastValue } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt index 9f0e67b60c29..85cc88dd5283 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt @@ -15,11 +15,13 @@ */ package com.android.systemui.monet -import androidx.test.filters.SmallTest import android.testing.AndroidTestingRunner import android.util.Log +import android.util.Pair +import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.theme.DynamicColors +import com.google.ux.material.libmonet.dynamiccolor.DynamicColor import com.google.ux.material.libmonet.hct.Hct import com.google.ux.material.libmonet.scheme.SchemeTonalSpot import java.io.File @@ -81,6 +83,10 @@ private fun commentShade(paletteName: String, tone: Int): String { @SmallTest @RunWith(AndroidTestingRunner::class) class ColorSchemeTest : SysuiTestCase() { + private val defaultContrast = 0.0 + private val defaultIsDark = false + private val defaultIsFidelity = false + @Test fun generateThemeStyles() { val document = buildDoc<Any>() @@ -107,7 +113,7 @@ class ColorSchemeTest : SysuiTestCase() { } val style = document.createElement(styleValue.name.lowercase()) - val colorScheme = ColorScheme(sourceColor.toInt(), false, styleValue) + val colorScheme = ColorScheme(sourceColor.toInt(), defaultIsDark, styleValue) style.appendChild( document.createTextNode( @@ -139,7 +145,7 @@ class ColorSchemeTest : SysuiTestCase() { document.appendWithBreak(resources) // shade colors - val colorScheme = ColorScheme(GOOGLE_BLUE, false) + val colorScheme = ColorScheme(GOOGLE_BLUE, defaultIsDark) arrayOf( Triple("accent1", "Primary", colorScheme.accent1), Triple("accent2", "Secondary", colorScheme.accent2), @@ -162,24 +168,35 @@ class ColorSchemeTest : SysuiTestCase() { resources.appendWithBreak(document.createComment(commentRoles), 2) - // dynamic colors - arrayOf(false, true).forEach { isDark -> - val suffix = if (isDark) "_dark" else "_light" - val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, 0.5) - DynamicColors.allDynamicColorsMapped(false).forEach { - resources.createColorEntry( - "system_${it.first}$suffix", - it.second.getArgb(dynamicScheme) - ) + fun generateDynamic(pairs: List<Pair<String, DynamicColor>>) { + arrayOf(false, true).forEach { isDark -> + val suffix = if (isDark) "_dark" else "_light" + val dynamicScheme = + SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, defaultContrast) + pairs.forEach { + resources.createColorEntry( + "system_${it.first}$suffix", + it.second.getArgb(dynamicScheme) + ) + } } } + // dynamic colors + generateDynamic(DynamicColors.allDynamicColorsMapped(defaultIsFidelity)) + // fixed colors - val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, 0.5) - DynamicColors.getFixedColorsMapped(false).forEach { + val dynamicScheme = + SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), defaultIsDark, defaultContrast) + DynamicColors.getFixedColorsMapped(defaultIsFidelity).forEach { resources.createColorEntry("system_${it.first}", it.second.getArgb(dynamicScheme)) } + resources.appendWithBreak(document.createComment(commentRoles), 2) + + // custom colors + generateDynamic(DynamicColors.getCustomColorsMapped(defaultIsFidelity)) + saveFile(document, "role_values.xml") } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt index 5e7d8fb5df02..a10d81f86d8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt @@ -21,7 +21,6 @@ import android.content.Intent import android.os.Bundle import android.os.UserHandle import android.testing.AndroidTestingRunner -import android.view.View import android.view.Window import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -49,7 +48,7 @@ class ActionExecutorTest : SysuiTestCase() { private val intentExecutor = mock<ActionIntentExecutor>() private val window = mock<Window>() - private val view = mock<View>() + private val viewProxy = mock<ScreenshotShelfViewProxy>() private val onDismiss = mock<(() -> Unit)>() private val pendingIntent = mock<PendingIntent>() @@ -70,16 +69,16 @@ class ActionExecutorTest : SysuiTestCase() { } @Test - fun sendPendingIntent_dismisses() = runTest { + fun sendPendingIntent_requestsDismissal() = runTest { actionExecutor = createActionExecutor() actionExecutor.sendPendingIntent(pendingIntent) verify(pendingIntent).send(any(Bundle::class.java)) - verify(onDismiss).invoke() + verify(viewProxy).requestDismissal(null) } private fun createActionExecutor(): ActionExecutor { - return ActionExecutor(intentExecutor, testScope, window, view, onDismiss) + return ActionExecutor(intentExecutor, testScope, window, viewProxy, onDismiss) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 766113f09308..8e3290748039 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -547,6 +547,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class)); // Dreaming->Lockscreen + when(mKeyguardTransitionInteractor.transition(any())) + .thenReturn(emptyFlow()); when(mKeyguardTransitionInteractor.transition(any(), any())) .thenReturn(emptyFlow()); when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha()) 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 45d0102f2dd1..4a867a8ecf22 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -46,6 +46,7 @@ import com.android.systemui.flags.andSceneContainer import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionStep @@ -173,7 +174,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : .thenReturn(keyguardBouncerComponent) whenever(keyguardBouncerComponent.securityContainerController) .thenReturn(keyguardSecurityContainerController) - whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING)) + whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING))) .thenReturn(emptyFlow<TransitionStep>()) featureFlagsClassic = FakeFeatureFlagsClassic() @@ -518,46 +519,6 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : } @Test - fun handleExternalTouch_intercepted_sendsOnTouch() { - // Accept dispatch and also intercept. - whenever(view.dispatchTouchEvent(any())).thenReturn(true) - whenever(view.onInterceptTouchEvent(any())).thenReturn(true) - - underTest.handleExternalTouch(DOWN_EVENT) - underTest.handleExternalTouch(MOVE_EVENT) - - // Once intercepted, both events are sent to the view. - verify(view).onTouchEvent(DOWN_EVENT) - verify(view).onTouchEvent(MOVE_EVENT) - } - - @Test - fun handleExternalTouch_notDispatched_interceptNotCalled() { - // Don't accept dispatch - whenever(view.dispatchTouchEvent(any())).thenReturn(false) - - underTest.handleExternalTouch(DOWN_EVENT) - - // Interception is not offered. - verify(view, never()).onInterceptTouchEvent(any()) - } - - @Test - fun handleExternalTouch_notIntercepted_onTouchNotSent() { - // Accept dispatch, but don't dispatch - whenever(view.dispatchTouchEvent(any())).thenReturn(true) - whenever(view.onInterceptTouchEvent(any())).thenReturn(false) - - underTest.handleExternalTouch(DOWN_EVENT) - underTest.handleExternalTouch(MOVE_EVENT) - - // Interception offered for both events, but onTouchEvent is never called. - verify(view).onInterceptTouchEvent(DOWN_EVENT) - verify(view).onInterceptTouchEvent(MOVE_EVENT) - verify(view, never()).onTouchEvent(any()) - } - - @Test fun testGetKeyguardMessageArea() = testScope.runTest { underTest.keyguardMessageArea diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index f380b6c700cd..e83a46bb56a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -36,6 +36,7 @@ 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.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.res.R @@ -151,7 +152,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { whenever(statusBarStateController.isDozing).thenReturn(false) mDependency.injectTestDependency(ShadeController::class.java, shadeController) whenever(dockManager.isDocked).thenReturn(false) - whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING)) + whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING))) .thenReturn(emptyFlow()) val featureFlags = FakeFeatureFlags() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt index 2c453a711c87..dfd7a715fcdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt @@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test @@ -36,6 +37,8 @@ class QuickSettingsControllerImplWithCoroutinesTest : QuickSettingsControllerImp runCurrent() assertThat(mQsController.isExpansionEnabled).isFalse() + + coroutineContext.cancelChildren() } @Test @@ -45,6 +48,8 @@ class QuickSettingsControllerImplWithCoroutinesTest : QuickSettingsControllerImp runCurrent() assertThat(mQsController.isExpansionEnabled).isTrue() + + coroutineContext.cancelChildren() } @Test @@ -58,6 +63,8 @@ class QuickSettingsControllerImplWithCoroutinesTest : QuickSettingsControllerImp runCurrent() assertThat(mQsController.isExpansionEnabled).isFalse() + + coroutineContext.cancelChildren() } @Test @@ -71,6 +78,8 @@ class QuickSettingsControllerImplWithCoroutinesTest : QuickSettingsControllerImp runCurrent() assertThat(mQsController.isExpansionEnabled).isFalse() + + coroutineContext.cancelChildren() } @Test @@ -81,5 +90,7 @@ class QuickSettingsControllerImplWithCoroutinesTest : QuickSettingsControllerImp runCurrent() assertThat(mQsController.isExpansionEnabled).isTrue() + + coroutineContext.cancelChildren() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index dfee7374c104..5b2526e22679 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -71,7 +71,6 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository; import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor; @@ -144,8 +143,6 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { @Mock private SecureSettings mSecureSettings; @Mock private CommandQueue mCommandQueue; @Mock private KeyguardLogger mLogger; - - @Mock private NotificationMediaManager mNotificationMediaManager; @Mock private StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory; private TestShadeViewStateProvider mShadeViewStateProvider; @@ -225,7 +222,6 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mFakeExecutor, mBackgroundExecutor, mLogger, - mNotificationMediaManager, mStatusOverlayHoverListenerFactory ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index ed7c9568a9db..a5e7a67b21a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -995,9 +995,11 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { .setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null)); // All dynamic colors were added twice: light and dark them // All fixed colors were added once + // All custom dynamic tokens added twice verify(dynamic, times( DynamicColors.allDynamicColorsMapped(false).size() * 2 - + DynamicColors.getFixedColorsMapped(false).size()) + + DynamicColors.getFixedColorsMapped(false).size() + + DynamicColors.getCustomColorsMapped(false).size() * 2) ).setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt index 948670f95f97..01795e92d141 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.user.domain.interactor import android.app.admin.DevicePolicyManager +import android.content.Context import android.content.pm.UserInfo import android.os.UserHandle import android.os.UserManager @@ -59,6 +60,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Mock private lateinit var switchUser: (Int) -> Unit @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver + @Mock private lateinit var otherContext: Context private lateinit var underTest: GuestUserInteractor @@ -74,28 +76,30 @@ class GuestUserInteractorTest : SysuiTestCase() { repository = FakeUserRepository() repository.setUserInfos(ALL_USERS) - underTest = - GuestUserInteractor( - applicationContext = context, - applicationScope = scope, - mainDispatcher = IMMEDIATE, - backgroundDispatcher = IMMEDIATE, - manager = manager, - repository = repository, - deviceProvisionedController = deviceProvisionedController, - devicePolicyManager = devicePolicyManager, - refreshUsersScheduler = - RefreshUsersScheduler( - applicationScope = scope, - mainDispatcher = IMMEDIATE, - repository = repository, - ), - uiEventLogger = uiEventLogger, - resumeSessionReceiver = resumeSessionReceiver, - resetOrExitSessionReceiver = resetOrExitSessionReceiver, - ) + underTest = initGuestUserInteractor(context) } + private fun initGuestUserInteractor(context: Context) = + GuestUserInteractor( + applicationContext = context, + applicationScope = scope, + mainDispatcher = IMMEDIATE, + backgroundDispatcher = IMMEDIATE, + manager = manager, + repository = repository, + deviceProvisionedController = deviceProvisionedController, + devicePolicyManager = devicePolicyManager, + refreshUsersScheduler = + RefreshUsersScheduler( + applicationScope = scope, + mainDispatcher = IMMEDIATE, + repository = repository, + ), + uiEventLogger = uiEventLogger, + resumeSessionReceiver = resumeSessionReceiver, + resetOrExitSessionReceiver = resetOrExitSessionReceiver, + ) + @Test fun registersBroadcastReceivers() { verify(resumeSessionReceiver).register() @@ -103,6 +107,16 @@ class GuestUserInteractorTest : SysuiTestCase() { } @Test + fun registersBroadcastReceiversOnlyForSystemUser() { + for (i in 1..5) { + whenever(otherContext.userId).thenReturn(UserHandle.MIN_SECONDARY_USER_ID + i) + initGuestUserInteractor(otherContext) + } + verify(resumeSessionReceiver).register() + verify(resetOrExitSessionReceiver).register() + } + + @Test fun onDeviceBootCompleted_allowedToAdd_createGuest() = runBlocking(IMMEDIATE) { setAllowedToAdd() diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt index 31848a67698c..e1dcb145f20b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt @@ -1,8 +1,8 @@ package com.android.systemui.util import android.graphics.Rect -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.wm.shell.common.FloatingContentCoordinator @@ -14,7 +14,7 @@ import org.junit.Test import org.junit.runner.RunWith @TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class FloatingContentCoordinatorTest : SysuiTestCase() { @@ -198,12 +198,11 @@ class FloatingContentCoordinatorTest : SysuiTestCase() { } /** - * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a - * Rect when needed. + * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a Rect when + * needed. */ - inner class FloatingRect( - private val underlyingRect: Rect - ) : FloatingContentCoordinator.FloatingContent { + inner class FloatingRect(private val underlyingRect: Rect) : + FloatingContentCoordinator.FloatingContent { override fun moveToBounds(bounds: Rect) { underlyingRect.set(bounds) } @@ -216,4 +215,4 @@ class FloatingContentCoordinatorTest : SysuiTestCase() { return underlyingRect } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt index 436f5b827ec6..457f2bb5d826 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt @@ -19,12 +19,13 @@ package com.android.systemui.util import android.content.BroadcastReceiver import android.content.IntentFilter import android.os.UserHandle -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.lifecycle.Observer +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher +import java.util.concurrent.Executor import org.junit.After import org.junit.Assert.assertTrue import org.junit.Before @@ -38,10 +39,9 @@ import org.mockito.Mockito.anyInt import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations -import java.util.concurrent.Executor @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class RingerModeLiveDataTest : SysuiTestCase() { @@ -52,16 +52,11 @@ class RingerModeLiveDataTest : SysuiTestCase() { private val INTENT = "INTENT" } - @Mock - private lateinit var broadcastDispatcher: BroadcastDispatcher - @Mock - private lateinit var valueSupplier: () -> Int - @Mock - private lateinit var observer: Observer<Int> - @Captor - private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver> - @Captor - private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter> + @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock private lateinit var valueSupplier: () -> Int + @Mock private lateinit var observer: Observer<Int> + @Captor private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver> + @Captor private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter> // Run everything immediately private val executor = Executor { it.run() } @@ -88,14 +83,14 @@ class RingerModeLiveDataTest : SysuiTestCase() { fun testOnActive_broadcastRegistered() { liveData.observeForever(observer) verify(broadcastDispatcher) - .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any()) + .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any()) } @Test fun testOnActive_intentFilterHasIntent() { liveData.observeForever(observer) - verify(broadcastDispatcher).registerReceiver(any(), capture(intentFilterCaptor), any(), - any(), anyInt(), any()) + verify(broadcastDispatcher) + .registerReceiver(any(), capture(intentFilterCaptor), any(), any(), anyInt(), any()) assertTrue(intentFilterCaptor.value.hasAction(INTENT)) } @@ -111,4 +106,4 @@ class RingerModeLiveDataTest : SysuiTestCase() { liveData.removeObserver(observer) verify(broadcastDispatcher).unregisterReceiver(any()) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt index b13cb72dc944..6271904b2f04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.util import android.app.WallpaperInfo import android.app.WallpaperManager import android.os.IBinder -import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.View import android.view.ViewRootImpl +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.eq @@ -32,36 +32,30 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.anyFloat import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.doThrow -import org.mockito.Mockito.times -import org.mockito.Mockito.verify import org.mockito.Mockito.mock import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever +import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @RunWithLooper @SmallTest class WallpaperControllerTest : SysuiTestCase() { - @Mock - private lateinit var wallpaperManager: WallpaperManager - @Mock - private lateinit var root: View - @Mock - private lateinit var viewRootImpl: ViewRootImpl - @Mock - private lateinit var windowToken: IBinder + @Mock private lateinit var wallpaperManager: WallpaperManager + @Mock private lateinit var root: View + @Mock private lateinit var viewRootImpl: ViewRootImpl + @Mock private lateinit var windowToken: IBinder private val wallpaperRepository = FakeWallpaperRepository() - @JvmField - @Rule - val mockitoRule = MockitoJUnit.rule() + @JvmField @Rule val mockitoRule = MockitoJUnit.rule() private lateinit var wallaperController: WallpaperController @@ -92,9 +86,7 @@ class WallpaperControllerTest : SysuiTestCase() { @Test fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() { - wallpaperRepository.wallpaperInfo.value = createWallpaperInfo( - useDefaultTransition = false - ) + wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(useDefaultTransition = false) wallaperController.setUnfoldTransitionZoom(0.5f) @@ -130,7 +122,8 @@ class WallpaperControllerTest : SysuiTestCase() { @Test fun setNotificationZoom_exceptionWhenUpdatingZoom_doesNotFail() { - doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager) + doThrow(IllegalArgumentException("test exception")) + .`when`(wallpaperManager) .setWallpaperZoomOut(any(), anyFloat()) wallaperController.setNotificationShadeZoom(0.5f) @@ -140,8 +133,7 @@ class WallpaperControllerTest : SysuiTestCase() { private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo { val info = mock(WallpaperInfo::class.java) - whenever(info.shouldUseDefaultUnfoldTransition()) - .thenReturn(useDefaultTransition) + whenever(info.shouldUseDefaultUnfoldTransition()).thenReturn(useDefaultTransition) return info } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt index 92afb038b321..b26598c80478 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt @@ -16,13 +16,16 @@ package com.android.systemui.util.animation +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat -import org.junit.Test import java.lang.IllegalArgumentException +import org.junit.runner.RunWith +import org.junit.Test @SmallTest +@RunWith(AndroidJUnit4::class) class AnimationUtilTest : SysuiTestCase() { @Test fun getMsForFrames_5frames_returns83() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java index 9dfa14dd0784..7dfac0ab2650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java @@ -22,8 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.testing.AndroidTestingRunner; - +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -39,7 +38,7 @@ import java.util.ArrayList; import java.util.List; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class FakeExecutorTest extends SysuiTestCase { @Before public void setUp() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java index 78fc6803ea7e..48fb74514b01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java @@ -24,8 +24,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; - +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -39,7 +38,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class MessageRouterImplTest extends SysuiTestCase { private static final int MESSAGE_A = 0; private static final int MESSAGE_B = 1; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt index 15032dc182d6..7ec420f0b6da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt @@ -15,7 +15,7 @@ */ package com.android.systemui.util.concurrency -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.time.FakeSystemClock @@ -26,7 +26,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class MockExecutorHandlerTest : SysuiTestCase() { /** Test FakeExecutor that receives non-delayed items to execute. */ @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java index 00f37ae6f6cb..13fff29d89ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java @@ -18,8 +18,7 @@ package com.android.systemui.util.concurrency; import static com.google.common.truth.Truth.assertThat; -import android.testing.AndroidTestingRunner; - +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -30,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class RepeatableExecutorTest extends SysuiTestCase { private static final int DELAY = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java index b367a603ec67..37015e30781e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java @@ -23,8 +23,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.testing.AndroidTestingRunner; - +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.CoreStartable; @@ -44,7 +43,7 @@ import java.util.HashSet; import java.util.Set; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ConditionalCoreStartableTest extends SysuiTestCase { public static class FakeConditionalCoreStartable extends ConditionalCoreStartable { interface Callback { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt index ac357ea34be0..b8f581574848 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt @@ -4,7 +4,7 @@ import android.content.res.Resources import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ShapeDrawable -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -12,7 +12,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @SmallTest class DrawableSizeTest : SysuiTestCase() { @@ -32,14 +32,11 @@ class DrawableSizeTest : SysuiTestCase() { @Test fun testDownscaleToSize_drawableSmallerThanRequirement_unchanged() { - val drawable = BitmapDrawable(resources, - Bitmap.createBitmap( - resources.displayMetrics, - 150, - 150, - Bitmap.Config.ARGB_8888 - ) - ) + val drawable = + BitmapDrawable( + resources, + Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888) + ) val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300) assertThat(result).isSameInstanceAs(drawable) } @@ -48,14 +45,11 @@ class DrawableSizeTest : SysuiTestCase() { fun testDownscaleToSize_drawableLargerThanRequirementWithDensity_resized() { // This bitmap would actually fail to resize if the method doesn't check for // bitmap dimensions inside drawable. - val drawable = BitmapDrawable(resources, - Bitmap.createBitmap( - resources.displayMetrics, - 150, - 75, - Bitmap.Config.ARGB_8888 - ) - ) + val drawable = + BitmapDrawable( + resources, + Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888) + ) val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75) assertThat(result).isNotSameInstanceAs(drawable) @@ -65,9 +59,9 @@ class DrawableSizeTest : SysuiTestCase() { @Test fun testDownscaleToSize_drawableAnimated_unchanged() { - val drawable = resources.getDrawable(android.R.drawable.stat_sys_download, - resources.newTheme()) + val drawable = + resources.getDrawable(android.R.drawable.stat_sys_download, resources.newTheme()) val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) assertThat(result).isSameInstanceAs(drawable) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt index 7d0d57b4037a..e2ce50ccb6da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt @@ -16,7 +16,7 @@ package com.android.systemui.util.kotlin -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.time.FakeSystemClock @@ -47,7 +47,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class PairwiseFlowTest : SysuiTestCase() { @Test fun simple() = runBlocking { @@ -89,7 +89,9 @@ class PairwiseFlowTest : SysuiTestCase() { initRun = true "initial" } - ) { prev: String, next: String -> "$prev|$next" } + ) { prev: String, next: String -> + "$prev|$next" + } ) .emitsExactly("initial|val1", "val1|val2") assertThat(initRun).isTrue() @@ -104,7 +106,9 @@ class PairwiseFlowTest : SysuiTestCase() { initRun = true "initial" } - ) { prev: String, next: String -> "$prev|$next" } + ) { prev: String, next: String -> + "$prev|$next" + } ) .emitsNothing() // Even though the flow will not emit anything, the initial value function should still get @@ -120,7 +124,9 @@ class PairwiseFlowTest : SysuiTestCase() { initRun = true "initial" } - ) { prev: String, next: String -> "$prev|$next" } + ) { prev: String, next: String -> + "$prev|$next" + } // Since the flow isn't collected, ensure [initialValueFun] isn't run. assertThat(initRun).isFalse() @@ -146,7 +152,7 @@ class PairwiseFlowTest : SysuiTestCase() { } @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class SetChangesFlowTest : SysuiTestCase() { @Test fun simple() = runBlocking { @@ -198,7 +204,7 @@ class SetChangesFlowTest : SysuiTestCase() { } @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class SampleFlowTest : SysuiTestCase() { @Test fun simple() = runBlocking { @@ -240,7 +246,7 @@ class SampleFlowTest : SysuiTestCase() { @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class ThrottleFlowTest : SysuiTestCase() { @Test @@ -248,13 +254,16 @@ class ThrottleFlowTest : SysuiTestCase() { // Arrange val choreographer = createChoreographer(this) val output = mutableListOf<Int>() - val collectJob = backgroundScope.launch { - flow { - emit(1) - delay(1000) - emit(2) - }.throttle(1000, choreographer.fakeClock).toList(output) - } + val collectJob = + backgroundScope.launch { + flow { + emit(1) + delay(1000) + emit(2) + } + .throttle(1000, choreographer.fakeClock) + .toList(output) + } // Act choreographer.advanceAndRun(0) @@ -283,13 +292,16 @@ class ThrottleFlowTest : SysuiTestCase() { // Arrange val choreographer = createChoreographer(this) val output = mutableListOf<Int>() - val collectJob = backgroundScope.launch { - flow { - emit(1) - delay(500) - emit(2) - }.throttle(1000, choreographer.fakeClock).toList(output) - } + val collectJob = + backgroundScope.launch { + flow { + emit(1) + delay(500) + emit(2) + } + .throttle(1000, choreographer.fakeClock) + .toList(output) + } // Act choreographer.advanceAndRun(0) @@ -319,15 +331,18 @@ class ThrottleFlowTest : SysuiTestCase() { // Arrange val choreographer = createChoreographer(this) val output = mutableListOf<Int>() - val collectJob = backgroundScope.launch { - flow { - emit(1) - delay(500) - emit(2) - delay(500) - emit(3) - }.throttle(1000, choreographer.fakeClock).toList(output) - } + val collectJob = + backgroundScope.launch { + flow { + emit(1) + delay(500) + emit(2) + delay(500) + emit(3) + } + .throttle(1000, choreographer.fakeClock) + .toList(output) + } // Act choreographer.advanceAndRun(0) @@ -357,15 +372,18 @@ class ThrottleFlowTest : SysuiTestCase() { // Arrange val choreographer = createChoreographer(this) val output = mutableListOf<Int>() - val collectJob = backgroundScope.launch { - flow { - emit(1) - delay(500) - emit(2) - delay(250) - emit(3) - }.throttle(1000, choreographer.fakeClock).toList(output) - } + val collectJob = + backgroundScope.launch { + flow { + emit(1) + delay(500) + emit(2) + delay(250) + emit(3) + } + .throttle(1000, choreographer.fakeClock) + .toList(output) + } // Act choreographer.advanceAndRun(0) @@ -391,15 +409,16 @@ class ThrottleFlowTest : SysuiTestCase() { collectJob.cancel() } - private fun createChoreographer(testScope: TestScope) = object { - val fakeClock = FakeSystemClock() + private fun createChoreographer(testScope: TestScope) = + object { + val fakeClock = FakeSystemClock() - fun advanceAndRun(millis: Long) { - fakeClock.advanceTime(millis) - testScope.advanceTimeBy(millis) - testScope.runCurrent() + fun advanceAndRun(millis: Long) { + fakeClock.advanceTime(millis) + testScope.advanceTimeBy(millis) + testScope.runCurrent() + } } - } } private fun <T> assertThatFlow(flow: Flow<T>) = diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt index 4ca1fd39682d..c31b287fddce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.util.kotlin -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import java.util.concurrent.atomic.AtomicLong @@ -31,43 +31,42 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class IpcSerializerTest : SysuiTestCase() { private val serializer = IpcSerializer() @Ignore("b/253046405") @Test - fun serializeManyIncomingIpcs(): Unit = runBlocking(Dispatchers.Main.immediate) { - val processor = launch(start = CoroutineStart.LAZY) { serializer.process() } - withContext(Dispatchers.IO) { - val lastEvaluatedTime = AtomicLong(System.currentTimeMillis()) - // First, launch many serialization requests in parallel - repeat(100_000) { - launch(Dispatchers.Unconfined) { - val enqueuedTime = System.currentTimeMillis() - serializer.runSerialized { - val last = lastEvaluatedTime.getAndSet(enqueuedTime) - assertTrue( - "expected $last less than or equal to $enqueuedTime ", - last <= enqueuedTime, - ) + fun serializeManyIncomingIpcs(): Unit = + runBlocking(Dispatchers.Main.immediate) { + val processor = launch(start = CoroutineStart.LAZY) { serializer.process() } + withContext(Dispatchers.IO) { + val lastEvaluatedTime = AtomicLong(System.currentTimeMillis()) + // First, launch many serialization requests in parallel + repeat(100_000) { + launch(Dispatchers.Unconfined) { + val enqueuedTime = System.currentTimeMillis() + serializer.runSerialized { + val last = lastEvaluatedTime.getAndSet(enqueuedTime) + assertTrue( + "expected $last less than or equal to $enqueuedTime ", + last <= enqueuedTime, + ) + } } } + // Then, process them all in the order they came in. + processor.start() } - // Then, process them all in the order they came in. - processor.start() + // All done, stop processing + processor.cancel() } - // All done, stop processing - processor.cancel() - } @Test(timeout = 5000) fun serializeOnOneThread_doesNotDeadlock() = runBlocking { val job = launch { serializer.process() } - repeat(100) { - serializer.runSerializedBlocking { } - } + repeat(100) { serializer.runSerializedBlocking {} } job.cancel() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt index 2013bb0a547e..8bfff9c209e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt @@ -27,13 +27,13 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters import org.mockito.Mock import org.mockito.MockitoAnnotations +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters @SmallTest -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) internal class PackageManagerExtComponentEnabledTest(private val testCase: TestCase) : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt index 6848b836a348..b2f7c1aa0384 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt @@ -16,7 +16,7 @@ package com.android.systemui.util.kotlin -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -28,7 +28,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class RaceSuspendTest : SysuiTestCase() { @Test fun raceSimple() = runBlocking { @@ -46,10 +46,11 @@ class RaceSuspendTest : SysuiTestCase() { @Test fun raceImmediate() = runBlocking { assertThat( - race<Int>( - { 1 }, - { 2 }, + race<Int>( + { 1 }, + { 2 }, + ) ) - ).isEqualTo(1) + .isEqualTo(1) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java index 84129beea92a..300c29852ead 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java @@ -26,9 +26,9 @@ import static org.mockito.Mockito.verify; import android.content.res.Resources; import android.hardware.Sensor; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -46,7 +46,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class PostureDependentProximitySensorTest extends SysuiTestCase { @Mock private Resources mResources; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java index 19dbf9aa3c13..5dd008ac10f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java @@ -23,9 +23,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -40,7 +40,7 @@ import org.junit.runner.RunWith; import java.util.function.Consumer; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class ProximityCheckTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java index 5e7557896145..0eab74eb3cfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java @@ -24,9 +24,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -40,7 +40,7 @@ import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class ProximitySensorImplDualTest extends SysuiTestCase { private ProximitySensor mProximitySensor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java index 752cd3211161..f44c842ad2eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java @@ -21,9 +21,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -40,7 +40,7 @@ import org.mockito.MockitoAnnotations; * Tests for ProximitySensor that rely on a single hardware sensor. */ @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class ProximitySensorImplSingleTest extends SysuiTestCase { private ProximitySensor mProximitySensor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java index 8d26c877f4cf..a54afad617bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java @@ -30,8 +30,8 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.os.IBinder; import android.os.UserHandle; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -49,7 +49,7 @@ import java.util.List; import java.util.Objects; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class ObservableServiceConnectionTest extends SysuiTestCase { static class Foo { int mValue; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java index a2fd288ef33e..a70b00c8972d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java @@ -24,8 +24,8 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -38,7 +38,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class PackageObserverTest extends SysuiTestCase { @Mock Context mContext; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java index 55c49ee4360d..ef10fdf63741 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java @@ -19,8 +19,7 @@ package com.android.systemui.util.service; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.testing.AndroidTestingRunner; - +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -37,7 +36,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class PersistentConnectionManagerTest extends SysuiTestCase { private static final int MAX_RETRIES = 5; private static final int RETRY_DELAY_MS = 1000; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java index f65caee24e34..99f6303ad73d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java @@ -28,8 +28,8 @@ import static org.mockito.Mockito.verify; import android.database.ContentObserver; import android.os.UserHandle; import android.provider.Settings; -import android.testing.AndroidTestingRunner; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -44,7 +44,7 @@ import java.util.Collection; import java.util.Map; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) public class FakeSettingsTest extends SysuiTestCase { @Mock ContentObserver mContentObserver; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt index 913759f77013..88b2630bd78f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.util.settings.repository import android.content.pm.UserInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -33,11 +34,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@RunWith(AndroidJUnit4::class) class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() { private val dispatcher = StandardTestDispatcher() @@ -48,11 +48,12 @@ class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() { @Before fun setup() { - repository = UserAwareSecureSettingsRepositoryImpl( - secureSettings, - userRepository, - dispatcher, - ) + repository = + UserAwareSecureSettingsRepositoryImpl( + secureSettings, + userRepository, + dispatcher, + ) userRepository.setUserInfos(USER_INFOS) setSettingValueForUser(enabled = true, userInfo = SETTING_ENABLED_USER) setSettingValueForUser(enabled = false, userInfo = SETTING_DISABLED_USER) @@ -105,4 +106,4 @@ class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() { val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0) val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt index 94100fe7f4c4..6637d5f8de92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.util.ui -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -30,7 +30,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) class AnimatedValueTest : SysuiTestCase() { @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt index e3cd9b2d6eaf..3dcb82811408 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt @@ -19,17 +19,20 @@ package com.android.systemui.util.view import android.graphics.Rect import android.view.View import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mockito.Mockito.doAnswer import org.mockito.Mockito.spy import org.mockito.Mockito.`when` @SmallTest +@RunWith(AndroidJUnit4::class) class ViewUtilTest : SysuiTestCase() { private val viewUtil = ViewUtil() private lateinit var view: View @@ -45,11 +48,13 @@ class ViewUtilTest : SysuiTestCase() { location[1] = VIEW_TOP `when`(view.locationOnScreen).thenReturn(location) doAnswer { invocation -> - val pos = invocation.arguments[0] as IntArray - pos[0] = VIEW_LEFT - pos[1] = VIEW_TOP - null - }.`when`(view).getLocationInWindow(any()) + val pos = invocation.arguments[0] as IntArray + pos[0] = VIEW_LEFT + pos[1] = VIEW_TOP + null + } + .`when`(view) + .getLocationInWindow(any()) } @Test @@ -59,9 +64,8 @@ class ViewUtilTest : SysuiTestCase() { @Test fun touchIsWithinView_onTopLeftCorner_returnsTrue() { - assertThat(viewUtil.touchIsWithinView( - view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat()) - ).isTrue() + assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())) + .isTrue() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java index ed07ad2a0976..207c35da1892 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java @@ -35,15 +35,18 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; import java.util.List; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + + @SmallTest -@RunWith(Parameterized.class) +@RunWith(ParameterizedAndroidJunit4.class) public class WakeLockTest extends SysuiTestCase { - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") public static List<FlagsParameterization> getFlags() { return FlagsParameterization.allCombinationsOf( Flags.FLAG_DELAYED_WAKELOCK_RELEASE_ON_BACKGROUND_THREAD); @@ -114,4 +117,4 @@ public class WakeLockTest extends SysuiTestCase { // shouldn't throw an exception on production builds mWakeLock.release(WHY); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index 38f2a56d317f..3401cc4be231 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -27,6 +27,9 @@ import com.android.systemui.keyguard.data.repository.fakeCommandQueue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.model.sysUiState +import com.android.systemui.settings.displayTracker val Kosmos.shortcutHelperRepository by Kosmos.Fixture { ShortcutHelperRepository(fakeCommandQueue, broadcastDispatcher) } @@ -42,7 +45,9 @@ val Kosmos.shortcutHelperTestHelper by } val Kosmos.shortcutHelperInteractor by - Kosmos.Fixture { ShortcutHelperInteractor(shortcutHelperRepository) } + Kosmos.Fixture { + ShortcutHelperInteractor(displayTracker, testScope, sysUiState, shortcutHelperRepository) + } val Kosmos.shortcutHelperViewModel by Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt index 6cc1e8eba73d..c90642d93939 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt @@ -20,6 +20,7 @@ import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.domain.interactor.sceneInteractor val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by Kosmos.Fixture { @@ -32,5 +33,6 @@ val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by fromAodTransitionInteractor = { fromAodTransitionInteractor }, fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor }, fromDozingTransitionInteractor = { fromDozingTransitionInteractor }, + sceneInteractor = { sceneInteractor } ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt index 460913f75eb4..b8fcec648393 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture @@ -27,7 +26,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToLockscreenTransitionViewModel by Fixture { AodToLockscreenTransitionViewModel( - deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, shadeInteractor = shadeInteractor, animationFlow = keyguardTransitionAnimationFlow, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java index 2bd584eac5e3..562ac0c15a0b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java @@ -27,6 +27,8 @@ import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; +import androidx.annotation.NonNull; + import com.android.internal.logging.InstanceId; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; @@ -36,6 +38,7 @@ import com.android.systemui.util.time.FakeSystemClock; import kotlin.Unit; import java.util.ArrayList; +import java.util.function.Consumer; /** * Combined builder for constructing a NotificationEntry and its associated StatusBarNotification @@ -73,6 +76,20 @@ public class NotificationEntryBuilder { mCreationTime = source.getCreationTime(); } + /** Allows the caller to sub-build the ranking */ + @NonNull + public NotificationEntryBuilder updateRanking(@NonNull Consumer<RankingBuilder> rankingUpdater) { + rankingUpdater.accept(mRankingBuilder); + return this; + } + + /** Allows the caller to sub-build the SBN */ + @NonNull + public NotificationEntryBuilder updateSbn(@NonNull Consumer<SbnBuilder> sbnUpdater) { + sbnUpdater.accept(mSbnBuilder); + return this; + } + /** Update an the parent on an existing entry */ public static void setNewParent(NotificationEntry entry, GroupEntry parent) { entry.setParent(parent); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt index 59bbff171725..1b58582a806f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt @@ -18,8 +18,6 @@ package com.android.systemui.volume import android.content.packageManager import android.content.pm.ApplicationInfo -import android.os.Handler -import android.os.looper import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.media.mediaOutputDialogManager @@ -32,6 +30,7 @@ import com.android.systemui.volume.panel.component.mediaoutput.data.repository.F import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaControllerInteractor val Kosmos.localMediaRepository by Kosmos.Fixture { FakeLocalMediaRepository() } val Kosmos.localMediaRepositoryFactory by @@ -53,7 +52,7 @@ val Kosmos.mediaOutputInteractor by testScope.backgroundScope, testScope.testScheduler, mediaControllerRepository, - Handler(looper), + mediaControllerInteractor, ) } @@ -61,7 +60,7 @@ val Kosmos.mediaDeviceSessionInteractor by Kosmos.Fixture { MediaDeviceSessionInteractor( testScope.testScheduler, - Handler(looper), + mediaControllerInteractor, mediaControllerRepository, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt new file mode 100644 index 000000000000..f03ec01bd36f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor + +import android.media.session.MediaController +import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow + +class FakeMediaControllerInteractor : MediaControllerInteractor { + + private val stateChanges = MutableSharedFlow<MediaControllerChangeModel>(replay = 1) + + override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> = + stateChanges + + fun updateState(change: MediaControllerChangeModel) { + stateChanges.tryEmit(change) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt new file mode 100644 index 000000000000..652b3ea984e7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor + +import android.os.Handler +import android.os.looper +import com.android.systemui.kosmos.Kosmos + +var Kosmos.mediaControllerInteractor: MediaControllerInteractor by + Kosmos.Fixture { MediaControllerInteractorImpl(Handler(looper)) } diff --git a/proto/src/am_capabilities.proto b/proto/src/am_capabilities.proto index fc9f7a4590bd..c2b3ac2aaa78 100644 --- a/proto/src/am_capabilities.proto +++ b/proto/src/am_capabilities.proto @@ -15,8 +15,16 @@ message FrameworkCapability { string name = 1; } +message VMInfo { + // The value of the "java.vm.name" system property + string name = 1; + // The value of the "java.vm.version" system property + string version = 2; +} + message Capabilities { repeated Capability values = 1; repeated VMCapability vm_capabilities = 2; repeated FrameworkCapability framework_capabilities = 3; + VMInfo vm_info = 4; } diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index bc608c5ac0c0..95cbb6b2130a 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -221,7 +221,9 @@ sh_test_host { data: [ ":framework-minus-apex.ravenwood.stats", ":framework-minus-apex.ravenwood.apis", + ":framework-minus-apex.ravenwood.keep_all", ":services.core.ravenwood.stats", ":services.core.ravenwood.apis", + ":services.core.ravenwood.keep_all", ], } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java new file mode 100644 index 000000000000..4992c4bcc77a --- /dev/null +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java @@ -0,0 +1,753 @@ +/* + * 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.platform.test.ravenwood; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.UserHandle; +import android.view.Display; +import android.view.DisplayAdjustments; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * A subclass of Context with all the abstract methods replaced with concrete methods. + * + * <p>In order to make sure it implements all the abstract methods, we intentionally keep it + * non-abstract. + */ +public class RavenwoodBaseContext extends Context { + RavenwoodBaseContext() { + // Only usable by ravenwood. + } + + private static RuntimeException notSupported() { + return new RuntimeException("This Context API is not yet supported under" + + " the Ravenwood deviceless testing environment. Contact g/ravenwood"); + } + + @Override + public AssetManager getAssets() { + throw notSupported(); + } + + @Override + public Resources getResources() { + throw notSupported(); + } + + @Override + public PackageManager getPackageManager() { + throw notSupported(); + } + + @Override + public ContentResolver getContentResolver() { + throw notSupported(); + } + + @Override + public Looper getMainLooper() { + throw notSupported(); + } + + @Override + public Context getApplicationContext() { + throw notSupported(); + } + + @Override + public void setTheme(int resid) { + throw notSupported(); + } + + @Override + public Theme getTheme() { + throw notSupported(); + } + + @Override + public ClassLoader getClassLoader() { + throw notSupported(); + } + + @Override + public String getPackageName() { + throw notSupported(); + } + + @Override + public String getBasePackageName() { + throw notSupported(); + } + + @Override + public ApplicationInfo getApplicationInfo() { + throw notSupported(); + } + + @Override + public String getPackageResourcePath() { + throw notSupported(); + } + + @Override + public String getPackageCodePath() { + throw notSupported(); + } + + @Override + public SharedPreferences getSharedPreferences(String name, int mode) { + throw notSupported(); + } + + @Override + public SharedPreferences getSharedPreferences(File file, int mode) { + throw notSupported(); + } + + @Override + public boolean moveSharedPreferencesFrom(Context sourceContext, String name) { + throw notSupported(); + } + + @Override + public boolean deleteSharedPreferences(String name) { + throw notSupported(); + } + + @Override + public void reloadSharedPreferences() { + throw notSupported(); + } + + @Override + public FileInputStream openFileInput(String name) throws FileNotFoundException { + throw notSupported(); + } + + @Override + public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException { + throw notSupported(); + } + + @Override + public boolean deleteFile(String name) { + throw notSupported(); + } + + @Override + public File getFileStreamPath(String name) { + throw notSupported(); + } + + @Override + public File getSharedPreferencesPath(String name) { + throw notSupported(); + } + + @Override + public File getDataDir() { + throw notSupported(); + } + + @Override + public File getFilesDir() { + throw notSupported(); + } + + @Override + public File getNoBackupFilesDir() { + throw notSupported(); + } + + @Override + public File getExternalFilesDir(String type) { + throw notSupported(); + } + + @Override + public File[] getExternalFilesDirs(String type) { + throw notSupported(); + } + + @Override + public File getObbDir() { + throw notSupported(); + } + + @Override + public File[] getObbDirs() { + throw notSupported(); + } + + @Override + public File getCacheDir() { + throw notSupported(); + } + + @Override + public File getCodeCacheDir() { + throw notSupported(); + } + + @Override + public File getExternalCacheDir() { + throw notSupported(); + } + + @Override + public File getPreloadsFileCache() { + throw notSupported(); + } + + @Override + public File[] getExternalCacheDirs() { + throw notSupported(); + } + + @Override + public File[] getExternalMediaDirs() { + throw notSupported(); + } + + @Override + public String[] fileList() { + throw notSupported(); + } + + @Override + public File getDir(String name, int mode) { + throw notSupported(); + } + + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { + throw notSupported(); + } + + @Override + public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, + DatabaseErrorHandler errorHandler) { + throw notSupported(); + } + + @Override + public boolean moveDatabaseFrom(Context sourceContext, String name) { + throw notSupported(); + } + + @Override + public boolean deleteDatabase(String name) { + throw notSupported(); + } + + @Override + public File getDatabasePath(String name) { + throw notSupported(); + } + + @Override + public String[] databaseList() { + throw notSupported(); + } + + @Override + public Drawable getWallpaper() { + throw notSupported(); + } + + @Override + public Drawable peekWallpaper() { + throw notSupported(); + } + + @Override + public int getWallpaperDesiredMinimumWidth() { + throw notSupported(); + } + + @Override + public int getWallpaperDesiredMinimumHeight() { + throw notSupported(); + } + + @Override + public void setWallpaper(Bitmap bitmap) throws IOException { + throw notSupported(); + } + + @Override + public void setWallpaper(InputStream data) throws IOException { + throw notSupported(); + } + + @Override + public void clearWallpaper() throws IOException { + throw notSupported(); + } + + @Override + public void startActivity(Intent intent) { + throw notSupported(); + } + + @Override + public void startActivity(Intent intent, Bundle options) { + throw notSupported(); + } + + @Override + public void startActivities(Intent[] intents) { + throw notSupported(); + } + + @Override + public void startActivities(Intent[] intents, Bundle options) { + throw notSupported(); + } + + @Override + public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask, + int flagsValues, int extraFlags) throws SendIntentException { + throw notSupported(); + } + + @Override + public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask, + int flagsValues, int extraFlags, Bundle options) throws SendIntentException { + throw notSupported(); + } + + @Override + public void sendBroadcast(Intent intent) { + throw notSupported(); + } + + @Override + public void sendBroadcast(Intent intent, String receiverPermission) { + throw notSupported(); + } + + @Override + public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, + String[] receiverPermissions) { + throw notSupported(); + } + + @Override + public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { + throw notSupported(); + } + + @Override + public void sendOrderedBroadcast(Intent intent, String receiverPermission) { + throw notSupported(); + } + + @Override + public void sendOrderedBroadcast(Intent intent, String receiverPermission, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { + throw notSupported(); + } + + @Override + public void sendOrderedBroadcast(Intent intent, String receiverPermission, + int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { + throw notSupported(); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user) { + throw notSupported(); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) { + throw notSupported(); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, + Bundle options) { + throw notSupported(); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, + int appOp) { + throw notSupported(); + } + + @Override + public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, + int initialCode, String initialData, Bundle initialExtras) { + throw notSupported(); + } + + @Override + public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + throw notSupported(); + } + + @Override + public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp, Bundle options, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { + throw notSupported(); + } + + @Override + public void sendStickyBroadcast(Intent intent) { + throw notSupported(); + } + + @Override + public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + throw notSupported(); + + } + + @Override + public void removeStickyBroadcast(Intent intent) { + throw notSupported(); + + } + + @Override + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) { + throw notSupported(); + } + + @Override + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + throw notSupported(); + + } + + @Override + public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, + BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, + String initialData, Bundle initialExtras) { + throw notSupported(); + } + + @Override + public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) { + throw notSupported(); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + throw notSupported(); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) { + throw notSupported(); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, + String broadcastPermission, Handler scheduler) { + throw notSupported(); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, + String broadcastPermission, Handler scheduler, int flags) { + throw notSupported(); + } + + @Override + public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, + IntentFilter filter, String broadcastPermission, Handler scheduler) { + throw notSupported(); + } + + @Override + public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, + IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) { + throw notSupported(); + } + + @Override + public void unregisterReceiver(BroadcastReceiver receiver) { + throw notSupported(); + } + + @Override + public ComponentName startService(Intent service) { + throw notSupported(); + } + + @Override + public ComponentName startForegroundService(Intent service) { + throw notSupported(); + } + + @Override + public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + throw notSupported(); + } + + @Override + public boolean stopService(Intent service) { + throw notSupported(); + } + + @Override + public ComponentName startServiceAsUser(Intent service, UserHandle user) { + throw notSupported(); + } + + @Override + public boolean stopServiceAsUser(Intent service, UserHandle user) { + throw notSupported(); + } + + @Override + public boolean bindService(Intent service, ServiceConnection conn, int flags) { + throw notSupported(); + } + + @Override + public void unbindService(ServiceConnection conn) { + throw notSupported(); + } + + @Override + public boolean startInstrumentation(ComponentName className, String profileFile, + Bundle arguments) { + throw notSupported(); + } + + @Override + public Object getSystemService(String name) { + throw notSupported(); + } + + @Override + public String getSystemServiceName(Class<?> serviceClass) { + throw notSupported(); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + throw notSupported(); + } + + @Override + public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { + throw notSupported(); + } + + @Override + public int checkCallingPermission(String permission) { + throw notSupported(); + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + throw notSupported(); + } + + @Override + public int checkSelfPermission(String permission) { + throw notSupported(); + } + + @Override + public void enforcePermission(String permission, int pid, int uid, String message) { + throw notSupported(); + } + + @Override + public void enforceCallingPermission(String permission, String message) { + throw notSupported(); + } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + throw notSupported(); + } + + @Override + public void grantUriPermission(String toPackage, Uri uri, int modeFlags) { + throw notSupported(); + } + + @Override + public void revokeUriPermission(Uri uri, int modeFlags) { + throw notSupported(); + } + + @Override + public void revokeUriPermission(String toPackage, Uri uri, int modeFlags) { + throw notSupported(); + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { + throw notSupported(); + } + + @Override + public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) { + throw notSupported(); + } + + @Override + public int checkCallingUriPermission(Uri uri, int modeFlags) { + throw notSupported(); + } + + @Override + public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) { + throw notSupported(); + } + + @Override + public int checkUriPermission(Uri uri, String readPermission, String writePermission, + int pid, int uid, int modeFlags) { + throw notSupported(); + } + + @Override + public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) { + throw notSupported(); + } + + @Override + public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) { + throw notSupported(); + } + + @Override + public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) { + throw notSupported(); + } + + @Override + public void enforceUriPermission(Uri uri, String readPermission, String writePermission, + int pid, int uid, int modeFlags, String message) { + throw notSupported(); + } + + @Override + public Context createPackageContext(String packageName, int flags) + throws NameNotFoundException { + throw notSupported(); + } + + @Override + public Context createApplicationContext(ApplicationInfo application, int flags) + throws NameNotFoundException { + throw notSupported(); + } + + @Override + public Context createContextForSplit(String splitName) throws NameNotFoundException { + throw notSupported(); + } + + @Override + public Context createConfigurationContext(Configuration overrideConfiguration) { + throw notSupported(); + } + + @Override + public Context createDisplayContext(Display display) { + throw notSupported(); + } + + @Override + public Context createDeviceProtectedStorageContext() { + throw notSupported(); + } + + @Override + public Context createCredentialProtectedStorageContext() { + throw notSupported(); + } + + @Override + public DisplayAdjustments getDisplayAdjustments(int displayId) { + throw notSupported(); + } + + @Override + public int getDisplayId() { + throw notSupported(); + } + + @Override + public void updateDisplay(int displayId) { + throw notSupported(); + } + + @Override + public boolean isDeviceProtectedStorage() { + throw notSupported(); + } + + @Override + public boolean isCredentialProtectedStorage() { + throw notSupported(); + } + + @Override + public boolean canLoadUnsafeResources() { + throw notSupported(); + } +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java index 109ef76b535f..1dd5e1ddd630 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java @@ -28,7 +28,6 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.ravenwood.example.BlueManager; import android.ravenwood.example.RedManager; -import android.test.mock.MockContext; import android.util.ArrayMap; import android.util.Singleton; @@ -36,7 +35,7 @@ import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Supplier; -public class RavenwoodContext extends MockContext { +public class RavenwoodContext extends RavenwoodBaseContext { private final String mPackageName; private final HandlerThread mMainThread; diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh index cf58bd2f3717..43b61a46b747 100755 --- a/ravenwood/scripts/ravenwood-stats-collector.sh +++ b/ravenwood/scripts/ravenwood-stats-collector.sh @@ -18,8 +18,14 @@ set -e # Output files -stats=/tmp/ravenwood-stats-all.csv -apis=/tmp/ravenwood-apis-all.csv +out_dir=/tmp/ravenwood +stats=$out_dir/ravenwood-stats-all.csv +apis=$out_dir/ravenwood-apis-all.csv +keep_all_dir=$out_dir/ravenwood-keep-all/ + +rm -fr $out_dir +mkdir -p $out_dir +mkdir -p $keep_all_dir # Where the input files are. path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/ @@ -76,3 +82,7 @@ collect_apis() { collect_stats $stats collect_apis $apis + +cp *keep_all.txt $keep_all_dir +echo "Keep all files created at:" +find $keep_all_dir -type f
\ No newline at end of file diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt index e452299373f8..d856f6d669eb 100644 --- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt @@ -1,5 +1,7 @@ # Only classes listed here can use the Ravenwood annotations. +com.android.internal.ravenwood.* + com.android.internal.display.BrightnessSynchronizer com.android.internal.util.ArrayUtils com.android.internal.logging.MetricsLogger diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt index 371c3acab144..9d29a051d092 100644 --- a/ravenwood/texts/ravenwood-framework-policies.txt +++ b/ravenwood/texts/ravenwood-framework-policies.txt @@ -1,59 +1,59 @@ # Ravenwood "policy" file for framework-minus-apex. # Keep all AIDL interfaces -class :aidl stubclass +class :aidl keepclass # Keep all feature flag implementations -class :feature_flags stubclass +class :feature_flags keepclass # Keep all sysprops generated code implementations -class :sysprops stubclass +class :sysprops keepclass # Exported to Mainline modules; cannot use annotations -class com.android.internal.util.FastXmlSerializer stubclass -class com.android.internal.util.FileRotator stubclass -class com.android.internal.util.HexDump stubclass -class com.android.internal.util.IndentingPrintWriter stubclass -class com.android.internal.util.LocalLog stubclass -class com.android.internal.util.MessageUtils stubclass -class com.android.internal.util.TokenBucket stubclass -class android.os.HandlerExecutor stubclass -class android.util.BackupUtils stubclass -class android.util.IndentingPrintWriter stubclass -class android.util.LocalLog stubclass -class android.util.Pair stubclass -class android.util.Rational stubclass +class com.android.internal.util.FastXmlSerializer keepclass +class com.android.internal.util.FileRotator keepclass +class com.android.internal.util.HexDump keepclass +class com.android.internal.util.IndentingPrintWriter keepclass +class com.android.internal.util.LocalLog keepclass +class com.android.internal.util.MessageUtils keepclass +class com.android.internal.util.TokenBucket keepclass +class android.os.HandlerExecutor keepclass +class android.util.BackupUtils keepclass +class android.util.IndentingPrintWriter keepclass +class android.util.LocalLog keepclass +class android.util.Pair keepclass +class android.util.Rational keepclass # From modules-utils; cannot use annotations -class com.android.internal.util.Preconditions stubclass -class com.android.internal.logging.InstanceId stubclass -class com.android.internal.logging.InstanceIdSequence stubclass -class com.android.internal.logging.UiEvent stubclass -class com.android.internal.logging.UiEventLogger stubclass +class com.android.internal.util.Preconditions keepclass +class com.android.internal.logging.InstanceId keepclass +class com.android.internal.logging.InstanceIdSequence keepclass +class com.android.internal.logging.UiEvent keepclass +class com.android.internal.logging.UiEventLogger keepclass # From modules-utils; cannot use annotations -class com.android.modules.utils.BinaryXmlPullParser stubclass -class com.android.modules.utils.BinaryXmlSerializer stubclass -class com.android.modules.utils.FastDataInput stubclass -class com.android.modules.utils.FastDataOutput stubclass -class com.android.modules.utils.ModifiedUtf8 stubclass -class com.android.modules.utils.TypedXmlPullParser stubclass -class com.android.modules.utils.TypedXmlSerializer stubclass +class com.android.modules.utils.BinaryXmlPullParser keepclass +class com.android.modules.utils.BinaryXmlSerializer keepclass +class com.android.modules.utils.FastDataInput keepclass +class com.android.modules.utils.FastDataOutput keepclass +class com.android.modules.utils.ModifiedUtf8 keepclass +class com.android.modules.utils.TypedXmlPullParser keepclass +class com.android.modules.utils.TypedXmlSerializer keepclass # Uri -class android.net.Uri stubclass -class android.net.UriCodec stubclass +class android.net.Uri keepclass +class android.net.UriCodec keepclass # Telephony -class android.telephony.PinResult stubclass +class android.telephony.PinResult keepclass # Just enough to support mocking, no further functionality -class android.content.BroadcastReceiver stub - method <init> ()V stub -class android.content.Context stub - method <init> ()V stub - method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; stub -class android.content.pm.PackageManager stub - method <init> ()V stub -class android.text.ClipboardManager stub - method <init> ()V stub +class android.content.BroadcastReceiver keep + method <init> ()V keep +class android.content.Context keep + method <init> ()V keep + method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep +class android.content.pm.PackageManager keep + method <init> ()V keep +class android.text.ClipboardManager keep + method <init> ()V keep diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt index d8d563e05435..5cdb4f74d7c0 100644 --- a/ravenwood/texts/ravenwood-services-policies.txt +++ b/ravenwood/texts/ravenwood-services-policies.txt @@ -1,7 +1,7 @@ # Ravenwood "policy" file for services.core. # Keep all AIDL interfaces -class :aidl stubclass +class :aidl keepclass # Keep all feature flag implementations -class :feature_flags stubclass +class :feature_flags keepclass diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java index 468b9ab5710e..219b788448e8 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java @@ -36,6 +36,7 @@ import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback; +import com.android.internal.inputmethod.InlineSuggestionsRequestCallback; import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; import com.android.server.autofill.ui.InlineFillUi; import com.android.server.inputmethod.InputMethodManagerInternal; @@ -376,8 +377,8 @@ final class AutofillInlineSuggestionsRequestSession { /** * Internal implementation of {@link IInlineSuggestionsRequestCallback}. */ - private static final class InlineSuggestionsRequestCallbackImpl extends - IInlineSuggestionsRequestCallback.Stub { + private static final class InlineSuggestionsRequestCallbackImpl + implements InlineSuggestionsRequestCallback { private final WeakReference<AutofillInlineSuggestionsRequestSession> mSession; @@ -388,7 +389,7 @@ final class AutofillInlineSuggestionsRequestSession { @BinderThread @Override - public void onInlineSuggestionsUnsupported() throws RemoteException { + public void onInlineSuggestionsUnsupported() { if (sDebug) Slog.d(TAG, "onInlineSuggestionsUnsupported() called."); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { @@ -412,7 +413,7 @@ final class AutofillInlineSuggestionsRequestSession { } @Override - public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException { + public void onInputMethodStartInput(AutofillId imeFieldId) { if (sVerbose) Slog.v(TAG, "onInputMethodStartInput() received on " + imeFieldId); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { @@ -423,7 +424,7 @@ final class AutofillInlineSuggestionsRequestSession { } @Override - public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException { + public void onInputMethodShowInputRequested(boolean requestResult) { if (sVerbose) { Slog.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult); } @@ -454,7 +455,7 @@ final class AutofillInlineSuggestionsRequestSession { } @Override - public void onInputMethodFinishInput() throws RemoteException { + public void onInputMethodFinishInput() { if (sVerbose) Slog.v(TAG, "onInputMethodFinishInput() received"); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { @@ -466,7 +467,7 @@ final class AutofillInlineSuggestionsRequestSession { @BinderThread @Override - public void onInlineSuggestionsSessionInvalidated() throws RemoteException { + public void onInlineSuggestionsSessionInvalidated() { if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called."); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java index 0e66fbc020a1..71a182225013 100644 --- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java @@ -23,6 +23,7 @@ import android.content.Context; import android.os.Build; import android.util.Slog; +import com.google.security.cryptauth.lib.securegcm.ukey2.AlertException; import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException; import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException; import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1; @@ -203,7 +204,8 @@ public class SecureChannel { * * This method must only be called from one of the two participants. */ - public void establishSecureConnection() throws IOException, SecureChannelException { + public void establishSecureConnection() throws IOException, + SecureChannelException, HandshakeException { if (isSecured()) { Slog.d(TAG, "Channel is already secure."); return; @@ -334,7 +336,7 @@ public class SecureChannel { } } - private void initiateHandshake() throws IOException, BadHandleException { + private void initiateHandshake() throws IOException, BadHandleException , HandshakeException { if (mConnectionContext != null) { Slog.d(TAG, "Ukey2 handshake is already completed."); return; @@ -394,8 +396,8 @@ public class SecureChannel { } } - private void exchangeHandshake() - throws IOException, HandshakeException, BadHandleException, CryptoException { + private void exchangeHandshake() throws IOException, HandshakeException, + BadHandleException, CryptoException, AlertException { if (mConnectionContext != null) { Slog.d(TAG, "Ukey2 handshake is already completed."); return; diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java index 9a73a2d75419..f5db6e962d01 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java @@ -27,9 +27,9 @@ import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_POINTER; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static com.android.server.contextualsearch.flags.Flags.enableExcludePersistentUi; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; @@ -286,13 +286,11 @@ public class ContextualSearchManagerService extends SystemService { } final ScreenCapture.ScreenshotHardwareBuffer shb; if (mWmInternal != null) { - if (enableExcludePersistentUi()) { - shb = mWmInternal.takeAssistScreenshot( - Set.of(TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)); - } else { - shb = mWmInternal.takeAssistScreenshot(/* windowTypesToExclude= */ Set.of()); - } - + shb = mWmInternal.takeAssistScreenshot(Set.of( + TYPE_STATUS_BAR, + TYPE_NAVIGATION_BAR, + TYPE_NAVIGATION_BAR_PANEL, + TYPE_POINTER)); } else { shb = null; } diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index d04dbc2f417e..d9e6186639ce 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -870,6 +870,7 @@ public final class PinnerService extends SystemService { } synchronized (this) { pinnedApp.mFiles.add(pf); + mPinnedFiles.put(pf.fileName, pf); } apkPinSizeLimit -= pf.bytesPinned; @@ -1341,18 +1342,6 @@ public final class PinnerService extends SystemService { public List<PinnedFileStat> getPinnerStats() { ArrayList<PinnedFileStat> stats = new ArrayList<>(); - Collection<PinnedApp> pinnedApps; - synchronized(this) { - pinnedApps = mPinnedApps.values(); - } - for (PinnedApp pinnedApp : pinnedApps) { - for (PinnedFile pf : pinnedApp.mFiles) { - PinnedFileStat stat = - new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName); - stats.add(stat); - } - } - Collection<PinnedFile> pinnedFiles; synchronized(this) { pinnedFiles = mPinnedFiles.values(); diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java index 2812233815a6..42391a55fed6 100644 --- a/services/core/java/com/android/server/WallpaperUpdateReceiver.java +++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java @@ -24,7 +24,6 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.ParcelFileDescriptor; import android.util.Slog; @@ -59,10 +58,10 @@ public class WallpaperUpdateReceiver extends BroadcastReceiver { return; } if (DEBUG) Slog.d(TAG, "Set customized default_wallpaper."); - Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); - // set a blank wallpaper to force a redraw of default_wallpaper - wallpaperManager.setBitmap(blank); - wallpaperManager.setResource(com.android.internal.R.drawable.default_wallpaper); + // Check if it is not a live wallpaper set + if (wallpaperManager.getWallpaperInfo() == null) { + wallpaperManager.clearWallpaper(); + } } catch (Exception e) { Slog.w(TAG, "Failed to customize system wallpaper." + e); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 94bf813d3696..9be0e1ff464e 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -3783,6 +3783,14 @@ public final class ActiveServices { return fgsInfo.getLastFgsStartTime() + Math.max(0, timeLimit - fgsInfo.getTotalRuntime()); } + private TimeLimitedFgsInfo getFgsTimeLimitedInfo(int uid, int fgsType) { + final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(uid); + if (fgsInfo != null) { + return fgsInfo.get(fgsType); + } + return null; + } + private void maybeUpdateFgsTrackingLocked(ServiceRecord sr, int previousFgsType) { final int previouslyTimeLimitedType = getTimeLimitedFgsType(previousFgsType); if (previouslyTimeLimitedType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE @@ -3793,16 +3801,12 @@ public final class ActiveServices { if (previouslyTimeLimitedType != ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) { // FGS is switching types and the previous type was time-limited so update the runtime. - final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid); - if (fgsInfo != null) { - final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(previouslyTimeLimitedType); - if (fgsTypeInfo != null) { - // Update the total runtime for the previous time-limited fgs type. - fgsTypeInfo.updateTotalRuntime(); - // TODO(b/330399444): handle the case where an app is running 2 services of the - // same time-limited type in parallel and stops one of them which leads to the - // second running one gaining additional runtime. - } + final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo( + sr.appInfo.uid, previouslyTimeLimitedType); + if (fgsTypeInfo != null) { + // Update the total runtime for the previous time-limited fgs type. + fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis()); + fgsTypeInfo.decNumParallelServices(); } if (!sr.isFgsTimeLimited()) { @@ -3825,10 +3829,10 @@ public final class ActiveServices { final int timeLimitedFgsType = getTimeLimitedFgsType(sr.foregroundServiceType); TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedFgsType); if (fgsTypeInfo == null) { - fgsTypeInfo = sr.createTimeLimitedFgsInfo(nowUptime); + fgsTypeInfo = sr.createTimeLimitedFgsInfo(); fgsInfo.put(timeLimitedFgsType, fgsTypeInfo); } - fgsTypeInfo.setLastFgsStartTime(nowUptime); + fgsTypeInfo.noteFgsFgsStart(nowUptime); // We'll cancel the previous ANR timer and start a fresh one below. mFGSAnrTimer.cancel(sr); @@ -3852,13 +3856,12 @@ public final class ActiveServices { return; // if the current fgs type is not time-limited, return. } - final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid); - if (fgsInfo != null) { - final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedType); - if (fgsTypeInfo != null) { - // Update the total runtime for the previous time-limited fgs type. - fgsTypeInfo.updateTotalRuntime(); - } + final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo( + sr.appInfo.uid, timeLimitedType); + if (fgsTypeInfo != null) { + // Update the total runtime for the previous time-limited fgs type. + fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis()); + fgsTypeInfo.decNumParallelServices(); } Slog.d(TAG_SERVICE, "Stop FGS timeout: " + sr); mFGSAnrTimer.cancel(sr); @@ -3913,24 +3916,21 @@ public final class ActiveServices { mFGSAnrTimer.accept(sr); traceInstant("FGS timed out: ", sr); - final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid); - if (fgsInfo != null) { - final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(fgsType); - if (fgsTypeInfo != null) { - // Update total runtime for the time-limited fgs type and mark it as timed out. - fgsTypeInfo.updateTotalRuntime(); - fgsTypeInfo.setTimeLimitExceededAt(nowUptime); - - logFGSStateChangeLocked(sr, - FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT, - nowUptime > fgsTypeInfo.getLastFgsStartTime() - ? (int) (nowUptime - fgsTypeInfo.getLastFgsStartTime()) : 0, - FGS_STOP_REASON_UNKNOWN, - FGS_TYPE_POLICY_CHECK_UNKNOWN, - FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA, - false /* fgsRestrictionRecalculated */ - ); - } + final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType); + if (fgsTypeInfo != null) { + // Update total runtime for the time-limited fgs type and mark it as timed out. + fgsTypeInfo.updateTotalRuntime(nowUptime); + fgsTypeInfo.setTimeLimitExceededAt(nowUptime); + + logFGSStateChangeLocked(sr, + FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT, + nowUptime > fgsTypeInfo.getFirstFgsStartUptime() + ? (int) (nowUptime - fgsTypeInfo.getFirstFgsStartUptime()) : 0, + FGS_STOP_REASON_UNKNOWN, + FGS_TYPE_POLICY_CHECK_UNKNOWN, + FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA, + false /* fgsRestrictionRecalculated */ + ); } try { @@ -3949,6 +3949,16 @@ public final class ActiveServices { if (fgsType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) { return; // no timed out FGS type was found (either it was stopped or it switched types) } + + synchronized (mAm) { + final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType); + if (fgsTypeInfo != null) { + // Runtime is already updated when the service times out - if the app didn't + // stop the service, decrement the number of parallel running services here. + fgsTypeInfo.decNumParallelServices(); + } + } + final String reason = "A foreground service of type " + ServiceInfo.foregroundServiceTypeToLabel(fgsType) + " did not stop within its timeout: " + sr.getComponentName(); @@ -3958,7 +3968,6 @@ public final class ActiveServices { Slog.wtf(TAG, reason); return; } - if (android.app.Flags.enableFgsTimeoutCrashBehavior()) { // Crash the app synchronized (mAm) { @@ -4005,7 +4014,6 @@ public final class ActiveServices { private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) { maybeStopShortFgsTimeoutLocked(service); - maybeStopFgsTimeoutLocked(service); final ProcessServiceRecord psr = service.app.mServices; psr.stopService(service); psr.updateBoundClientUids(); @@ -6291,7 +6299,6 @@ public final class ActiveServices { Slog.w(TAG_SERVICE, "Short FGS brought down without stopping: " + r); maybeStopShortFgsTimeoutLocked(r); } - maybeStopFgsTimeoutLocked(r); // Report to all of the connections that the service is no longer // available. @@ -6416,7 +6423,6 @@ public final class ActiveServices { final boolean exitingFg = r.isForeground; if (exitingFg) { maybeStopShortFgsTimeoutLocked(r); - maybeStopFgsTimeoutLocked(r); decActiveForegroundAppLocked(smap, r); synchronized (mAm.mProcessStats.mLock) { ServiceState stracker = r.getTracker(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 46ed1fd79874..008d7b20c5fd 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -598,6 +598,9 @@ public class ActivityManagerService extends IActivityManager.Stub // as one line, but close enough for now. static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100; + // How many seconds should the system wait before terminating the spawned logcat process. + static final int LOGCAT_TIMEOUT_SEC = 10; + // Necessary ApplicationInfo flags to mark an app as persistent static final int PERSISTENT_MASK = ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT; @@ -9939,126 +9942,70 @@ public class ActivityManagerService extends IActivityManager.Stub // If process is null, we are being called from some internal code // and may be about to die -- run this synchronously. final boolean runSynchronously = process == null; - Thread worker = - new Thread("Error dump: " + dropboxTag) { - @Override - public void run() { - if (report != null) { - sb.append(report); - } - - String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag; - String maxBytesSetting = - Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag; - int lines = - Build.IS_USER - ? 0 - : Settings.Global.getInt( - mContext.getContentResolver(), logcatSetting, 0); - int dropboxMaxSize = - Settings.Global.getInt( - mContext.getContentResolver(), - maxBytesSetting, - DROPBOX_DEFAULT_MAX_SIZE); - - if (dataFile != null) { - // Attach the stack traces file to the report so collectors can load - // them - // by file if they have access. - sb.append(DATA_FILE_PATH_HEADER) - .append(dataFile.getAbsolutePath()) - .append('\n'); - - int maxDataFileSize = - dropboxMaxSize - - sb.length() - - lines * RESERVED_BYTES_PER_LOGCAT_LINE - - DATA_FILE_PATH_FOOTER.length(); - - if (maxDataFileSize > 0) { - // Inline dataFile contents if there is room. - try { - sb.append( - FileUtils.readTextFile( - dataFile, - maxDataFileSize, - "\n\n[[TRUNCATED]]\n")); - } catch (IOException e) { - Slog.e(TAG, "Error reading " + dataFile, e); - } - } - - // Always append the footer, even there wasn't enough space to inline - // the - // dataFile contents. - sb.append(DATA_FILE_PATH_FOOTER); - } - - if (crashInfo != null && crashInfo.stackTrace != null) { - sb.append(crashInfo.stackTrace); - } - - if (lines > 0 && !runSynchronously) { - sb.append("\n"); - - InputStreamReader input = null; - try { - java.lang.Process logcat = - new ProcessBuilder( - // Time out after 10s of inactivity, but - // kill logcat with SEGV - // so we can investigate why it didn't - // finish. - "/system/bin/timeout", - "-i", - "-s", - "SEGV", - "10s", - // Merge several logcat streams, and take - // the last N lines. - "/system/bin/logcat", - "-v", - "threadtime", - "-b", - "events", - "-b", - "system", - "-b", - "main", - "-b", - "crash", - "-t", - String.valueOf(lines)) - .redirectErrorStream(true) - .start(); - - try { - logcat.getOutputStream().close(); - } catch (IOException e) { - } - try { - logcat.getErrorStream().close(); - } catch (IOException e) { - } - input = new InputStreamReader(logcat.getInputStream()); - - int num; - char[] buf = new char[8192]; - while ((num = input.read(buf)) > 0) sb.append(buf, 0, num); - } catch (IOException e) { - Slog.e(TAG, "Error running logcat", e); - } finally { - if (input != null) - try { - input.close(); - } catch (IOException e) { - } - } + Thread worker = new Thread("Error dump: " + dropboxTag) { + @Override + public void run() { + if (report != null) { + sb.append(report); + } + + String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag; + String kerLogSetting = Settings.Global.ERROR_KERNEL_LOG_PREFIX + dropboxTag; + String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag; + int logcatLines = Build.IS_USER + ? 0 + : Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0); + int kernelLogLines = Build.IS_USER + ? 0 + : Settings.Global.getInt(mContext.getContentResolver(), kerLogSetting, 0); + int dropboxMaxSize = Settings.Global.getInt( + mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE); + + if (dataFile != null) { + // Attach the stack traces file to the report so collectors can load them + // by file if they have access. + sb.append(DATA_FILE_PATH_HEADER) + .append(dataFile.getAbsolutePath()).append('\n'); + + int maxDataFileSize = dropboxMaxSize + - sb.length() + - logcatLines * RESERVED_BYTES_PER_LOGCAT_LINE + - kernelLogLines * RESERVED_BYTES_PER_LOGCAT_LINE + - DATA_FILE_PATH_FOOTER.length(); + + if (maxDataFileSize > 0) { + // Inline dataFile contents if there is room. + try { + sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize, + "\n\n[[TRUNCATED]]\n")); + } catch (IOException e) { + Slog.e(TAG, "Error reading " + dataFile, e); } + } + // Always append the footer, even there wasn't enough space to inline the + // dataFile contents. + sb.append(DATA_FILE_PATH_FOOTER); + } - dbox.addText(dropboxTag, sb.toString()); + if (crashInfo != null && crashInfo.stackTrace != null) { + sb.append(crashInfo.stackTrace); + } + boolean shouldAddLogs = logcatLines > 0 || kernelLogLines > 0; + if (!runSynchronously && shouldAddLogs) { + sb.append("\n"); + if (logcatLines > 0) { + fetchLogcatBuffers(sb, logcatLines, LOGCAT_TIMEOUT_SEC, + List.of("events", "system", "main", "crash")); } - }; + if (kernelLogLines > 0) { + fetchLogcatBuffers(sb, kernelLogLines, LOGCAT_TIMEOUT_SEC / 2, + List.of("kernel")); + } + } + + dbox.addText(dropboxTag, sb.toString()); + } + }; if (runSynchronously) { final int oldMask = StrictMode.allowThreadDiskWritesMask(); @@ -10321,6 +10268,67 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Retrieves logs from specified logcat buffers and appends them to a StringBuilder + * in the supplied order. The method executes a logcat command to fetch specific + * log entries from the supplied buffers. + * + * @param sb the StringBuilder to append the logcat output to. + * @param lines the number of lines to retrieve. + * @param timeout the maximum allowed time in seconds for logcat to run before being terminated. + * @param buffers the list of log buffers from which to retrieve logs. + */ + private static void fetchLogcatBuffers(StringBuilder sb, int lines, + int timeout, List<String> buffers) { + + if (buffers.size() == 0 || lines <= 0 || timeout <= 0) { + return; + } + + List<String> command = new ArrayList<>(10 + (2 * buffers.size())); + // Time out after 10s of inactivity, but kill logcat with SEGV + // so we can investigate why it didn't finish. + command.add("/system/bin/timeout"); + command.add("-i"); + command.add("-s"); + command.add("SEGV"); + command.add(timeout + "s"); + + // Merge several logcat streams, and take the last N lines. + command.add("/system/bin/logcat"); + command.add("-v"); + // This adds a timestamp and thread info to each log line. + command.add("threadtime"); + for (String buffer : buffers) { + command.add("-b"); + command.add(buffer); + } + // Limit the output to the last N lines. + command.add("-t"); + command.add(String.valueOf(lines)); + + try { + java.lang.Process proc = + new ProcessBuilder(command).redirectErrorStream(true).start(); + + // Close the output stream immediately as we do not send input to the process. + try { + proc.getOutputStream().close(); + } catch (IOException e) { + } + + try (InputStreamReader reader = new InputStreamReader(proc.getInputStream())) { + char[] buffer = new char[8192]; + int numRead; + while ((numRead = reader.read(buffer, 0, buffer.length)) > 0) { + sb.append(buffer, 0, numRead); + } + } + } catch (IOException e) { + Slog.e(TAG, "Error running logcat", e); + } + } + + /** * Check if the calling process has the permission to dump given package, * throw SecurityException if it doesn't have the permission. * diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index a182a106ed8c..bbd432340e8f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -130,6 +130,7 @@ import com.android.server.am.nano.Capabilities; import com.android.server.am.nano.Capability; import com.android.server.am.nano.FrameworkCapability; import com.android.server.am.nano.VMCapability; +import com.android.server.am.nano.VMInfo; import com.android.server.compat.PlatformCompat; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.Slogf; @@ -460,6 +461,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } } + String vmName = System.getProperty("java.vm.name", "?"); + String vmVersion = System.getProperty("java.vm.version", "?"); if (outputAsProtobuf) { Capabilities capabilities = new Capabilities(); @@ -486,6 +489,11 @@ final class ActivityManagerShellCommand extends ShellCommand { capabilities.frameworkCapabilities[i] = cap; } + VMInfo vmInfo = new VMInfo(); + vmInfo.name = vmName; + vmInfo.version = vmVersion; + capabilities.vmInfo = vmInfo; + try { getRawOutputStream().write(Capabilities.toByteArray(capabilities)); } catch (IOException e) { @@ -505,6 +513,8 @@ final class ActivityManagerShellCommand extends ShellCommand { for (String capability : Debug.getFeatureList()) { pw.println("framework:" + capability); } + pw.println("vm_name:" + vmName); + pw.println("vm_version:" + vmVersion); } return 0; } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 8eca4fc0d2ff..218434049869 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -690,10 +690,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN private long mTimeLimitExceededAt = Long.MIN_VALUE; @UptimeMillisLong private long mTotalRuntime = 0; + private int mNumParallelServices = 0; - TimeLimitedFgsInfo(@UptimeMillisLong long startTime) { - mFirstFgsStartUptime = startTime; - mFirstFgsStartRealtime = SystemClock.elapsedRealtime(); + public void noteFgsFgsStart(@UptimeMillisLong long startTime) { + mNumParallelServices++; + if (mNumParallelServices == 1) { + mFirstFgsStartUptime = startTime; + mFirstFgsStartRealtime = SystemClock.elapsedRealtime(); + } mLastFgsStartTime = startTime; } @@ -707,17 +711,23 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return mFirstFgsStartRealtime; } - public void setLastFgsStartTime(@UptimeMillisLong long startTime) { - mLastFgsStartTime = startTime; - } - @UptimeMillisLong public long getLastFgsStartTime() { return mLastFgsStartTime; } - public void updateTotalRuntime() { - mTotalRuntime += SystemClock.uptimeMillis() - mLastFgsStartTime; + public void decNumParallelServices() { + if (mNumParallelServices > 0) { + mNumParallelServices--; + } + if (mNumParallelServices == 0) { + mLastFgsStartTime = 0; + } + } + + public void updateTotalRuntime(@UptimeMillisLong long nowUptime) { + mTotalRuntime += nowUptime - mLastFgsStartTime; + mLastFgsStartTime = nowUptime; } @UptimeMillisLong @@ -735,6 +745,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } public void reset() { + mNumParallelServices = 0; mFirstFgsStartUptime = 0; mFirstFgsStartRealtime = 0; mLastFgsStartTime = 0; @@ -1872,8 +1883,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN /** * Called when a time-limited FGS starts. */ - public TimeLimitedFgsInfo createTimeLimitedFgsInfo(@UptimeMillisLong long nowUptime) { - return new TimeLimitedFgsInfo(nowUptime); + public TimeLimitedFgsInfo createTimeLimitedFgsInfo() { + return new TimeLimitedFgsInfo(); } /** diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index d9c3ab806888..30d12e670a6c 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -1189,7 +1189,11 @@ public class AutomaticBrightnessController { update(); } - void switchMode(@AutomaticBrightnessMode int mode) { + /** + * Responsible for switching the AutomaticBrightnessMode of the associated display. Also takes + * care of resetting the short term model wherever required + */ + public void switchMode(@AutomaticBrightnessMode int mode) { if (!mBrightnessMappingStrategyMap.contains(mode)) { return; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 70a1014a0c8c..0fcdf198eece 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -84,6 +84,7 @@ import com.android.server.display.brightness.BrightnessUtils; import com.android.server.display.brightness.DisplayBrightnessController; import com.android.server.display.brightness.clamper.BrightnessClamperController; import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy2; +import com.android.server.display.brightness.strategy.DisplayBrightnessStrategyConstants; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; import com.android.server.display.config.HysteresisLevels; @@ -797,7 +798,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return; } mDisplayStateController.overrideDozeScreenState(displayState, reason); - sendUpdatePowerState(); + updatePowerState(); }, mClock.uptimeMillis()); } @@ -1333,12 +1334,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayStateController.shouldPerformScreenOffTransition()); state = mPowerState.getScreenState(); - // Switch to doze auto-brightness mode if needed - if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null - && !mAutomaticBrightnessController.isInIdleMode()) { - mAutomaticBrightnessController.switchMode(Display.isDozeState(state) - ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); - } DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController .updateBrightness(mPowerRequest, state); @@ -1372,6 +1367,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean wasShortTermModelActive = mAutomaticBrightnessStrategy.isShortTermModelActive(); if (!mFlags.isRefactorDisplayPowerControllerEnabled()) { + // Switch to doze auto-brightness mode if needed + if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null + && !mAutomaticBrightnessController.isInIdleMode()) { + mAutomaticBrightnessController.switchMode(Display.isDozeState(state) + ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); + } + mAutomaticBrightnessStrategy.setAutoBrightnessState(state, mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(), mBrightnessReasonTemp.getReason(), mPowerRequest.policy, @@ -1440,45 +1442,52 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightnessState = clampScreenBrightness(brightnessState); } - // If there's an offload session, we need to set the initial doze brightness before - // the offload session starts controlling the brightness. - // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy - // will be selected again, meaning that no new brightness will be sent to the hardware and - // the display will stay at the brightness level set by the offload session. - if (Float.isNaN(brightnessState) && mFlags.isDisplayOffloadEnabled() - && Display.isDozeState(state) && mDisplayOffloadSession != null) { - if (mAutomaticBrightnessController != null - && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) { - // Use the auto-brightness curve and the last observed lux - rawBrightnessState = mAutomaticBrightnessController - .getAutomaticScreenBrightnessBasedOnLastUsedLux( - mTempBrightnessEvent); - } else { - rawBrightnessState = getDozeBrightnessForOffload(); - mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags() - | BrightnessEvent.FLAG_DOZE_SCALE); - } - - if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) { - brightnessState = clampScreenBrightness(rawBrightnessState); - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL); - + if (Display.isDozeState(state)) { + // If there's an offload session, we need to set the initial doze brightness before + // the offload session starts controlling the brightness. + // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy + // will be selected again, meaning that no new brightness will be sent to the hardware + // and the display will stay at the brightness level set by the offload session. + if ((Float.isNaN(brightnessState) + || displayBrightnessState.getDisplayBrightnessStrategyName() + .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME)) + && mFlags.isDisplayOffloadEnabled() + && mDisplayOffloadSession != null) { if (mAutomaticBrightnessController != null && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) { - // Keep the brightness in the setting so that we can use it after the screen - // turns on, until a lux sample becomes available. We don't do this when - // auto-brightness is disabled - in that situation we still want to use - // the last brightness from when the screen was on. - updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState; + // Use the auto-brightness curve and the last observed lux + rawBrightnessState = mAutomaticBrightnessController + .getAutomaticScreenBrightnessBasedOnLastUsedLux( + mTempBrightnessEvent); + } else { + rawBrightnessState = getDozeBrightnessForOffload(); + mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags() + | BrightnessEvent.FLAG_DOZE_SCALE); + } + + if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) { + brightnessState = clampScreenBrightness(rawBrightnessState); + mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL); + + if (mAutomaticBrightnessController != null + && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) { + // Keep the brightness in the setting so that we can use it after the screen + // turns on, until a lux sample becomes available. We don't do this when + // auto-brightness is disabled - in that situation we still want to use + // the last brightness from when the screen was on. + updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState; + } } } - } - // Use default brightness when dozing unless overridden. - if (Float.isNaN(brightnessState) && Display.isDozeState(state)) { - rawBrightnessState = mScreenBrightnessDozeConfig; - brightnessState = clampScreenBrightness(rawBrightnessState); - mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); + // Use default brightness when dozing unless overridden. + if (Float.isNaN(brightnessState) + || displayBrightnessState.getDisplayBrightnessStrategyName() + .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME)) { + rawBrightnessState = mScreenBrightnessDozeConfig; + brightnessState = clampScreenBrightness(rawBrightnessState); + mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); + } } if (!mFlags.isRefactorDisplayPowerControllerEnabled()) { @@ -1502,7 +1511,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Apply manual brightness. - if (Float.isNaN(brightnessState)) { + if (Float.isNaN(brightnessState) && !mFlags.isRefactorDisplayPowerControllerEnabled()) { rawBrightnessState = currentBrightnessSetting; brightnessState = clampScreenBrightness(rawBrightnessState); if (brightnessState != currentBrightnessSetting) { diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index 22a21a6c113c..feec4e6b2259 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -32,6 +32,7 @@ import com.android.server.display.brightness.strategy.AutomaticBrightnessStrateg import com.android.server.display.brightness.strategy.BoostBrightnessStrategy; import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy; import com.android.server.display.brightness.strategy.DozeBrightnessStrategy; +import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy; import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy; import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy; import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy; @@ -85,6 +86,9 @@ public class DisplayBrightnessStrategySelector { @Nullable private final AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy; + @Nullable + private final FallbackBrightnessStrategy mFallbackBrightnessStrategy; + // A collective representation of all the strategies that the selector is aware of. This is // non null, but the strategies this is tracking can be null @NonNull @@ -118,7 +122,8 @@ public class DisplayBrightnessStrategySelector { mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy(); mAutomaticBrightnessStrategy1 = (!mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null - : injector.getAutomaticBrightnessStrategy1(context, displayId); + : injector.getAutomaticBrightnessStrategy1(context, displayId, + mDisplayManagerFlags); mAutomaticBrightnessStrategy2 = (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null : injector.getAutomaticBrightnessStrategy2(context, displayId); @@ -134,11 +139,14 @@ public class DisplayBrightnessStrategySelector { } else { mOffloadBrightnessStrategy = null; } + mFallbackBrightnessStrategy = (mDisplayManagerFlags + .isRefactorDisplayPowerControllerEnabled()) + ? injector.getFallbackBrightnessStrategy() : null; mDisplayBrightnessStrategies = new DisplayBrightnessStrategy[]{mInvalidBrightnessStrategy, mScreenOffBrightnessStrategy, mDozeBrightnessStrategy, mFollowerBrightnessStrategy, mBoostBrightnessStrategy, mOverrideBrightnessStrategy, mTemporaryBrightnessStrategy, mAutomaticBrightnessStrategy1, mOffloadBrightnessStrategy, - mAutoBrightnessFallbackStrategy}; + mAutoBrightnessFallbackStrategy, mFallbackBrightnessStrategy}; mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean( R.bool.config_allowAutoBrightnessWhileDozing); mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName(); @@ -179,6 +187,12 @@ public class DisplayBrightnessStrategySelector { displayBrightnessStrategy = mOffloadBrightnessStrategy; } else if (isAutoBrightnessFallbackStrategyValid()) { displayBrightnessStrategy = mAutoBrightnessFallbackStrategy; + } else { + // This will become the ultimate fallback strategy once the flag has been fully rolled + // out + if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) { + displayBrightnessStrategy = mFallbackBrightnessStrategy; + } } if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) { @@ -330,8 +344,8 @@ public class DisplayBrightnessStrategySelector { } AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context, - int displayId) { - return new AutomaticBrightnessStrategy(context, displayId); + int displayId, DisplayManagerFlags displayManagerFlags) { + return new AutomaticBrightnessStrategy(context, displayId, displayManagerFlags); } AutomaticBrightnessStrategy2 getAutomaticBrightnessStrategy2(Context context, @@ -347,5 +361,9 @@ public class DisplayBrightnessStrategySelector { AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() { return new AutoBrightnessFallbackStrategy(/* injector= */ null); } + + FallbackBrightnessStrategy getFallbackBrightnessStrategy() { + return new FallbackBrightnessStrategy(); + } } } diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index 23052286d9ec..f809a49fd3d3 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -17,6 +17,9 @@ package com.android.server.display.brightness.strategy; import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; + import android.annotation.Nullable; import android.content.Context; import android.hardware.display.BrightnessConfiguration; @@ -33,6 +36,7 @@ import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.brightness.BrightnessUtils; import com.android.server.display.brightness.StrategyExecutionRequest; import com.android.server.display.brightness.StrategySelectionNotifyRequest; +import com.android.server.display.feature.DisplayManagerFlags; import java.io.PrintWriter; @@ -98,19 +102,24 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 private Injector mInjector; + private DisplayManagerFlags mDisplayManagerFlags; + @VisibleForTesting - AutomaticBrightnessStrategy(Context context, int displayId, Injector injector) { + AutomaticBrightnessStrategy(Context context, int displayId, Injector injector, + DisplayManagerFlags displayManagerFlags) { super(context, displayId); mContext = context; mDisplayId = displayId; mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mDisplayManagerFlags = displayManagerFlags; mInjector = (injector == null) ? new RealInjector() : injector; } - public AutomaticBrightnessStrategy(Context context, int displayId) { - this(context, displayId, null); + public AutomaticBrightnessStrategy(Context context, int displayId, + DisplayManagerFlags displayManagerFlags) { + this(context, displayId, null, displayManagerFlags); } /** @@ -120,6 +129,7 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 public void setAutoBrightnessState(int targetDisplayState, boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy, float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) { + switchMode(targetDisplayState); final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE; mIsAutoBrightnessEnabled = shouldUseAutoBrightness() @@ -479,6 +489,16 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 } } + + private void switchMode(int state) { + if (mDisplayManagerFlags.areAutoBrightnessModesEnabled() + && mAutomaticBrightnessController != null + && !mAutomaticBrightnessController.isInIdleMode()) { + mAutomaticBrightnessController.switchMode(Display.isDozeState(state) + ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT); + } + } + /** * Evaluates if there are any temporary auto-brightness adjustments which is not applied yet. * Temporary brightness adjustments happen when the user moves the brightness slider in the diff --git a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java index 504683a55735..7b2f2b9d307b 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java +++ b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java @@ -18,4 +18,5 @@ package com.android.server.display.brightness.strategy; public class DisplayBrightnessStrategyConstants { static final String INVALID_BRIGHTNESS_STRATEGY_NAME = "InvalidBrightnessStrategy"; + public static final String FALLBACK_BRIGHTNESS_STRATEGY_NAME = "FallbackBrightnessStrategy"; } diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java new file mode 100644 index 000000000000..3463649aa000 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.strategy; + +import android.annotation.NonNull; + +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; +import com.android.server.display.brightness.StrategyExecutionRequest; +import com.android.server.display.brightness.StrategySelectionNotifyRequest; + +import java.io.PrintWriter; + +/** + * Manages the brightness of the associated display when no other strategy qualifies for + * setting up the brightness state. This strategy is also being used for evaluating the + * display brightness state when we have a manually set brightness. This is a temporary state, and + * the logic for evaluating the manual brightness will be moved to a separate strategy + */ +public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{ + @Override + public DisplayBrightnessState updateBrightness( + StrategyExecutionRequest strategyExecutionRequest) { + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_MANUAL); + return new DisplayBrightnessState.Builder() + .setBrightness(strategyExecutionRequest.getCurrentScreenBrightness()) + .setSdrBrightness(strategyExecutionRequest.getCurrentScreenBrightness()) + .setBrightnessReason(brightnessReason) + .setDisplayBrightnessStrategyName(getName()) + // The fallback brightness might change due to clamping. Make sure we tell the rest + // of the system by updating the setting + .setShouldUpdateScreenBrightnessSetting(true) + .build(); + } + + @NonNull + @Override + public String getName() { + return DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME; + } + + @Override + public int getReason() { + return BrightnessReason.REASON_MANUAL; + } + + @Override + public void dump(PrintWriter writer) { + + } + + @Override + public void strategySelectionPostProcessor( + StrategySelectionNotifyRequest strategySelectionNotifyRequest) { + + } +} diff --git a/services/core/java/com/android/server/display/config/RefreshRateData.java b/services/core/java/com/android/server/display/config/RefreshRateData.java index b186fd57e31f..d7ed904e398d 100644 --- a/services/core/java/com/android/server/display/config/RefreshRateData.java +++ b/services/core/java/com/android/server/display/config/RefreshRateData.java @@ -20,6 +20,10 @@ import android.annotation.Nullable; import android.content.res.Resources; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Collections; +import java.util.List; /** * RefreshRates config for display @@ -58,12 +62,17 @@ public class RefreshRateData { */ public final int defaultRefreshRateInHbmSunlight; + public final List<SupportedModeData> lowPowerSupportedModes; + + @VisibleForTesting public RefreshRateData(int defaultRefreshRate, int defaultPeakRefreshRate, - int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight) { + int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight, + List<SupportedModeData> lowPowerSupportedModes) { this.defaultRefreshRate = defaultRefreshRate; this.defaultPeakRefreshRate = defaultPeakRefreshRate; this.defaultRefreshRateInHbmHdr = defaultRefreshRateInHbmHdr; this.defaultRefreshRateInHbmSunlight = defaultRefreshRateInHbmSunlight; + this.lowPowerSupportedModes = Collections.unmodifiableList(lowPowerSupportedModes); } @@ -71,9 +80,10 @@ public class RefreshRateData { public String toString() { return "RefreshRateData {" + "defaultRefreshRate: " + defaultRefreshRate - + "defaultPeakRefreshRate: " + defaultPeakRefreshRate - + "defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr - + "defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight + + ", defaultPeakRefreshRate: " + defaultPeakRefreshRate + + ", defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr + + ", defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight + + ", lowPowerSupportedModes=" + lowPowerSupportedModes + "} "; } @@ -90,8 +100,13 @@ public class RefreshRateData { int defaultRefreshRateInHbmSunlight = loadDefaultRefreshRateInHbmSunlight( refreshRateConfigs, resources); + NonNegativeFloatToFloatMap modes = + refreshRateConfigs == null ? null : refreshRateConfigs.getLowPowerSupportedModes(); + List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(modes); + return new RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate, - defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight); + defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight, + lowPowerSupportedModes); } private static int loadDefaultRefreshRate( diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java index 6ad13c3b8a40..8bfc4a3ad77a 100644 --- a/services/core/java/com/android/server/display/config/SensorData.java +++ b/services/core/java/com/android/server/display/config/SensorData.java @@ -24,7 +24,6 @@ import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.feature.DisplayManagerFlags; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -42,7 +41,7 @@ public class SensorData { public final String name; public final float minRefreshRate; public final float maxRefreshRate; - public final List<SupportedMode> supportedModes; + public final List<SupportedModeData> supportedModes; @VisibleForTesting public SensorData() { @@ -61,7 +60,7 @@ public class SensorData { @VisibleForTesting public SensorData(String type, String name, float minRefreshRate, float maxRefreshRate, - List<SupportedMode> supportedModes) { + List<SupportedModeData> supportedModes) { this.type = type; this.name = name; this.minRefreshRate = minRefreshRate; @@ -214,26 +213,11 @@ public class SensorData { minRefreshRate = rr.getMinimum().floatValue(); maxRefreshRate = rr.getMaximum().floatValue(); } - ArrayList<SupportedMode> supportedModes = new ArrayList<>(); - NonNegativeFloatToFloatMap configSupportedModes = sensorDetails.getSupportedModes(); - if (configSupportedModes != null) { - for (NonNegativeFloatToFloatPoint supportedMode : configSupportedModes.getPoint()) { - supportedModes.add(new SupportedMode(supportedMode.getFirst().floatValue(), - supportedMode.getSecond().floatValue())); - } - } + List<SupportedModeData> supportedModes = SupportedModeData.load( + sensorDetails.getSupportedModes()); return new SensorData(sensorDetails.getType(), sensorDetails.getName(), minRefreshRate, maxRefreshRate, supportedModes); } - public static class SupportedMode { - public final float refreshRate; - public final float vsyncRate; - - public SupportedMode(float refreshRate, float vsyncRate) { - this.refreshRate = refreshRate; - this.vsyncRate = vsyncRate; - } - } } diff --git a/services/core/java/com/android/server/display/config/SupportedModeData.java b/services/core/java/com/android/server/display/config/SupportedModeData.java new file mode 100644 index 000000000000..3c82884e1024 --- /dev/null +++ b/services/core/java/com/android/server/display/config/SupportedModeData.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.config; + +import android.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Supported display mode data. Display mode is uniquely identified by refreshRate-vsync pair + */ +public class SupportedModeData { + public final float refreshRate; + public final float vsyncRate; + + public SupportedModeData(float refreshRate, float vsyncRate) { + this.refreshRate = refreshRate; + this.vsyncRate = vsyncRate; + } + + @Override + public String toString() { + return "SupportedModeData{" + + "refreshRate= " + refreshRate + + ", vsyncRate= " + vsyncRate + + '}'; + } + + static List<SupportedModeData> load(@Nullable NonNegativeFloatToFloatMap configMap) { + ArrayList<SupportedModeData> supportedModes = new ArrayList<>(); + if (configMap != null) { + for (NonNegativeFloatToFloatPoint supportedMode : configMap.getPoint()) { + supportedModes.add(new SupportedModeData(supportedMode.getFirst().floatValue(), + supportedMode.getSecond().floatValue())); + } + } + return supportedModes; + } +} diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 91bd80eb9037..846ee238499a 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -78,6 +78,7 @@ import com.android.server.LocalServices; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint; import com.android.server.display.config.RefreshRateData; +import com.android.server.display.config.SupportedModeData; import com.android.server.display.feature.DeviceConfigParameterProvider; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.utils.AmbientFilter; @@ -451,15 +452,6 @@ public class DisplayModeDirector { return config != null && config.isVrrSupportEnabled(); } - private boolean isVrrSupportedByAnyDisplayLocked() { - for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) { - if (mDisplayDeviceConfigByDisplay.valueAt(i).isVrrSupportEnabled()) { - return true; - } - } - return false; - } - /** * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges. */ @@ -939,18 +931,44 @@ public class DisplayModeDirector { private final Uri mMatchContentFrameRateSetting = Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE); - private final boolean mVsynLowPowerVoteEnabled; + private final boolean mVsyncLowPowerVoteEnabled; private final boolean mPeakRefreshRatePhysicalLimitEnabled; private final Context mContext; + private final Handler mHandler; private float mDefaultPeakRefreshRate; private float mDefaultRefreshRate; + private boolean mIsLowPower = false; + + private final DisplayManager.DisplayListener mDisplayListener = + new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + synchronized (mLock) { + updateLowPowerModeAllowedModesLocked(); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + mVotesStorage.updateVote(displayId, Vote.PRIORITY_LOW_POWER_MODE_MODES, + null); + } + + @Override + public void onDisplayChanged(int displayId) { + synchronized (mLock) { + updateLowPowerModeAllowedModesLocked(); + } + } + }; SettingsObserver(@NonNull Context context, @NonNull Handler handler, DisplayManagerFlags flags) { super(handler); mContext = context; - mVsynLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled(); + mHandler = handler; + mVsyncLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled(); mPeakRefreshRatePhysicalLimitEnabled = flags.isPeakRefreshRatePhysicalLimitEnabled(); // We don't want to load from the DeviceConfig while constructing since this leads to // a spike in the latency of DisplayManagerService startup. This happens because @@ -983,6 +1001,7 @@ public class DisplayModeDirector { UserHandle.USER_SYSTEM); cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/, this); + mInjector.registerDisplayListener(mDisplayListener, mHandler); float deviceConfigDefaultPeakRefresh = mConfigParameterProvider.getPeakRefreshRateDefault(); @@ -995,6 +1014,7 @@ public class DisplayModeDirector { updateLowPowerModeSettingLocked(); updateModeSwitchingTypeSettingLocked(); } + } public void setDefaultRefreshRate(float refreshRate) { @@ -1061,23 +1081,36 @@ public class DisplayModeDirector { } private void updateLowPowerModeSettingLocked() { - boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(), + mIsLowPower = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0; final Vote vote; - if (inLowPowerMode && mVsynLowPowerVoteEnabled && isVrrSupportedByAnyDisplayLocked()) { - vote = Vote.forSupportedRefreshRates(List.of( - new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f, - /* vsyncRate= */ 240f), - new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f, - /* vsyncRate= */ 60f) - )); - } else if (inLowPowerMode) { + if (mIsLowPower) { vote = Vote.forRenderFrameRates(0f, 60f); } else { vote = null; } - mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE, vote); - mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode); + mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, vote); + mBrightnessObserver.onLowPowerModeEnabledLocked(mIsLowPower); + updateLowPowerModeAllowedModesLocked(); + } + + private void updateLowPowerModeAllowedModesLocked() { + if (!mVsyncLowPowerVoteEnabled) { + return; + } + if (mIsLowPower) { + for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) { + DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.valueAt(i); + List<SupportedModeData> supportedModes = config + .getRefreshRateData().lowPowerSupportedModes; + mVotesStorage.updateVote( + mDisplayDeviceConfigByDisplay.keyAt(i), + Vote.PRIORITY_LOW_POWER_MODE_MODES, + Vote.forSupportedRefreshRates(supportedModes)); + } + } else { + mVotesStorage.removeAllVotesForPriority(Vote.PRIORITY_LOW_POWER_MODE_MODES); + } } /** diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java index ddb334ee1a9d..8167c1fe1e91 100644 --- a/services/core/java/com/android/server/display/mode/Vote.java +++ b/services/core/java/com/android/server/display/mode/Vote.java @@ -18,6 +18,9 @@ package com.android.server.display.mode; import android.annotation.NonNull; +import com.android.server.display.config.SupportedModeData; + +import java.util.ArrayList; import java.util.List; interface Vote { @@ -102,9 +105,15 @@ interface Vote { // For internal application to limit display modes to specific ids int PRIORITY_SYSTEM_REQUESTED_MODES = 14; - // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if + // PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if + // Settings.Global.LOW_POWER_MODE is on. + // Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other + // higher priority votes), render rate limit can still apply + int PRIORITY_LOW_POWER_MODE_MODES = 14; + + // PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if // Settings.Global.LOW_POWER_MODE is on. - int PRIORITY_LOW_POWER_MODE = 15; + int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 15; // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the // higher priority voters' result is a range, it will fix the rate to a single choice. @@ -177,22 +186,26 @@ interface Vote { return new BaseModeRefreshRateVote(baseModeRefreshRate); } - static Vote forSupportedRefreshRates( - List<SupportedRefreshRatesVote.RefreshRates> refreshRates) { - return new SupportedRefreshRatesVote(refreshRates); + static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) { + if (supportedModes.isEmpty()) { + return null; + } + List<SupportedRefreshRatesVote.RefreshRates> rates = new ArrayList<>(); + for (SupportedModeData data : supportedModes) { + rates.add(new SupportedRefreshRatesVote.RefreshRates(data.refreshRate, data.vsyncRate)); + } + return new SupportedRefreshRatesVote(rates); } static Vote forSupportedModes(List<Integer> modeIds) { return new SupportedModesVote(modeIds); } - - static Vote forSupportedRefreshRatesAndDisableSwitching( List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) { return new CombinedVote( List.of(forDisableRefreshRateSwitching(), - forSupportedRefreshRates(supportedRefreshRates))); + new SupportedRefreshRatesVote(supportedRefreshRates))); } static String priorityToString(int priority) { @@ -213,8 +226,10 @@ interface Vote { return "PRIORITY_HIGH_BRIGHTNESS_MODE"; case PRIORITY_PROXIMITY: return "PRIORITY_PROXIMITY"; - case PRIORITY_LOW_POWER_MODE: - return "PRIORITY_LOW_POWER_MODE"; + case PRIORITY_LOW_POWER_MODE_MODES: + return "PRIORITY_LOW_POWER_MODE_MODES"; + case PRIORITY_LOW_POWER_MODE_RENDER_RATE: + return "PRIORITY_LOW_POWER_MODE_RENDER_RATE"; case PRIORITY_SKIN_TEMPERATURE: return "PRIORITY_SKIN_TEMPERATURE"; case PRIORITY_UDFPS: @@ -227,6 +242,8 @@ interface Vote { return "PRIORITY_LIMIT_MODE"; case PRIORITY_SYNCHRONIZED_REFRESH_RATE: return "PRIORITY_SYNCHRONIZED_REFRESH_RATE"; + case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE: + return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE"; case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE: return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE"; case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE: diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 816242df639d..c6aef7fb3540 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -249,14 +249,14 @@ final class DreamController { mCurrentDream.mAppTask = appTask; } - void setDreamHasFocus(boolean hasFocus) { + void setDreamIsObscured(boolean isObscured) { if (mCurrentDream != null) { - mCurrentDream.mDreamHasFocus = hasFocus; + mCurrentDream.mDreamIsObscured = isObscured; } } - boolean dreamHasFocus() { - return mCurrentDream != null && mCurrentDream.mDreamHasFocus; + boolean dreamIsFrontmost() { + return mCurrentDream != null && mCurrentDream.dreamIsFrontmost(); } /** @@ -451,7 +451,7 @@ final class DreamController { private String mStopReason; private long mDreamStartTime; public boolean mWakingGently; - public boolean mDreamHasFocus; + private boolean mDreamIsObscured; private final Runnable mStopPreviousDreamsIfNeeded = this::stopPreviousDreamsIfNeeded; private final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded; @@ -549,5 +549,9 @@ final class DreamController { mHandler.removeCallbacks(mReleaseWakeLockIfNeeded); } } + + boolean dreamIsFrontmost() { + return !mDreamIsObscured; + } } } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 2def5aed2478..18a9986d34ba 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -20,7 +20,7 @@ import static android.Manifest.permission.BIND_DREAM_SERVICE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.service.dreams.Flags.dreamTracksFocus; +import static android.service.dreams.Flags.dreamHandlesBeingObscured; import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID; @@ -428,7 +428,7 @@ public final class DreamManagerService extends SystemService { // Can't start dreaming if we are already dreaming and the dream has focus. If we are // dreaming but the dream does not have focus, then the dream can be brought to the // front so it does have focus. - if (isScreenOn && isDreamingInternal() && dreamHasFocus()) { + if (isScreenOn && isDreamingInternal() && dreamIsFrontmost()) { return false; } @@ -463,9 +463,10 @@ public final class DreamManagerService extends SystemService { } } - private boolean dreamHasFocus() { - // Dreams always had focus before they were able to track it. - return !dreamTracksFocus() || mController.dreamHasFocus(); + private boolean dreamIsFrontmost() { + // Dreams were always considered frontmost before they began tracking whether they are + // obscured. + return !dreamHandlesBeingObscured() || mController.dreamIsFrontmost(); } protected void requestStartDreamFromShell() { @@ -473,7 +474,7 @@ public final class DreamManagerService extends SystemService { } private void requestDreamInternal() { - if (isDreamingInternal() && !dreamHasFocus() && mController.bringDreamToFront()) { + if (isDreamingInternal() && !dreamIsFrontmost() && mController.bringDreamToFront()) { return; } @@ -1159,10 +1160,16 @@ public final class DreamManagerService extends SystemService { } @Override - public void onDreamFocusChanged(boolean hasFocus) { + public void setDreamIsObscured(boolean isObscured) { + if (!dreamHandlesBeingObscured()) { + return; + } + + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + final long ident = Binder.clearCallingIdentity(); try { - mController.setDreamHasFocus(hasFocus); + mHandler.post(() -> mController.setDreamIsObscured(isObscured)); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index d21fc85d6448..5db17bb90637 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -29,7 +29,6 @@ import android.os.Binder; import android.os.Handler; import android.os.PowerManager; import android.os.SystemProperties; -import android.os.UserHandle; import android.sysprop.HdmiProperties; import android.util.Slog; @@ -278,8 +277,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { void dismissUiOnActiveSourceStatusRecovered() { assertRunOnServiceThread(); Intent intent = new Intent(HdmiControlManager.ACTION_ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI); - mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL, - HdmiControlService.PERMISSION); + mService.sendBroadcastAsUser(intent); } @Override diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index d2d0279e768e..936e8b6a28b6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -40,6 +40,7 @@ import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -4392,8 +4393,7 @@ public class HdmiControlService extends SystemService { assertRunOnServiceThread(); Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE); intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId); - getContext().sendBroadcastAsUser(intent, UserHandle.ALL, - HdmiControlService.PERMISSION); + sendBroadcastAsUser(intent); } @ServiceThreadOnly @@ -4402,8 +4402,17 @@ public class HdmiControlService extends SystemService { Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE); intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId); intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra); - getContext().sendBroadcastAsUser(intent, UserHandle.ALL, - HdmiControlService.PERMISSION); + sendBroadcastAsUser(intent); + } + + // This method is used such that we can override it inside unit tests to avoid a + // SecurityException. + @ServiceThreadOnly + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + assertRunOnServiceThread(); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL, HdmiControlService.PERMISSION); } @VisibleForTesting diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index b47631c35e38..d32a5ed60094 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -218,4 +218,13 @@ public abstract class InputManagerInternal { * display, external peripherals, fingerprint sensor, etc. */ public abstract void notifyUserActivity(); + + /** + * Get the device ID of the {@link InputDevice} that used most recently. + * + * @return the last used input device ID, or + * {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used + * since boot. + */ + public abstract int getLastUsedInputDeviceId(); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 83179914c746..8685d2c45762 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3204,6 +3204,11 @@ public class InputManagerService extends IInputManager.Stub public void setStylusButtonMotionEventsEnabled(boolean enabled) { mNative.setStylusButtonMotionEventsEnabled(enabled); } + + @Override + public int getLastUsedInputDeviceId() { + return mNative.getLastUsedInputDeviceId(); + } } @Override diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index f742360484f5..0208a325a1d5 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -271,6 +271,15 @@ interface NativeInputManagerService { void setInputMethodConnectionIsActive(boolean isActive); + /** + * Get the device ID of the InputDevice that used most recently. + * + * @return the last used input device ID, or + * {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used + * since boot. + */ + int getLastUsedInputDeviceId(); + /** The native implementation of InputManagerService methods. */ class NativeImpl implements NativeInputManagerService { /** Pointer to native input manager service object, used by native code. */ @@ -544,5 +553,8 @@ interface NativeInputManagerService { @Override public native void setInputMethodConnectionIsActive(boolean isActive); + + @Override + public native int getLastUsedInputDeviceId(); } } diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java index 00bc7517bebd..ad98b4a8db13 100644 --- a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java +++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java @@ -29,6 +29,7 @@ import android.view.inputmethod.InputMethodInfo; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback; +import com.android.internal.inputmethod.InlineSuggestionsRequestCallback; import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; /** @@ -49,12 +50,12 @@ final class AutofillSuggestionsController { private static final class CreateInlineSuggestionsRequest { @NonNull final InlineSuggestionsRequestInfo mRequestInfo; - @NonNull final IInlineSuggestionsRequestCallback mCallback; + @NonNull final InlineSuggestionsRequestCallback mCallback; @NonNull final String mPackageName; CreateInlineSuggestionsRequest( @NonNull InlineSuggestionsRequestInfo requestInfo, - @NonNull IInlineSuggestionsRequestCallback callback, + @NonNull InlineSuggestionsRequestCallback callback, @NonNull String packageName) { mRequestInfo = requestInfo; mCallback = callback; @@ -78,7 +79,7 @@ final class AutofillSuggestionsController { */ @GuardedBy("ImfLock.class") @Nullable - private IInlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback; + private InlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback; AutofillSuggestionsController(@NonNull InputMethodManagerService service) { mService = service; @@ -97,33 +98,30 @@ final class AutofillSuggestionsController { @GuardedBy("ImfLock.class") void onCreateInlineSuggestionsRequest(@UserIdInt int userId, - InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback, + InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback callback, boolean touchExplorationEnabled) { clearPendingInlineSuggestionsRequest(); mInlineSuggestionsRequestCallback = callback; final InputMethodInfo imi = mService.queryInputMethodForCurrentUserLocked( mService.getSelectedMethodIdLocked()); - try { - if (userId == mService.getCurrentImeUserIdLocked() - && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) { - mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest( - requestInfo, callback, imi.getPackageName()); - if (mService.getCurMethodLocked() != null) { - // In the normal case when the IME is connected, we can make the request here. - performOnCreateInlineSuggestionsRequest(); - } else { - // Otherwise, the next time the IME connection is established, - // InputMethodBindingController.mMainConnection#onServiceConnected() will call - // into #performOnCreateInlineSuggestionsRequestLocked() to make the request. - if (DEBUG) { - Slog.d(TAG, "IME not connected. Delaying inline suggestions request."); - } - } + + if (userId == mService.getCurrentImeUserIdLocked() + && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) { + mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest( + requestInfo, callback, imi.getPackageName()); + if (mService.getCurMethodLocked() != null) { + // In the normal case when the IME is connected, we can make the request here. + performOnCreateInlineSuggestionsRequest(); } else { - callback.onInlineSuggestionsUnsupported(); + // Otherwise, the next time the IME connection is established, + // InputMethodBindingController.mMainConnection#onServiceConnected() will call + // into #performOnCreateInlineSuggestionsRequestLocked() to make the request. + if (DEBUG) { + Slog.d(TAG, "IME not connected. Delaying inline suggestions request."); + } } - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e); + } else { + callback.onInlineSuggestionsUnsupported(); } } @@ -166,11 +164,7 @@ final class AutofillSuggestionsController { @GuardedBy("ImfLock.class") void invalidateAutofillSession() { if (mInlineSuggestionsRequestCallback != null) { - try { - mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated(); - } catch (RemoteException e) { - Slog.e(TAG, "Cannot invalidate autofill session.", e); - } + mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated(); } } @@ -180,13 +174,13 @@ final class AutofillSuggestionsController { */ private final class InlineSuggestionsRequestCallbackDecorator extends IInlineSuggestionsRequestCallback.Stub { - @NonNull private final IInlineSuggestionsRequestCallback mCallback; + @NonNull private final InlineSuggestionsRequestCallback mCallback; @NonNull private final String mImePackageName; private final int mImeDisplayId; @NonNull private final IBinder mImeToken; InlineSuggestionsRequestCallbackDecorator( - @NonNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName, + @NonNull InlineSuggestionsRequestCallback callback, @NonNull String imePackageName, int displayId, @NonNull IBinder imeToken) { mCallback = callback; mImePackageName = imePackageName; @@ -195,7 +189,7 @@ final class AutofillSuggestionsController { } @Override - public void onInlineSuggestionsUnsupported() throws RemoteException { + public void onInlineSuggestionsUnsupported() { mCallback.onInlineSuggestionsUnsupported(); } @@ -220,32 +214,32 @@ final class AutofillSuggestionsController { } @Override - public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException { + public void onInputMethodStartInput(AutofillId imeFieldId) { mCallback.onInputMethodStartInput(imeFieldId); } @Override - public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException { + public void onInputMethodShowInputRequested(boolean requestResult) { mCallback.onInputMethodShowInputRequested(requestResult); } @Override - public void onInputMethodStartInputView() throws RemoteException { + public void onInputMethodStartInputView() { mCallback.onInputMethodStartInputView(); } @Override - public void onInputMethodFinishInputView() throws RemoteException { + public void onInputMethodFinishInputView() { mCallback.onInputMethodFinishInputView(); } @Override - public void onInputMethodFinishInput() throws RemoteException { + public void onInputMethodFinishInput() { mCallback.onInputMethodFinishInput(); } @Override - public void onInlineSuggestionsSessionInvalidated() throws RemoteException { + public void onInlineSuggestionsSessionInvalidated() { mCallback.onInlineSuggestionsSessionInvalidated(); } } diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java index 4c20c3b9784a..f78ea84efb77 100644 --- a/services/core/java/com/android/server/inputmethod/ImeBindingState.java +++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java @@ -21,7 +21,9 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCU import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.IBinder; +import android.os.UserHandle; import android.util.Printer; import android.util.proto.ProtoOutputStream; import android.view.WindowManager; @@ -36,6 +38,9 @@ import com.android.server.wm.WindowManagerInternal; */ final class ImeBindingState { + @UserIdInt + final int mUserId; + /** * The last window token that we confirmed to be focused. This is always updated upon * reports from the input method client. If the window state is already changed before the @@ -90,6 +95,7 @@ final class ImeBindingState { static ImeBindingState newEmptyState() { return new ImeBindingState( + /*userId=*/ UserHandle.USER_NULL, /*focusedWindow=*/ null, /*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED, /*focusedWindowClient=*/ null, @@ -97,10 +103,12 @@ final class ImeBindingState { ); } - ImeBindingState(@Nullable IBinder focusedWindow, + ImeBindingState(@UserIdInt int userId, + @Nullable IBinder focusedWindow, @SoftInputModeFlags int focusedWindowSoftInputMode, @Nullable ClientState focusedWindowClient, @Nullable EditorInfo focusedWindowEditorInfo) { + mUserId = userId; mFocusedWindow = focusedWindow; mFocusedWindowSoftInputMode = focusedWindowSoftInputMode; mFocusedWindowClient = focusedWindowClient; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index e8543f225ef0..dace67f2c462 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -25,7 +25,7 @@ import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InputMethodInfo; import com.android.internal.inputmethod.IAccessibilityInputMethodSession; -import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; +import com.android.internal.inputmethod.InlineSuggestionsRequestCallback; import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.server.LocalServices; @@ -86,11 +86,11 @@ public abstract class InputMethodManagerInternal { * * @param userId the user ID to be queried * @param requestInfo information needed to create an {@link InlineSuggestionsRequest}. - * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request + * @param cb {@link InlineSuggestionsRequestCallback} used to pass back the request * object */ public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId, - InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb); + InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb); /** * Force switch to the enabled input method by {@code imeId} for current user. If the input @@ -263,7 +263,7 @@ public abstract class InputMethodManagerInternal { @Override public void onCreateInlineSuggestionsRequest(@UserIdInt int userId, InlineSuggestionsRequestInfo requestInfo, - IInlineSuggestionsRequestCallback cb) { + InlineSuggestionsRequestCallback cb) { } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 67df99279242..954f9bcd9c43 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -147,7 +147,6 @@ import com.android.internal.inputmethod.IAccessibilityInputMethodSession; import com.android.internal.inputmethod.IBooleanListener; import com.android.internal.inputmethod.IConnectionlessHandwritingCallback; import com.android.internal.inputmethod.IImeTracker; -import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethod; import com.android.internal.inputmethod.IInputMethodClient; @@ -157,6 +156,7 @@ import com.android.internal.inputmethod.IInputMethodSessionCallback; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.IRemoteInputConnection; import com.android.internal.inputmethod.ImeTracing; +import com.android.internal.inputmethod.InlineSuggestionsRequestCallback; import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; import com.android.internal.inputmethod.InputBindResult; import com.android.internal.inputmethod.InputMethodDebug; @@ -533,21 +533,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. EditorInfo mCurEditorInfo; /** - * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently - * connected to or in the process of connecting to. - * - * <p>This can be {@code null} when no input method is connected.</p> - * - * @see #getSelectedMethodIdLocked() - */ - @GuardedBy("ImfLock.class") - @Nullable - private String getCurIdLocked() { - final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - return userData.mBindingController.getCurId(); - } - - /** * The current subtype of the current input method. */ @MultiUserUnawareField @@ -577,16 +562,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. boolean mInFullscreenMode; /** - * The Intent used to connect to the current input method. - */ - @GuardedBy("ImfLock.class") - @Nullable - private Intent getCurIntentLocked() { - final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - return userData.mBindingController.getCurIntent(); - } - - /** * The token we have made for the currently active input method, to * identify it in the future. */ @@ -632,15 +607,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } /** - * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntentLocked()}. - */ - @GuardedBy("ImfLock.class") - private int getCurMethodUidLocked() { - final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); - return userData.mBindingController.getCurMethodUid(); - } - - /** * Have we called mCurMethod.bindInput()? */ @MultiUserUnawareField @@ -1058,8 +1024,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } private void onFinishPackageChangesInternal() { + final int userId = getChangingUserId(); + + // Instantiating InputMethodInfo requires disk I/O. + // Do them before acquiring the lock to minimize the chances of ANR (b/340221861). + final var newMethodMapWithoutAdditionalSubtypes = + queryInputMethodServicesInternal(mContext, userId, + AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO) + .getMethodMap(); + synchronized (ImfLock.class) { - final int userId = getChangingUserId(); final boolean isCurrentUser = (userId == mCurrentUserId); final AdditionalSubtypeMap additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId); @@ -1111,9 +1085,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. && !(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) { return; } - - final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext, - userId, newAdditionalSubtypeMap, DirectBootAwareness.AUTO); + final var newMethodMap = newMethodMapWithoutAdditionalSubtypes + .applyAdditionalSubtypes(newAdditionalSubtypeMap); + final InputMethodSettings newSettings = + InputMethodSettings.create(newMethodMap, userId); InputMethodSettingsRepository.put(userId, newSettings); if (!isCurrentUser) { return; @@ -2011,8 +1986,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); final StartInputInfo info = new StartInputInfo(mCurrentUserId, getCurTokenLocked(), - mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting, - UserHandle.getUserId(mCurClient.mUid), + mCurTokenDisplayId, userData.mBindingController.getCurId(), startInputReason, + restarting, UserHandle.getUserId(mCurClient.mUid), mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo, mImeBindingState.mFocusedWindowSoftInputMode, userData.mBindingController.getSequenceNumber()); @@ -2026,8 +2001,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case. if (mCurrentUserId == UserHandle.getUserId( mCurClient.mUid)) { - mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, - null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()), + mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */, + UserHandle.getAppId(userData.mBindingController.getCurMethodUid()), mCurClient.mUid, true /* direct */); } @@ -2048,7 +2023,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); } - final var curId = getCurIdLocked(); + final var curId = userData.mBindingController.getCurId(); final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId) .getMethodMap().get(curId); final boolean suppressesSpellChecker = @@ -2337,7 +2312,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. requestClientSessionForAccessibilityLocked(cs); return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION, - null, null, null, getCurIdLocked(), + null, null, null, + userData.mBindingController.getCurId(), userData.mBindingController.getSequenceNumber(), false); } else { final long lastBindTime = userData.mBindingController.getLastBindTime(); @@ -2352,7 +2328,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // to see if we can get back in touch with the service. return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING, - null, null, null, getCurIdLocked(), + null, null, null, + userData.mBindingController.getCurId(), userData.mBindingController.getSequenceNumber(), false); } else { EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, @@ -2707,7 +2684,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // When the IME switcher dialog is shown, the IME switcher button should be hidden. if (mMenuController.getSwitchingDialogLocked() != null) return false; // When we are switching IMEs, the IME switcher button should be hidden. - if (!Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) { + final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + if (!Objects.equals(userData.mBindingController.getCurId(), getSelectedMethodIdLocked())) { return false; } if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded() @@ -2869,8 +2847,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } else { vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE; } + final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var curId = userData.mBindingController.getCurId(); if (mMenuController.getSwitchingDialogLocked() != null - || !Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) { + || !Objects.equals(curId, getSelectedMethodIdLocked())) { // When the IME switcher dialog is shown, or we are switching IMEs, // the back button should be in the default state (as if the IME is not shown). backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING; @@ -3741,7 +3721,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. null, null, null, null, -1, false); } - mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo); + mImeBindingState = new ImeBindingState(userData.mUserId, windowToken, softInputMode, cs, + editorInfo); mFocusedWindowPerceptible.put(windowToken, true); // We want to start input before showing the IME, but after closing @@ -3831,10 +3812,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mCurrentUserId != UserHandle.getUserId(uid)) { return false; } - if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid( - mPackageManagerInternal, - uid, - getCurIntentLocked().getComponent().getPackageName())) { + final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + final var curIntent = userData.mBindingController.getCurIntent(); + if (curIntent != null && InputMethodUtils.checkIfPackageBelongsToUid( + mPackageManagerInternal, uid, curIntent.getComponent().getPackageName())) { return true; } return false; @@ -4438,7 +4419,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mCurEditorInfo != null) { mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE); } - proto.write(CUR_ID, getCurIdLocked()); + proto.write(CUR_ID, userData.mBindingController.getCurId()); mVisibilityStateComputer.dumpDebug(proto, fieldId); proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode); proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked())); @@ -4895,7 +4876,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mCurClient == null || mCurClient.mClient == null) { return; } - if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) { + // TODO(b/325515685): user data must be retrieved by a userId parameter + final var userData = mUserDataRepository.getOrCreate(mCurrentUserId); + if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol( + userData.mBindingController.getCurMethodUid())) { // Handle IME visibility when interactive changed before finishing the input to // ensure we preserve the last state as possible. final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged( @@ -5502,7 +5486,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void onCreateInlineSuggestionsRequest(@UserIdInt int userId, - InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) { + InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb) { // Get the device global touch exploration state before lock to avoid deadlock. final boolean touchExplorationEnabled = AccessibilityManagerInternal.get() .isTouchExplorationEnabled(userId); @@ -5648,7 +5632,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final InputBindResult res = new InputBindResult( InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION, imeSession, accessibilityInputMethodSessions, /* channel= */ null, - getCurIdLocked(), + userData.mBindingController.getCurId(), userData.mBindingController.getSequenceNumber(), /* isInputMethodSuppressingSpellChecker= */ false); mCurClient.mClient.onBindAccessibilityService(res, accessibilityConnectionId); @@ -5901,7 +5885,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. p.println(" mFocusedWindowPerceptible=" + mFocusedWindowPerceptible); mImeBindingState.dump(/* prefix= */ " ", p); - p.println(" mCurId=" + getCurIdLocked() + p.println(" mCurId=" + userData.mBindingController.getCurId() + " mHaveConnection=" + userData.mBindingController.hasMainConnection() + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + userData.mBindingController.isVisibleBound()); @@ -5920,7 +5904,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. p.println(" mCurToken=" + getCurTokenLocked()); p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId); p.println(" mCurHostInputToken=" + mAutofillController.getCurHostInputToken()); - p.println(" mCurIntent=" + getCurIntentLocked()); + p.println(" mCurIntent=" + userData.mBindingController.getCurIntent()); method = getCurMethodLocked(); p.println(" mCurMethod=" + getCurMethodLocked()); p.println(" mEnabledSession=" + mEnabledSession); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMap.java b/services/core/java/com/android/server/inputmethod/InputMethodMap.java index a8e5e2ef4f72..221309e4bf3e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMap.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMap.java @@ -75,4 +75,28 @@ final class InputMethodMap { int size() { return mMap.size(); } + + @AnyThread + @NonNull + public InputMethodMap applyAdditionalSubtypes( + @NonNull AdditionalSubtypeMap additionalSubtypeMap) { + if (additionalSubtypeMap.isEmpty()) { + return this; + } + final int size = size(); + final ArrayMap<String, InputMethodInfo> newMethodMap = new ArrayMap<>(size); + boolean updated = false; + for (int i = 0; i < size; ++i) { + final var imi = valueAt(i); + final var imeId = imi.getId(); + final var newAdditionalSubtypes = additionalSubtypeMap.get(imeId); + if (newAdditionalSubtypes == null || newAdditionalSubtypes.isEmpty()) { + newMethodMap.put(imi.getId(), imi); + } else { + newMethodMap.put(imi.getId(), new InputMethodInfo(imi, newAdditionalSubtypes)); + updated = true; + } + } + return updated ? InputMethodMap.of(newMethodMap) : this; + } } diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java index a0dbfa082978..ec94e2be2c59 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java @@ -412,10 +412,15 @@ import java.util.concurrent.atomic.AtomicInteger; /* package */ synchronized void addTransaction( ContextHubServiceTransaction transaction) throws IllegalStateException { + if (transaction == null) { + return; + } + if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) { throw new IllegalStateException("Transaction queue is full (capacity = " + MAX_PENDING_REQUESTS + ")"); } + mTransactionQueue.add(transaction); mTransactionRecordDeque.add(new TransactionRecord(transaction.toString())); @@ -517,7 +522,10 @@ import java.util.concurrent.atomic.AtomicInteger; * the caller has obtained a lock on this ContextHubTransactionManager object. */ private void removeTransactionAndStartNext() { - mTimeoutFuture.cancel(false /* mayInterruptIfRunning */); + if (mTimeoutFuture != null) { + mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false); + mTimeoutFuture = null; + } ContextHubServiceTransaction transaction = mTransactionQueue.remove(); transaction.setComplete(); diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java index d25f52973085..5ea3e70f7957 100644 --- a/services/core/java/com/android/server/net/NetworkManagementService.java +++ b/services/core/java/com/android/server/net/NetworkManagementService.java @@ -20,6 +20,9 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; @@ -31,6 +34,9 @@ import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; @@ -143,6 +149,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { private final Object mQuotaLock = new Object(); private final Object mRulesLock = new Object(); + private final boolean mUseMeteredFirewallChains; + /** Set of interfaces with active quotas. */ @GuardedBy("mQuotaLock") private HashMap<String, Long> mActiveQuotas = Maps.newHashMap(); @@ -150,9 +158,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @GuardedBy("mQuotaLock") private HashMap<String, Long> mActiveAlerts = Maps.newHashMap(); /** Set of UIDs denied on metered networks. */ + // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains. @GuardedBy("mRulesLock") private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray(); /** Set of UIDs allowed on metered networks. */ + // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains. @GuardedBy("mRulesLock") private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray(); /** Set of UIDs with cleartext penalties. */ @@ -196,10 +206,32 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @GuardedBy("mRulesLock") private final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray(); + /** + * Contains the per-UID firewall rules that are used to allowlist the app from metered-network + * restrictions when data saver is enabled. + */ + @GuardedBy("mRulesLock") + private final SparseIntArray mUidMeteredFirewallAllowRules = new SparseIntArray(); + + /** + * Contains the per-UID firewall rules that are used to deny app access to metered networks + * due to user action. + */ + @GuardedBy("mRulesLock") + private final SparseIntArray mUidMeteredFirewallDenyUserRules = new SparseIntArray(); + + /** + * Contains the per-UID firewall rules that are used to deny app access to metered networks + * due to admin action. + */ + @GuardedBy("mRulesLock") + private final SparseIntArray mUidMeteredFirewallDenyAdminRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mRulesLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); + // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains. @GuardedBy("mQuotaLock") private volatile boolean mDataSaverMode; @@ -217,6 +249,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub { mContext = context; mDeps = deps; + mUseMeteredFirewallChains = Flags.useMeteredFirewallChains(); + + if (mUseMeteredFirewallChains) { + // These firewalls are always on and currently ConnectivityService does not allow + // changing their enabled state. + mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_USER, true); + mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_ADMIN, true); + } + mDaemonHandler = new Handler(FgThread.get().getLooper()); mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener(); @@ -410,33 +451,39 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } } - SparseBooleanArray uidRejectOnQuota = null; - SparseBooleanArray uidAcceptOnQuota = null; - synchronized (mRulesLock) { - size = mUidRejectOnMetered.size(); - if (size > 0) { - if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules"); - uidRejectOnQuota = mUidRejectOnMetered; - mUidRejectOnMetered = new SparseBooleanArray(); - } + if (!mUseMeteredFirewallChains) { + SparseBooleanArray uidRejectOnQuota = null; + SparseBooleanArray uidAcceptOnQuota = null; + synchronized (mRulesLock) { + size = mUidRejectOnMetered.size(); + if (size > 0) { + if (DBG) { + Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules"); + } + uidRejectOnQuota = mUidRejectOnMetered; + mUidRejectOnMetered = new SparseBooleanArray(); + } - size = mUidAllowOnMetered.size(); - if (size > 0) { - if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules"); - uidAcceptOnQuota = mUidAllowOnMetered; - mUidAllowOnMetered = new SparseBooleanArray(); + size = mUidAllowOnMetered.size(); + if (size > 0) { + if (DBG) { + Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules"); + } + uidAcceptOnQuota = mUidAllowOnMetered; + mUidAllowOnMetered = new SparseBooleanArray(); + } } - } - if (uidRejectOnQuota != null) { - for (int i = 0; i < uidRejectOnQuota.size(); i++) { - setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i), - uidRejectOnQuota.valueAt(i)); + if (uidRejectOnQuota != null) { + for (int i = 0; i < uidRejectOnQuota.size(); i++) { + setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i), + uidRejectOnQuota.valueAt(i)); + } } - } - if (uidAcceptOnQuota != null) { - for (int i = 0; i < uidAcceptOnQuota.size(); i++) { - setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i), - uidAcceptOnQuota.valueAt(i)); + if (uidAcceptOnQuota != null) { + for (int i = 0; i < uidAcceptOnQuota.size(); i++) { + setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i), + uidAcceptOnQuota.valueAt(i)); + } } } @@ -459,8 +506,16 @@ public class NetworkManagementService extends INetworkManagementService.Stub { syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted "); syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby "); syncFirewallChainLocked(FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_NAME_BACKGROUND); + if (mUseMeteredFirewallChains) { + syncFirewallChainLocked(FIREWALL_CHAIN_METERED_ALLOW, + FIREWALL_CHAIN_NAME_METERED_ALLOW); + syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_USER, + FIREWALL_CHAIN_NAME_METERED_DENY_USER); + syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_ADMIN, + FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN); + } - final int[] chains = { + final int[] chainsToEnable = { FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE, @@ -469,14 +524,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub { FIREWALL_CHAIN_BACKGROUND, }; - for (int chain : chains) { + for (int chain : chainsToEnable) { if (getFirewallChainState(chain)) { setFirewallChainEnabled(chain, true); } } } - try { getBatteryStats().noteNetworkStatsEnabled(); } catch (RemoteException e) { @@ -1077,6 +1131,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub { mContext.getSystemService(ConnectivityManager.class) .setDataSaverEnabled(enable); mDataSaverMode = enable; + if (mUseMeteredFirewallChains) { + // Copy mDataSaverMode state to FIREWALL_CHAIN_METERED_ALLOW + // until ConnectivityService allows manipulation of the data saver mode via + // FIREWALL_CHAIN_METERED_ALLOW. + synchronized (mRulesLock) { + mFirewallChainStates.put(FIREWALL_CHAIN_METERED_ALLOW, enable); + } + } return true; } else { final boolean changed = mNetdService.bandwidthEnableDataSaver(enable); @@ -1191,9 +1253,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { setFirewallChainState(chain, enable); } - final String chainName = getFirewallChainName(chain); - if (chain == FIREWALL_CHAIN_NONE) { - throw new IllegalArgumentException("Bad child chain: " + chainName); + if (!isValidFirewallChainForSetEnabled(chain)) { + throw new IllegalArgumentException("Invalid chain for setFirewallChainEnabled: " + + NetworkPolicyLogger.getFirewallChainName(chain)); } final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); @@ -1205,38 +1267,29 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } } - private String getFirewallChainName(int chain) { - switch (chain) { - case FIREWALL_CHAIN_STANDBY: - return FIREWALL_CHAIN_NAME_STANDBY; - case FIREWALL_CHAIN_DOZABLE: - return FIREWALL_CHAIN_NAME_DOZABLE; - case FIREWALL_CHAIN_POWERSAVE: - return FIREWALL_CHAIN_NAME_POWERSAVE; - case FIREWALL_CHAIN_RESTRICTED: - return FIREWALL_CHAIN_NAME_RESTRICTED; - case FIREWALL_CHAIN_LOW_POWER_STANDBY: - return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; - case FIREWALL_CHAIN_BACKGROUND: - return FIREWALL_CHAIN_NAME_BACKGROUND; - default: - throw new IllegalArgumentException("Bad child chain: " + chain); - } + private boolean isValidFirewallChainForSetEnabled(int chain) { + return switch (chain) { + case FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE, + FIREWALL_CHAIN_RESTRICTED, FIREWALL_CHAIN_LOW_POWER_STANDBY, + FIREWALL_CHAIN_BACKGROUND -> true; + // METERED_* firewall chains are not yet supported by + // ConnectivityService#setFirewallChainEnabled. + default -> false; + }; } private int getFirewallType(int chain) { switch (chain) { case FIREWALL_CHAIN_STANDBY: + case FIREWALL_CHAIN_METERED_DENY_ADMIN: + case FIREWALL_CHAIN_METERED_DENY_USER: return FIREWALL_DENYLIST; case FIREWALL_CHAIN_DOZABLE: - return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_POWERSAVE: - return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_RESTRICTED: - return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_LOW_POWER_STANDBY: - return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_BACKGROUND: + case FIREWALL_CHAIN_METERED_ALLOW: return FIREWALL_ALLOWLIST; default: return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST; @@ -1360,6 +1413,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return mUidFirewallLowPowerStandbyRules; case FIREWALL_CHAIN_BACKGROUND: return mUidFirewallBackgroundRules; + case FIREWALL_CHAIN_METERED_ALLOW: + return mUidMeteredFirewallAllowRules; + case FIREWALL_CHAIN_METERED_DENY_USER: + return mUidMeteredFirewallDenyUserRules; + case FIREWALL_CHAIN_METERED_DENY_ADMIN: + return mUidMeteredFirewallDenyAdminRules; case FIREWALL_CHAIN_NONE: return mUidFirewallRules; default: @@ -1378,6 +1437,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + pw.println("Flags:"); + pw.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains); + pw.println(); + synchronized (mQuotaLock) { pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString()); pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString()); @@ -1416,6 +1479,27 @@ public class NetworkManagementService extends INetworkManagementService.Stub { pw.print("UID firewall background chain enabled: "); pw.println(getFirewallChainState(FIREWALL_CHAIN_BACKGROUND)); dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_BACKGROUND, mUidFirewallBackgroundRules); + + pw.print("UID firewall metered allow chain enabled (Data saver mode): "); + // getFirewallChainState should maintain a duplicated state from mDataSaverMode when + // mUseMeteredFirewallChains is enabled. + pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW)); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_ALLOW, + mUidMeteredFirewallAllowRules); + + pw.print("UID firewall metered deny_user chain enabled (always-on): "); + // This always-on state should be reflected by getFirewallChainState when + // mUseMeteredFirewallChains is enabled. + pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER)); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_USER, + mUidMeteredFirewallDenyUserRules); + + pw.print("UID firewall metered deny_admin chain enabled (always-on): "); + // This always-on state should be reflected by getFirewallChainState when + // mUseMeteredFirewallChains is enabled. + pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN)); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN, + mUidMeteredFirewallDenyAdminRules); } pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); @@ -1520,14 +1604,40 @@ public class NetworkManagementService extends INetworkManagementService.Stub { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because it is in background"); return true; } - if (mUidRejectOnMetered.get(uid)) { - if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data" - + " in the background"); - return true; - } - if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) { - if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode"); - return true; + if (mUseMeteredFirewallChains) { + if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER) + && mUidMeteredFirewallDenyUserRules.get(uid) == FIREWALL_RULE_DENY) { + if (DBG) { + Slog.d(TAG, "Uid " + uid + " restricted because of user-restricted metered" + + " data in the background"); + } + return true; + } + if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN) + && mUidMeteredFirewallDenyAdminRules.get(uid) == FIREWALL_RULE_DENY) { + if (DBG) { + Slog.d(TAG, "Uid " + uid + " restricted because of admin-restricted metered" + + " data in the background"); + } + return true; + } + if (getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW) + && mUidMeteredFirewallAllowRules.get(uid) != FIREWALL_RULE_ALLOW) { + if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode"); + return true; + } + } else { + if (mUidRejectOnMetered.get(uid)) { + if (DBG) { + Slog.d(TAG, "Uid " + uid + + " restricted because of no metered data in the background"); + } + return true; + } + if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) { + if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode"); + return true; + } } return false; } diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 8e2d7780204a..681aa8aef219 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -19,6 +19,9 @@ import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; @@ -28,6 +31,9 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; @@ -379,7 +385,7 @@ public class NetworkPolicyLogger { return "Interfaces of netId=" + netId + " changed to " + newIfaces; } - private static String getFirewallChainName(int chain) { + static String getFirewallChainName(int chain) { switch (chain) { case FIREWALL_CHAIN_DOZABLE: return FIREWALL_CHAIN_NAME_DOZABLE; @@ -393,6 +399,12 @@ public class NetworkPolicyLogger { return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; case FIREWALL_CHAIN_BACKGROUND: return FIREWALL_CHAIN_NAME_BACKGROUND; + case FIREWALL_CHAIN_METERED_ALLOW: + return FIREWALL_CHAIN_NAME_METERED_ALLOW; + case FIREWALL_CHAIN_METERED_DENY_USER: + return FIREWALL_CHAIN_NAME_METERED_DENY_USER; + case FIREWALL_CHAIN_METERED_DENY_ADMIN: + return FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN; default: return String.valueOf(chain); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 22f5332e150c..c60ac3a74ebd 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -60,6 +60,9 @@ import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; @@ -514,6 +517,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private boolean mBackgroundNetworkRestricted; + /** + * Whether or not metered firewall chains should be used for uid policy controlling access to + * metered networks. + */ + private boolean mUseMeteredFirewallChains; + // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); final Object mNetworkPoliciesSecondLock = new Object(); @@ -997,6 +1006,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mAppStandby = LocalServices.getService(AppStandbyInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + mUseMeteredFirewallChains = Flags.useMeteredFirewallChains(); + synchronized (mUidRulesFirstLock) { synchronized (mNetworkPoliciesSecondLock) { updatePowerSaveAllowlistUL(); @@ -4030,8 +4041,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.println(); fout.println("Flags:"); - fout.println("Network blocked for TOP_SLEEPING and above: " + fout.println(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE + ": " + mBackgroundNetworkRestricted); + fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + + mUseMeteredFirewallChains); fout.println(); fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); @@ -5373,23 +5386,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { postUidRulesChangedMsg(uid, uidRules); } - // Note that the conditionals below are for avoiding unnecessary calls to netd. - // TODO: Measure the performance for doing a no-op call to netd so that we can - // remove the conditionals to simplify the logic below. We can also further reduce - // some calls to netd if they turn out to be costly. - final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED - | BLOCKED_METERED_REASON_USER_RESTRICTED; - if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE - || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) { - setMeteredNetworkDenylist(uid, - (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE); - } - final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND - | ALLOWED_METERED_REASON_USER_EXEMPTED; - if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE - || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) { - setMeteredNetworkAllowlist(uid, - (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE); + if (mUseMeteredFirewallChains) { + if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_ADMIN_DISABLED) + != BLOCKED_REASON_NONE) { + setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DENY); + } else { + setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DEFAULT); + } + if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_USER_RESTRICTED) + != BLOCKED_REASON_NONE) { + setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DENY); + } else { + setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DEFAULT); + } + if ((newAllowedReasons & (ALLOWED_METERED_REASON_FOREGROUND + | ALLOWED_METERED_REASON_USER_EXEMPTED)) != ALLOWED_REASON_NONE) { + setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_ALLOW); + } else { + setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_DEFAULT); + } + } else { + // Note that the conditionals below are for avoiding unnecessary calls to netd. + // TODO: Measure the performance for doing a no-op call to netd so that we can + // remove the conditionals to simplify the logic below. We can also further reduce + // some calls to netd if they turn out to be costly. + final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED + | BLOCKED_METERED_REASON_USER_RESTRICTED; + if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE + || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) { + setMeteredNetworkDenylist(uid, + (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE); + } + final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND + | ALLOWED_METERED_REASON_USER_EXEMPTED; + if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE + || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) { + setMeteredNetworkAllowlist(uid, + (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE); + } } } @@ -6149,6 +6183,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } else if (chain == FIREWALL_CHAIN_BACKGROUND) { mUidFirewallBackgroundRules.put(uid, rule); } + // Note that we do not need keep a separate cache of uid rules for chains that we do + // not call #setUidFirewallRulesUL for. try { mNetworkManager.setFirewallUidRule(chain, uid, rule); @@ -6206,10 +6242,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { FIREWALL_RULE_DEFAULT); mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DEFAULT); - mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); - mLogger.meteredAllowlistChanged(uid, false); - mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); - mLogger.meteredDenylistChanged(uid, false); + if (mUseMeteredFirewallChains) { + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, + FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, uid, + FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, uid, + FIREWALL_RULE_DEFAULT); + } else { + mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); + mLogger.meteredAllowlistChanged(uid, false); + mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); + mLogger.meteredDenylistChanged(uid, false); + } } catch (IllegalStateException e) { Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig index d9491de52d87..e986dd81b94b 100644 --- a/services/core/java/com/android/server/net/flags.aconfig +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -7,3 +7,13 @@ flag { description: "Block network access for apps in a low importance background state" bug: "304347838" } + +flag { + name: "use_metered_firewall_chains" + namespace: "backstage_power" + description: "Use metered firewall chains to control access to metered networks" + bug: "336693007" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java index bf49671e2d82..a78b3a2e0c61 100644 --- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java +++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java @@ -135,7 +135,7 @@ public final class NotificationAttentionHelper { private LogicalLight mAttentionLight; private final boolean mUseAttentionLight; - boolean mHasLight = true; + boolean mHasLight; private final SettingsObserver mSettingsObserver; @@ -149,7 +149,7 @@ public final class NotificationAttentionHelper { private boolean mInCallStateOffHook = false; private boolean mScreenOn = true; private boolean mUserPresent = false; - boolean mNotificationPulseEnabled; + private boolean mNotificationPulseEnabled; private final Uri mInCallNotificationUri; private final AudioAttributes mInCallNotificationAudioAttributes; private final float mInCallNotificationVolume; @@ -305,6 +305,13 @@ public final class NotificationAttentionHelper { } private void loadUserSettings() { + boolean pulseEnabled = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0; + if (mNotificationPulseEnabled != pulseEnabled) { + mNotificationPulseEnabled = pulseEnabled; + updateLightsLocked(); + } + if (Flags.politeNotifications()) { try { mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser()); @@ -874,6 +881,9 @@ public final class NotificationAttentionHelper { boolean canShowLightsLocked(final NotificationRecord record, final Signals signals, boolean aboveThreshold) { + if (!mSystemReady) { + return false; + } // device lacks light if (!mHasLight) { return false; @@ -1721,8 +1731,6 @@ public final class NotificationAttentionHelper { void setLights(LogicalLight light) { mNotificationLight = light; mAttentionLight = light; - mNotificationPulseEnabled = true; - mHasLight = true; } @VisibleForTesting diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4f87c83bb0d7..4e9c91cd671d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7225,7 +7225,15 @@ public class NotificationManagerService extends SystemService { callingUid, userId, true, false, "cancelNotificationWithTag", pkg); // ensure opPkg is delegate if does not match pkg - int uid = resolveNotificationUid(opPkg, pkg, callingUid, userId); + + int uid = INVALID_UID; + + try { + uid = resolveNotificationUid(opPkg, pkg, callingUid, userId); + } catch (NameNotFoundException e) { + // package either never existed so there's no posted notification or it's being + // uninstalled so we'll be cleaning it up soon. log and return immediately below. + } if (uid == INVALID_UID) { Slog.w(TAG, opPkg + ":" + callingUid + " trying to cancel notification " @@ -7319,7 +7327,13 @@ public class NotificationManagerService extends SystemService { // Can throw a SecurityException if the calling uid doesn't have permission to post // as "pkg" - final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId); + int notificationUid = INVALID_UID; + + try { + notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId); + } catch (NameNotFoundException e) { + // not great - throw immediately below + } if (notificationUid == INVALID_UID) { throw new SecurityException("Caller " + opPkg + ":" + callingUid @@ -7876,7 +7890,8 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting - int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) { + int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) + throws NameNotFoundException { if (userId == USER_ALL) { userId = USER_SYSTEM; } @@ -7887,12 +7902,8 @@ public class NotificationManagerService extends SystemService { return callingUid; } - int targetUid = INVALID_UID; - try { - targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId); - } catch (NameNotFoundException e) { - /* ignore, handled by caller */ - } + int targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId); + // posted from app A on behalf of app B if (isCallerAndroid(callingPkg, callingUid) || mPreferencesHelper.isDelegateAllowed( diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java index 681dd0b49f4e..96ab2ccc8611 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java +++ b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java @@ -33,6 +33,7 @@ import android.graphics.Bitmap; import android.os.BadParcelableException; import android.os.Bundle; import android.os.ParcelFileDescriptor; +import android.os.Parcelable; import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.RemoteException; @@ -41,7 +42,10 @@ import android.system.ErrnoException; import android.system.Os; import android.util.Log; +import com.android.internal.infra.AndroidFuture; + import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; /** * Util methods for ensuring the Bundle passed in various methods are read-only and restricted to @@ -78,16 +82,16 @@ public class BundleUtil { if (canMarshall(obj) || obj instanceof CursorWindow) { continue; } - - if (obj instanceof ParcelFileDescriptor) { + if (obj instanceof Bundle) { + sanitizeInferenceParams((Bundle) obj); + } else if (obj instanceof ParcelFileDescriptor) { validatePfdReadOnly((ParcelFileDescriptor) obj); } else if (obj instanceof SharedMemory) { ((SharedMemory) obj).setProtect(PROT_READ); } else if (obj instanceof Bitmap) { - if (((Bitmap) obj).isMutable()) { - throw new BadParcelableException( - "Encountered a mutable Bitmap in the Bundle at key : " + key); - } + validateBitmap((Bitmap) obj); + } else if (obj instanceof Parcelable[]) { + validateParcelableArray((Parcelable[]) obj); } else { throw new BadParcelableException( "Unsupported Parcelable type encountered in the Bundle: " @@ -125,20 +129,20 @@ public class BundleUtil { continue; } - if (obj instanceof ParcelFileDescriptor) { + if (obj instanceof Bundle) { + sanitizeResponseParams((Bundle) obj); + } else if (obj instanceof ParcelFileDescriptor) { validatePfdReadOnly((ParcelFileDescriptor) obj); } else if (obj instanceof Bitmap) { - if (((Bitmap) obj).isMutable()) { - throw new BadParcelableException( - "Encountered a mutable Bitmap in the Bundle at key : " + key); - } + validateBitmap((Bitmap) obj); + } else if (obj instanceof Parcelable[]) { + validateParcelableArray((Parcelable[]) obj); } else { throw new BadParcelableException( "Unsupported Parcelable type encountered in the Bundle: " + obj.getClass().getSimpleName()); } } - Log.e(TAG, "validateResponseParams : Finished"); } /** @@ -183,7 +187,8 @@ public class BundleUtil { public static IStreamingResponseCallback wrapWithValidation( IStreamingResponseCallback streamingResponseCallback, - Executor resourceClosingExecutor) { + Executor resourceClosingExecutor, + AndroidFuture future) { return new IStreamingResponseCallback.Stub() { @Override public void onNewContent(Bundle processedResult) throws RemoteException { @@ -203,6 +208,7 @@ public class BundleUtil { streamingResponseCallback.onSuccess(resultBundle); } finally { resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle)); + future.complete(null); } } @@ -210,6 +216,7 @@ public class BundleUtil { public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams) throws RemoteException { streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams); + future.completeExceptionally(new TimeoutException()); } @Override @@ -237,7 +244,8 @@ public class BundleUtil { } public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback, - Executor resourceClosingExecutor) { + Executor resourceClosingExecutor, + AndroidFuture future) { return new IResponseCallback.Stub() { @Override public void onSuccess(Bundle resultBundle) @@ -247,6 +255,7 @@ public class BundleUtil { responseCallback.onSuccess(resultBundle); } finally { resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle)); + future.complete(null); } } @@ -254,6 +263,7 @@ public class BundleUtil { public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams) throws RemoteException { responseCallback.onFailure(errorCode, errorMessage, errorParams); + future.completeExceptionally(new TimeoutException()); } @Override @@ -280,17 +290,20 @@ public class BundleUtil { } - public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback) { + public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback, + AndroidFuture future) { return new ITokenInfoCallback.Stub() { @Override public void onSuccess(TokenInfo tokenInfo) throws RemoteException { responseCallback.onSuccess(tokenInfo); + future.complete(null); } @Override public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams) throws RemoteException { responseCallback.onFailure(errorCode, errorMessage, errorParams); + future.completeExceptionally(new TimeoutException()); } }; } @@ -310,6 +323,26 @@ public class BundleUtil { } } + private static void validateParcelableArray(Parcelable[] parcelables) { + if (parcelables.length > 0 + && parcelables[0] instanceof ParcelFileDescriptor) { + // Safe to cast + validatePfdsReadOnly(parcelables); + } else if (parcelables.length > 0 + && parcelables[0] instanceof Bitmap) { + validateBitmapsImmutable(parcelables); + } else { + throw new BadParcelableException( + "Could not cast to any known parcelable array"); + } + } + + public static void validatePfdsReadOnly(Parcelable[] pfds) { + for (Parcelable pfd : pfds) { + validatePfdReadOnly((ParcelFileDescriptor) pfd); + } + } + public static void validatePfdReadOnly(ParcelFileDescriptor pfd) { if (pfd == null) { return; @@ -326,6 +359,19 @@ public class BundleUtil { } } + private static void validateBitmap(Bitmap obj) { + if (obj.isMutable()) { + throw new BadParcelableException( + "Encountered a mutable Bitmap in the Bundle at key : " + obj); + } + } + + private static void validateBitmapsImmutable(Parcelable[] bitmaps) { + for (Parcelable bitmap : bitmaps) { + validateBitmap((Bitmap) bitmap); + } + } + public static void tryCloseResource(Bundle bundle) { if (bundle == null || bundle.isEmpty() || !bundle.hasFileDescriptors()) { return; diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java index 235e3cd7c9d2..b2e861cf2876 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java @@ -33,6 +33,7 @@ import android.annotation.RequiresPermission; import android.app.AppGlobals; import android.app.ondeviceintelligence.DownloadCallback; import android.app.ondeviceintelligence.Feature; +import android.app.ondeviceintelligence.FeatureDetails; import android.app.ondeviceintelligence.IDownloadCallback; import android.app.ondeviceintelligence.IFeatureCallback; import android.app.ondeviceintelligence.IFeatureDetailsCallback; @@ -64,6 +65,7 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.provider.Settings; import android.service.ondeviceintelligence.IOnDeviceIntelligenceService; import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService; import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback; @@ -82,13 +84,17 @@ import com.android.internal.infra.ServiceConnector; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback; import java.io.FileDescriptor; import java.io.IOException; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * This is the system service for handling calls on the @@ -135,7 +141,6 @@ public class OnDeviceIntelligenceManagerService extends SystemService { @GuardedBy("mLock") private String[] mTemporaryServiceNames; - @GuardedBy("mLock") private String[] mTemporaryBroadcastKeys; @GuardedBy("mLock") @@ -145,6 +150,8 @@ public class OnDeviceIntelligenceManagerService extends SystemService { * Handler used to reset the temporary service names. */ private Handler mTemporaryHandler; + private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper()); + public OnDeviceIntelligenceManagerService(Context context) { super(context); @@ -204,8 +211,16 @@ public class OnDeviceIntelligenceManagerService extends SystemService { return; } ensureRemoteIntelligenceServiceInitialized(); - mRemoteOnDeviceIntelligenceService.run( - service -> service.getVersion(remoteCallback)); + mRemoteOnDeviceIntelligenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + service.getVersion(new RemoteCallback( + result -> { + remoteCallback.sendResult(result); + future.complete(null); + })); + return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS); + }); } @Override @@ -225,8 +240,25 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } ensureRemoteIntelligenceServiceInitialized(); int callerUid = Binder.getCallingUid(); - mRemoteOnDeviceIntelligenceService.run( - service -> service.getFeature(callerUid, id, featureCallback)); + mRemoteOnDeviceIntelligenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + service.getFeature(callerUid, id, new IFeatureCallback.Stub() { + @Override + public void onSuccess(Feature result) throws RemoteException { + featureCallback.onSuccess(result); + future.complete(null); + } + + @Override + public void onFailure(int errorCode, String errorMessage, + PersistableBundle errorParams) throws RemoteException { + featureCallback.onFailure(errorCode, errorMessage, errorParams); + future.completeExceptionally(new TimeoutException()); + } + }); + return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS); + }); } @Override @@ -246,9 +278,29 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } ensureRemoteIntelligenceServiceInitialized(); int callerUid = Binder.getCallingUid(); - mRemoteOnDeviceIntelligenceService.run( - service -> service.listFeatures(callerUid, - listFeaturesCallback)); + mRemoteOnDeviceIntelligenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + service.listFeatures(callerUid, + new IListFeaturesCallback.Stub() { + @Override + public void onSuccess(List<Feature> result) + throws RemoteException { + listFeaturesCallback.onSuccess(result); + future.complete(null); + } + + @Override + public void onFailure(int errorCode, String errorMessage, + PersistableBundle errorParams) + throws RemoteException { + listFeaturesCallback.onFailure(errorCode, errorMessage, + errorParams); + future.completeExceptionally(new TimeoutException()); + } + }); + return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS); + }); } @Override @@ -270,9 +322,29 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } ensureRemoteIntelligenceServiceInitialized(); int callerUid = Binder.getCallingUid(); - mRemoteOnDeviceIntelligenceService.run( - service -> service.getFeatureDetails(callerUid, feature, - featureDetailsCallback)); + mRemoteOnDeviceIntelligenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + service.getFeatureDetails(callerUid, feature, + new IFeatureDetailsCallback.Stub() { + @Override + public void onSuccess(FeatureDetails result) + throws RemoteException { + future.complete(null); + featureDetailsCallback.onSuccess(result); + } + + @Override + public void onFailure(int errorCode, String errorMessage, + PersistableBundle errorParams) + throws RemoteException { + future.completeExceptionally(null); + featureDetailsCallback.onFailure(errorCode, + errorMessage, errorParams); + } + }); + return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS); + }); } @Override @@ -293,10 +365,20 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } ensureRemoteIntelligenceServiceInitialized(); int callerUid = Binder.getCallingUid(); - mRemoteOnDeviceIntelligenceService.run( - service -> service.requestFeatureDownload(callerUid, feature, - wrapCancellationFuture(cancellationSignalFuture), - downloadCallback)); + mRemoteOnDeviceIntelligenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + ListenableDownloadCallback listenableDownloadCallback = + new ListenableDownloadCallback( + downloadCallback, + mMainHandler, future, getIdleTimeoutMs()); + service.requestFeatureDownload(callerUid, feature, + wrapCancellationFuture(cancellationSignalFuture), + listenableDownloadCallback); + return future; // this future has no timeout because, actual download + // might take long, but we fail early if there is no progress callbacks. + } + ); } @@ -323,11 +405,15 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } ensureRemoteInferenceServiceInitialized(); int callerUid = Binder.getCallingUid(); - result = mRemoteInferenceService.post( - service -> service.requestTokenInfo(callerUid, feature, - request, - wrapCancellationFuture(cancellationSignalFuture), - wrapWithValidation(tokenInfoCallback))); + result = mRemoteInferenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + service.requestTokenInfo(callerUid, feature, + request, + wrapCancellationFuture(cancellationSignalFuture), + wrapWithValidation(tokenInfoCallback, future)); + return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS); + }); result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request), resourceClosingExecutor); } finally { @@ -362,13 +448,18 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } ensureRemoteInferenceServiceInitialized(); int callerUid = Binder.getCallingUid(); - result = mRemoteInferenceService.post( - service -> service.processRequest(callerUid, feature, - request, - requestType, - wrapCancellationFuture(cancellationSignalFuture), - wrapProcessingFuture(processingSignalFuture), - wrapWithValidation(responseCallback, resourceClosingExecutor))); + result = mRemoteInferenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + service.processRequest(callerUid, feature, + request, + requestType, + wrapCancellationFuture(cancellationSignalFuture), + wrapProcessingFuture(processingSignalFuture), + wrapWithValidation(responseCallback, + resourceClosingExecutor, future)); + return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS); + }); result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request), resourceClosingExecutor); } finally { @@ -402,13 +493,18 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } ensureRemoteInferenceServiceInitialized(); int callerUid = Binder.getCallingUid(); - result = mRemoteInferenceService.post( - service -> service.processRequestStreaming(callerUid, - feature, - request, requestType, - wrapCancellationFuture(cancellationSignalFuture), - wrapProcessingFuture(processingSignalFuture), - streamingCallback)); + result = mRemoteInferenceService.postAsync( + service -> { + AndroidFuture future = new AndroidFuture(); + service.processRequestStreaming(callerUid, + feature, + request, requestType, + wrapCancellationFuture(cancellationSignalFuture), + wrapProcessingFuture(processingSignalFuture), + wrapWithValidation(streamingCallback, + resourceClosingExecutor, future)); + return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS); + }); result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request), resourceClosingExecutor); } finally { @@ -859,4 +955,10 @@ public class OnDeviceIntelligenceManagerService extends SystemService { return mTemporaryHandler; } + + private long getIdleTimeoutMs() { + return Settings.Secure.getLongForUser(mContext.getContentResolver(), + Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1), + mContext.getUserId()); + } } diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java index 48258d7bea72..ac9747aa83b3 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java +++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java @@ -22,17 +22,21 @@ import static android.content.Context.BIND_INCLUDE_CAPABILITIES; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.provider.Settings; import android.service.ondeviceintelligence.IOnDeviceIntelligenceService; import android.service.ondeviceintelligence.OnDeviceIntelligenceService; import com.android.internal.infra.ServiceConnector; +import java.util.concurrent.TimeUnit; + /** * Manages the connection to the remote on-device intelligence service. Also, handles unbinding * logic set by the service implementation via a Secure Settings flag. */ public class RemoteOnDeviceIntelligenceService extends ServiceConnector.Impl<IOnDeviceIntelligenceService> { + private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(4); private static final String TAG = RemoteOnDeviceIntelligenceService.class.getSimpleName(); @@ -48,9 +52,15 @@ public class RemoteOnDeviceIntelligenceService extends } @Override + protected long getRequestTimeoutMs() { + return LONG_TIMEOUT; + } + + @Override protected long getAutoDisconnectTimeoutMs() { - // Disable automatic unbinding. - // TODO: add logic to fetch this flag via SecureSettings. - return -1; + return Settings.Secure.getLongForUser(mContext.getContentResolver(), + Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, + TimeUnit.SECONDS.toMillis(30), + mContext.getUserId()); } } diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java index 69ba1d2fb599..18b13838ea7c 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java +++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java @@ -22,18 +22,24 @@ import static android.content.Context.BIND_INCLUDE_CAPABILITIES; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.provider.Settings; import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService; import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService; import com.android.internal.infra.ServiceConnector; +import java.util.concurrent.TimeUnit; + /** - * Manages the connection to the remote on-device sand boxed inference service. Also, handles unbinding + * Manages the connection to the remote on-device sand boxed inference service. Also, handles + * unbinding * logic set by the service implementation via a SecureSettings flag. */ public class RemoteOnDeviceSandboxedInferenceService extends ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> { + private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(1); + /** * Creates an instance of {@link ServiceConnector} * @@ -54,11 +60,17 @@ public class RemoteOnDeviceSandboxedInferenceService extends connect(); } + @Override + protected long getRequestTimeoutMs() { + return LONG_TIMEOUT; + } + @Override protected long getAutoDisconnectTimeoutMs() { - // Disable automatic unbinding. - // TODO: add logic to fetch this flag via SecureSettings. - return -1; + return Settings.Secure.getLongForUser(mContext.getContentResolver(), + Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, + TimeUnit.SECONDS.toMillis(30), + mContext.getUserId()); } } diff --git a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java new file mode 100644 index 000000000000..32f0698a8f9c --- /dev/null +++ b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java @@ -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.server.ondeviceintelligence.callbacks; + +import android.app.ondeviceintelligence.IDownloadCallback; +import android.os.Handler; +import android.os.PersistableBundle; +import android.os.RemoteException; + +import com.android.internal.infra.AndroidFuture; + +import java.util.concurrent.TimeoutException; + +/** + * This class extends the {@link IDownloadCallback} and adds a timeout Runnable to the callback + * such that, in the case where the callback methods are not invoked, we do not have to wait for + * timeout based on {@link #onDownloadCompleted} which might take minutes or hours to complete in + * some cases. Instead, in such cases we rely on the remote service sending progress updates and if + * there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the + * download will not complete and enabling faster cleanup. + */ +public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable { + private final IDownloadCallback callback; + private final Handler handler; + private final AndroidFuture future; + private final long idleTimeoutMs; + + /** + * Constructor to create a ListenableDownloadCallback. + * + * @param callback callback to send download updates to caller. + * @param handler handler to schedule timeout runnable. + * @param future future to complete to signal the callback has reached a terminal state. + * @param idleTimeoutMs timeout within which download updates should be received. + */ + public ListenableDownloadCallback(IDownloadCallback callback, Handler handler, + AndroidFuture future, + long idleTimeoutMs) { + this.callback = callback; + this.handler = handler; + this.future = future; + this.idleTimeoutMs = idleTimeoutMs; + handler.postDelayed(this, + idleTimeoutMs); // init the timeout runnable in case no callback is ever invoked + } + + @Override + public void onDownloadStarted(long bytesToDownload) throws RemoteException { + callback.onDownloadStarted(bytesToDownload); + handler.removeCallbacks(this); + handler.postDelayed(this, idleTimeoutMs); + } + + @Override + public void onDownloadProgress(long bytesDownloaded) throws RemoteException { + callback.onDownloadProgress(bytesDownloaded); + handler.removeCallbacks(this); // remove previously queued timeout tasks. + handler.postDelayed(this, idleTimeoutMs); // queue fresh timeout task for next update. + } + + @Override + public void onDownloadFailed(int failureStatus, + String errorMessage, PersistableBundle errorParams) throws RemoteException { + callback.onDownloadFailed(failureStatus, errorMessage, errorParams); + handler.removeCallbacks(this); + future.completeExceptionally(new TimeoutException()); + } + + @Override + public void onDownloadCompleted( + android.os.PersistableBundle downloadParams) throws RemoteException { + callback.onDownloadCompleted(downloadParams); + handler.removeCallbacks(this); + future.complete(null); + } + + @Override + public void run() { + future.completeExceptionally( + new TimeoutException()); // complete the future as we haven't received updates + // for download progress. + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b1976cd0d13b..4ff345fbedf9 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -600,8 +600,11 @@ public class UserManagerService extends IUserManager.Stub { public void onReceive(Context context, Intent intent) { if (isAutoLockForPrivateSpaceEnabled()) { if (ACTION_SCREEN_OFF.equals(intent.getAction())) { + Slog.d(LOG_TAG, "SCREEN_OFF broadcast received"); maybeScheduleMessageToAutoLockPrivateSpace(); } else if (ACTION_SCREEN_ON.equals(intent.getAction())) { + Slog.d(LOG_TAG, "SCREEN_ON broadcast received, " + + "removing queued message to auto-lock private space"); // Remove any queued messages since the device is interactive again mHandler.removeCallbacksAndMessages(PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN); } @@ -619,6 +622,8 @@ public class UserManagerService extends IUserManager.Stub { getMainUserIdUnchecked()); if (privateSpaceAutoLockPreference != Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY) { + Slogf.d(LOG_TAG, "Not scheduling auto-lock on inactivity," + + "preference is set to %d", privateSpaceAutoLockPreference); return; } int privateProfileUserId = getPrivateProfileUserId(); @@ -632,6 +637,7 @@ public class UserManagerService extends IUserManager.Stub { @VisibleForTesting void scheduleMessageToAutoLockPrivateSpace(int userId, Object token, long delayInMillis) { + Slog.i(LOG_TAG, "Scheduling auto-lock message"); mHandler.postDelayed(() -> { final PowerManager powerManager = mContext.getSystemService(PowerManager.class); if (powerManager != null && !powerManager.isInteractive()) { @@ -1060,8 +1066,6 @@ public class UserManagerService extends IUserManager.Stub { if (isAutoLockingPrivateSpaceOnRestartsEnabled()) { autoLockPrivateSpace(); } - - markEphemeralUsersForRemoval(); } private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() { @@ -1098,21 +1102,6 @@ public class UserManagerService extends IUserManager.Stub { } } - /** Marks all ephemeral users as slated for deletion. **/ - private void markEphemeralUsersForRemoval() { - synchronized (mUsersLock) { - final int userSize = mUsers.size(); - for (int i = 0; i < userSize; i++) { - final UserInfo ui = mUsers.valueAt(i).info; - if (ui.isEphemeral() && !ui.preCreated && ui.id != UserHandle.USER_SYSTEM) { - addRemovingUserIdLocked(ui.id); - ui.partial = true; - ui.flags |= UserInfo.FLAG_DISABLED; - } - } - } - } - /* Prunes out any partially created or partially removed users. */ private void cleanupPartialUsers() { ArrayList<UserInfo> partials = new ArrayList<>(); @@ -4223,6 +4212,13 @@ public class UserManagerService extends IUserManager.Stub { || mNextSerialNumber <= userData.info.id) { mNextSerialNumber = userData.info.id + 1; } + if (userData.info.isEphemeral() && !userData.info.preCreated + && userData.info.id != UserHandle.USER_SYSTEM) { + // Mark ephemeral user as slated for deletion. + addRemovingUserIdLocked(userData.info.id); + userData.info.partial = true; + userData.info.flags |= UserInfo.FLAG_DISABLED; + } } } } else if (name.equals(TAG_GUEST_RESTRICTIONS)) { diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index e3e478d5ce9f..3b9ad1915478 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -49,10 +49,13 @@ import static android.util.MathUtils.constrain; import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; -import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; -import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU; -import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE; -import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE; +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE; import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC; import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC; import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO; @@ -61,7 +64,6 @@ import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STA import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePuller; -import static com.android.server.stats.Flags.statsPullNetworkStatsManagerInitOrderFix; import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines; @@ -431,12 +433,6 @@ public class StatsPullAtomService extends SystemService { public static final boolean ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER = addMobileBytesTransferByProcStatePuller(); - /** - * Whether or not to enable the mNetworkStatsManager initialization order fix - */ - private static final boolean ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX = - statsPullNetworkStatsManagerInitOrderFix(); - // Puller locks private final Object mDataBytesTransferLock = new Object(); private final Object mBluetoothBytesTransferLock = new Object(); @@ -799,7 +795,7 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.KEYSTORE2_CRASH_STATS: return pullKeystoreAtoms(atomTag, data); case FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS: - return pullAccessibilityShortcutStatsLocked(atomTag, data); + return pullAccessibilityShortcutStatsLocked(data); case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS: return pullAccessibilityFloatingMenuStatsLocked(atomTag, data); case FrameworkStatsLog.MEDIA_CAPABILITIES: @@ -840,7 +836,7 @@ public class StatsPullAtomService extends SystemService { registerEventListeners(); }); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { - if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) { + if (true) { initNetworkStatsManager(); } BackgroundThread.getHandler().post(() -> { @@ -863,7 +859,7 @@ public class StatsPullAtomService extends SystemService { mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager); mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class); - if (!ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) { + if (false) { initNetworkStatsManager(); } @@ -1047,7 +1043,7 @@ public class StatsPullAtomService extends SystemService { */ @NonNull private NetworkStatsManager getNetworkStatsManager() { - if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) { + if (true) { if (mNetworkStatsManager == null) { throw new IllegalStateException("NetworkStatsManager is not ready"); } @@ -4774,7 +4770,10 @@ public class StatsPullAtomService extends SystemService { } } - int pullAccessibilityShortcutStatsLocked(int atomTag, List<StatsEvent> pulledData) { + /** + * Pulls ACCESSIBILITY_SHORTCUT_STATS atom + */ + int pullAccessibilityShortcutStatsLocked(List<StatsEvent> pulledData) { UserManager userManager = mContext.getSystemService(UserManager.class); if (userManager == null) { return StatsManager.PULL_SKIP; @@ -4782,10 +4781,6 @@ public class StatsPullAtomService extends SystemService { final long token = Binder.clearCallingIdentity(); try { final ContentResolver resolver = mContext.getContentResolver(); - final int hardware_shortcut_type = - FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; - final int triple_tap_shortcut = - FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP; for (UserInfo userInfo : userManager.getUsers()) { final int userId = userInfo.getUserHandle().getIdentifier(); @@ -4803,15 +4798,22 @@ public class StatsPullAtomService extends SystemService { final int hardware_shortcut_service_num = countAccessibilityServices( hardware_shortcut_list); + final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver, + Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId); + final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list); + // only allow magnification to use it for now final int triple_tap_service_num = Settings.Secure.getIntForUser(resolver, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId); - - pulledData.add( - FrameworkStatsLog.buildStatsEvent(atomTag, - software_shortcut_type, software_shortcut_service_num, - hardware_shortcut_type, hardware_shortcut_service_num, - triple_tap_shortcut, triple_tap_service_num)); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS, + software_shortcut_type, software_shortcut_service_num, + ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY, + hardware_shortcut_service_num, + ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP, + triple_tap_service_num, + ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS, + qs_shortcut_enabled)); } } } catch (RuntimeException e) { @@ -5150,16 +5152,19 @@ public class StatsPullAtomService extends SystemService { Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId); final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId); + final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver, + Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId); final boolean hardware_shortcut_dialog_shown = Settings.Secure.getIntForUser(resolver, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId) == 1; final boolean software_shortcut_enabled = !TextUtils.isEmpty(software_shortcut_list); final boolean hardware_shortcut_enabled = hardware_shortcut_dialog_shown && !TextUtils.isEmpty(hardware_shortcut_list); + final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list); final boolean triple_tap_shortcut_enabled = Settings.Secure.getIntForUser(resolver, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId) == 1; return software_shortcut_enabled || hardware_shortcut_enabled - || triple_tap_shortcut_enabled; + || triple_tap_shortcut_enabled || qs_shortcut_enabled; } private boolean isAccessibilityFloatingMenuUser(Context context, @UserIdInt int userId) { @@ -5176,13 +5181,13 @@ public class StatsPullAtomService extends SystemService { private int convertToAccessibilityShortcutType(int shortcutType) { switch (shortcutType) { case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR: - return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; + return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON; case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU: - return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU; + return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU; case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE: - return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE; + return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE; default: - return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; + return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE; } } diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig index c479c6d11164..6faa2737ac30 100644 --- a/services/core/java/com/android/server/stats/stats_flags.aconfig +++ b/services/core/java/com/android/server/stats/stats_flags.aconfig @@ -8,11 +8,3 @@ flag { bug: "309512867" is_fixed_read_only: true } - -flag { - name: "stats_pull_network_stats_manager_init_order_fix" - namespace: "statsd" - description: "Fix the mNetworkStatsManager initialization order" - bug: "331989853" - is_fixed_read_only: true -} diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 21e4c967a995..2b32a30c31b4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3272,8 +3272,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mOccludesParent = occludesParent; setMainWindowOpaque(occludesParent); - if (changed && task != null && !occludesParent) { - getRootTask().convertActivityToTranslucent(this); + if (changed && task != null) { + if (!occludesParent) { + getRootTask().convertActivityToTranslucent(this); + } else { + getRootTask().convertActivityFromTranslucent(this); + } } // Always ensure visibility if this activity doesn't occlude parent, so the // {@link #returningOptions} of the activity under this one can be applied in @@ -4266,6 +4270,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A getTaskFragment().cleanUpActivityReferences(this); clearLastParentBeforePip(); + // Abort and reset state if the scence transition is playing. + final Task rootTask = getRootTask(); + if (rootTask != null) { + rootTask.abortTranslucentActivityWaiting(this); + } + // Clean up the splash screen if it was still displayed. cleanUpSplashScreen(); @@ -11111,7 +11121,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * Otherwise, return the creation time of the top window. */ long getLastWindowCreateTime() { - final WindowState window = getWindow(win -> true); + final WindowState window = getWindow(alwaysTruePredicate()); return window != null && window.mAttrs.type != TYPE_BASE_APPLICATION ? window.getCreateTime() : createTime; diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java index 3609837f417b..ed07afd2eab5 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java @@ -30,10 +30,12 @@ class ActivitySnapshotCache extends SnapshotCache<ActivityRecord> { @Override void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) { final int hasCode = System.identityHashCode(ar); + snapshot.addReference(TaskSnapshot.REFERENCE_CACHE); synchronized (mLock) { final CacheEntry entry = mRunningCache.get(hasCode); if (entry != null) { mAppIdMap.remove(entry.topApp); + entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE); } mAppIdMap.put(ar, hasCode); mRunningCache.put(hasCode, new CacheEntry(snapshot, ar)); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 330336760413..08aeedebf77d 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1768,6 +1768,7 @@ class ActivityStarter { if (!avoidMoveToFront() && (mService.mHomeProcess == null || mService.mHomeProcess.mUid != realCallingUid) && (prevTopTask != null && prevTopTask.isActivityTypeHomeOrRecents()) + && !targetTask.isActivityTypeHomeOrRecents() && r.mTransitionController.isTransientHide(targetTask)) { mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY; } @@ -2113,7 +2114,6 @@ class ActivityStarter { if (hostTask == null || targetTask != hostTask) { return EMBEDDING_DISALLOWED_NEW_TASK; } - return taskFragment.isAllowedToEmbedActivity(starting); } @@ -2167,7 +2167,7 @@ class ActivityStarter { // We don't need to start a new activity, and the client said not to do anything // if that is the case, so this is it! And for paranoia, make sure we have // correctly resumed the top activity. - if (!mMovedToFront && mDoResume) { + if (!mMovedToFront && mDoResume && !avoidMoveToFront()) { ProtoLog.d(WM_DEBUG_TASKS, "Bring to front target: %s from %s", mTargetRootTask, targetTaskTop); mTargetRootTask.moveToFront("intentActivityFound"); @@ -2196,7 +2196,7 @@ class ActivityStarter { if (mMovedToFront) { // We moved the task to front, use starting window to hide initial drawn delay. targetTaskTop.showStartingWindow(true /* taskSwitch */); - } else if (mDoResume) { + } else if (mDoResume && !avoidMoveToFront()) { // Make sure the root task and its belonging display are moved to topmost. mTargetRootTask.moveToFront("intentActivityFound"); } @@ -2961,23 +2961,9 @@ class ActivityStarter { sendCanNotEmbedActivityError(mInTaskFragment, embeddingCheckResult); } } else { - TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null; + TaskFragment candidateTf = mAddingToTaskFragment; if (candidateTf == null) { - // Puts the activity on the top-most non-isolated navigation TF, unless the - // activity is launched from the same TF. - final TaskFragment sourceTaskFragment = - mSourceRecord != null ? mSourceRecord.getTaskFragment() : null; - final ActivityRecord top = task.getActivity(r -> { - if (!r.canBeTopRunning()) { - return false; - } - final TaskFragment taskFragment = r.getTaskFragment(); - return !taskFragment.isIsolatedNav() || (sourceTaskFragment != null - && sourceTaskFragment == taskFragment); - }); - if (top != null) { - candidateTf = top.getTaskFragment(); - } + candidateTf = findCandidateTaskFragment(task); } if (candidateTf != null && candidateTf.isEmbedded() && canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) { @@ -2995,6 +2981,50 @@ class ActivityStarter { } /** + * Finds a candidate TaskFragment in {@code task} to launch activity, or returns {@code null} + * if there's no such a TaskFragment. + */ + @Nullable + private TaskFragment findCandidateTaskFragment(@NonNull Task task) { + final TaskFragment sourceTaskFragment = + mSourceRecord != null ? mSourceRecord.getTaskFragment() : null; + for (int i = task.getChildCount() - 1; i >= 0; --i) { + final WindowContainer<?> wc = task.getChildAt(i); + final ActivityRecord activity = wc.asActivityRecord(); + if (activity != null) { + if (activity.finishing) { + continue; + } + // Early return if the top child is an Activity. + return null; + } + final TaskFragment taskFragment = wc.asTaskFragment(); + if (taskFragment == null || taskFragment.isRemovalRequested()) { + // Skip if the TaskFragment is going to be finished. + continue; + } + if (taskFragment.getActivity(ActivityRecord::canBeTopRunning) == null) { + // Skip if there's no activity in this TF can be top running. + continue; + } + if (taskFragment.isIsolatedNav()) { + // Stop here if we reach an isolated navigated TF. + return null; + } + if (sourceTaskFragment != null && sourceTaskFragment == taskFragment) { + // Choose the taskFragment launched from even if it's pinned. + return taskFragment; + } + if (taskFragment.isPinned()) { + // Skip the pinned TaskFragment. + continue; + } + return taskFragment; + } + return null; + } + + /** * Notifies the client side that {@link #mStartActivity} cannot be embedded to * {@code taskFragment}. */ diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 237003a5fa10..3aa63af014c8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -7418,7 +7418,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { FEATURE_LEANBACK); final boolean isArc = arcFeature != null && arcFeature.version >= 0; final boolean isTv = tvFeature != null && tvFeature.version >= 0; - sIsPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false) + sIsPip2ExperimentEnabled = SystemProperties.getBoolean( + "persist.wm_shell.pip2", false) || (Flags.enablePip2Implementation() && !isArc && !isTv); } return sIsPip2ExperimentEnabled; diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index c9703d871431..0e4f0335118d 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1732,7 +1732,10 @@ class BackNavigationController { // The activity was detached from hierarchy. return; } - activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); + + if (activity.mDisplayContent.isFixedRotationLaunchingApp(activity)) { + activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); + } // Restore the launch-behind state. activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c9a5e71ef1fd..5079ec18bb0b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2755,7 +2755,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Nullable Task getTopRootTask() { - return getRootTask(t -> true); + return getRootTask(alwaysTruePredicate()); } /** @@ -5009,7 +5009,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // This should be called after the insets have been dispatched to clients and we have // committed finish drawing windows. - mInsetsStateController.getImeSourceProvider().checkShowImePostLayout(); + mInsetsStateController.getImeSourceProvider().checkAndStartShowImePostLayout(); mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent; if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) { diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 092ff3dd07a5..e03ff6881bd8 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -24,7 +24,6 @@ import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME; import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER; -import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_DRAWN; import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS; import android.annotation.NonNull; @@ -52,19 +51,26 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { private static final String TAG = ImeInsetsSourceProvider.class.getSimpleName(); - /** The token tracking the current IME request or {@code null} otherwise. */ + /** The token tracking the show IME request, non-null only while a show request is pending. */ + @Nullable + private ImeTracker.Token mStatsToken; + /** The target that requested to show the IME, non-null only while a show request is pending. */ @Nullable - private ImeTracker.Token mImeRequesterStatsToken; private InsetsControlTarget mImeRequester; - private Runnable mShowImeRunner; - private boolean mIsImeLayoutDrawn; + /** @see #isImeShowing() */ private boolean mImeShowing; + /** The latest received insets source. */ private final InsetsSource mLastSource = new InsetsSource(ID_IME, WindowInsets.Type.ime()); /** @see #setFrozen(boolean) */ private boolean mFrozen; - /** @see #setServerVisible(boolean) */ + /** + * The server visibility of the source provider's window container. This is out of sync with + * {@link InsetsSourceProvider#mServerVisible} while {@link #mFrozen} is {@code true}. + * + * @see #setServerVisible + */ private boolean mServerVisible; ImeInsetsSourceProvider(@NonNull InsetsSource source, @@ -73,6 +79,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { super(source, stateController, displayContent); } + @Nullable @Override InsetsSourceControl getControl(InsetsControlTarget target) { final InsetsSourceControl control = super.getControl(target); @@ -124,9 +131,9 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { /** * Freeze IME insets source state when required. * - * When setting {@param frozen} as {@code true}, the IME insets provider will freeze the + * <p>When setting {@param frozen} as {@code true}, the IME insets provider will freeze the * current IME insets state and pending the IME insets state update until setting - * {@param frozen} as {@code false}. + * {@param frozen} as {@code false}.</p> */ void setFrozen(boolean frozen) { if (mFrozen == frozen) { @@ -223,27 +230,29 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { /** * Called from {@link WindowManagerInternal#showImePostLayout} * when {@link android.inputmethodservice.InputMethodService} requests to show IME - * on {@param imeTarget}. + * on the given control target. * - * @param imeTarget imeTarget on which IME request is coming from. + * @param imeTarget the control target on which the IME request is coming from. * @param statsToken the token tracking the current IME request. */ - void scheduleShowImePostLayout(InsetsControlTarget imeTarget, + void scheduleShowImePostLayout(@NonNull InsetsControlTarget imeTarget, @NonNull ImeTracker.Token statsToken) { - if (mImeRequesterStatsToken != null) { - // Cancel the pre-existing stats token, if any. - // Log state on pre-existing request cancel. - logShowImePostLayoutState(false /* aborted */); - ImeTracker.forLogging().onCancelled( - mImeRequesterStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER); - } - mImeRequesterStatsToken = statsToken; - boolean targetChanged = isTargetChangedWithinActivity(imeTarget); + if (mImeRequester == null) { + // Start tracing only on initial scheduled show IME request, to record end-to-end time. + Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); + } else { + // We already have a scheduled show IME request, cancel the previous statsToken and + // continue with the new one. + logIsScheduledAndReadyToShowIme(false /* aborted */); + ImeTracker.forLogging().onCancelled(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER); + } + final boolean targetChanged = isTargetChangedWithinActivity(imeTarget); mImeRequester = imeTarget; + mStatsToken = statsToken; if (targetChanged) { // target changed, check if new target can show IME. ProtoLog.d(WM_DEBUG_IME, "IME target changed within ActivityRecord"); - checkShowImePostLayout(); + checkAndStartShowImePostLayout(); // if IME cannot be shown at this time, it is scheduled to be shown. // once window that called IMM.showSoftInput() and DisplayContent's ImeTarget match, // it will be shown. @@ -252,79 +261,58 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeRequester.getWindow() == null ? mImeRequester : mImeRequester.getWindow().getName()); - mShowImeRunner = () -> { - ImeTracker.forLogging().onProgress(mImeRequesterStatsToken, - ImeTracker.PHASE_WM_SHOW_IME_RUNNER); - ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner"); - // Target should still be the same. - if (isReadyToShowIme()) { - ImeTracker.forLogging().onProgress(mImeRequesterStatsToken, - ImeTracker.PHASE_WM_SHOW_IME_READY); - final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL); - - ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s", - target.getWindow() != null ? target.getWindow().getName() : ""); - setImeShowing(true); - target.showInsets(WindowInsets.Type.ime(), true /* fromIme */, - mImeRequesterStatsToken); - Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); - if (target != mImeRequester && mImeRequester != null) { - ProtoLog.w(WM_DEBUG_IME, - "showInsets(ime) was requested by different window: %s ", - (mImeRequester.getWindow() != null - ? mImeRequester.getWindow().getName() : "")); - } - } else { - ImeTracker.forLogging().onFailed(mImeRequesterStatsToken, - ImeTracker.PHASE_WM_SHOW_IME_READY); - } - // Clear token here so we don't report an error in abortShowImePostLayout(). - mImeRequesterStatsToken = null; - abortShowImePostLayout(); - }; mDisplayContent.mWmService.requestTraversal(); } - void checkShowImePostLayout() { - if (mWindowContainer == null) { + /** + * Checks whether there is a previously scheduled show IME request and we are ready to show, + * in which case also start handling the request. + */ + void checkAndStartShowImePostLayout() { + if (!isScheduledAndReadyToShowIme()) { + // This can later become ready, so we don't want to cancel the pending request here. return; } - WindowState windowState = mWindowContainer.asWindowState(); - if (windowState == null) { - throw new IllegalArgumentException("IME insets must be provided by a window."); - } - // check if IME is drawn - if (mIsImeLayoutDrawn - || (isReadyToShowIme() - && windowState.isDrawn() - && !windowState.mGivenInsetsPending)) { - mIsImeLayoutDrawn = true; - // show IME if InputMethodService requested it to be shown. - if (mShowImeRunner != null) { - mShowImeRunner.run(); - } + + ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER); + ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner"); + + final InsetsControlTarget target = getControlTarget(); + + ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s", + target.getWindow() != null ? target.getWindow().getName() : ""); + setImeShowing(true); + target.showInsets(WindowInsets.Type.ime(), true /* fromIme */, mStatsToken); + Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); + if (target != mImeRequester) { + ProtoLog.w(WM_DEBUG_IME, "showInsets(ime) was requested by different window: %s ", + (mImeRequester.getWindow() != null ? mImeRequester.getWindow().getName() : "")); } + resetShowImePostLayout(); } - /** - * Abort any pending request to show IME post layout. - */ + /** Aborts the previously scheduled show IME request. */ void abortShowImePostLayout() { - ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout"); - if (mImeRequesterStatsToken != null) { - // Log state on abort. - logShowImePostLayoutState(true /* aborted */); - ImeTracker.forLogging().onFailed( - mImeRequesterStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT); - mImeRequesterStatsToken = null; + if (mImeRequester == null) { + return; } + ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout"); + Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); + logIsScheduledAndReadyToShowIme(true /* aborted */); + ImeTracker.forLogging().onFailed( + mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT); + resetShowImePostLayout(); + } + + /** Resets the state of the previously scheduled show IME request. */ + private void resetShowImePostLayout() { mImeRequester = null; - mIsImeLayoutDrawn = false; - mShowImeRunner = null; + mStatsToken = null; } + /** Checks whether there is a previously scheduled show IME request and we are ready to show. */ @VisibleForTesting - boolean isReadyToShowIme() { + boolean isScheduledAndReadyToShowIme() { // IMMS#mLastImeTargetWindow always considers focused window as // IME target, however DisplayContent#computeImeTarget() can compute // a different IME target. @@ -334,32 +322,47 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { // Also, if imeTarget is closing, it would be considered as outdated target. // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of // actual IME target. + if (mImeRequester == null) { + // No show IME request previously scheduled. + return false; + } + if (!mServerVisible || mFrozen) { + // The window container is not available and considered visible. + // If frozen, the server visibility is not set until unfrozen. + return false; + } + if (mWindowContainer == null) { + // No window container set. + return false; + } + final WindowState windowState = mWindowContainer.asWindowState(); + if (windowState == null) { + throw new IllegalArgumentException("IME insets must be provided by a window."); + } + if (!windowState.isDrawn() || windowState.mGivenInsetsPending) { + // The window is not drawn, or it has pending insets. + return false; + } final InsetsControlTarget dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING); - if (dcTarget == null || mImeRequester == null) { - // Not ready to show if there is no IME layering target, or no IME requester. + if (dcTarget == null) { + // No IME layering target. return false; } final InsetsControlTarget controlTarget = getControlTarget(); if (controlTarget == null) { - // Not ready to show if there is no IME control target. + // No IME control target. return false; } if (controlTarget != mDisplayContent.getImeTarget(IME_TARGET_CONTROL)) { - // Not ready to show if control target does not match the one in DisplayContent. - return false; - } - if (!mServerVisible || mFrozen) { - // Not ready to show if the window container is not available and considered visible. - // If frozen, the server visibility is not set until unfrozen. + // The control target does not match the one in DisplayContent. return false; } if (mStateController.hasPendingControls(controlTarget)) { - // Not ready to show if control target has pending controls. + // The control target has pending controls. return false; } if (getLeash(controlTarget) == null) { - // Not ready to show if control target has no source control leash (or leash is not - // ready for dispatching). + // The control target has no source control leash (or it is not ready for dispatching). return false; } @@ -371,51 +374,44 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { || isAboveImeLayeringTarget(mImeRequester, dcTarget) || isImeFallbackTarget(mImeRequester) || isImeInputTarget(mImeRequester) - || sameAsImeControlTarget(); + || sameAsImeControlTarget(mImeRequester); } /** - * Logs the current state required for showImePostLayout to be triggered. + * Logs the current state that can be checked by {@link #isScheduledAndReadyToShowIme}. * - * @param aborted whether the showImePostLayout was aborted or cancelled. + * @param aborted whether the scheduled show IME request was aborted or cancelled. */ - private void logShowImePostLayoutState(boolean aborted) { + private void logIsScheduledAndReadyToShowIme(boolean aborted) { final var windowState = mWindowContainer != null ? mWindowContainer.asWindowState() : null; final var dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING); final var controlTarget = getControlTarget(); final var sb = new StringBuilder(); sb.append("showImePostLayout ").append(aborted ? "aborted" : "cancelled"); - sb.append(", mWindowContainer is: "); - sb.append(mWindowContainer != null ? "non-null" : "null"); + sb.append(", isScheduledAndReadyToShowIme: ").append(isScheduledAndReadyToShowIme()); + sb.append(", mImeRequester: ").append(mImeRequester); + sb.append(", serverVisible: ").append(mServerVisible); + sb.append(", frozen: ").append(mFrozen); + sb.append(", mWindowContainer is: ").append(mWindowContainer != null ? "non-null" : "null"); sb.append(", windowState: ").append(windowState); if (windowState != null) { - sb.append(", windowState.isDrawn(): "); - sb.append(windowState.isDrawn()); - sb.append(", windowState.mGivenInsetsPending: "); - sb.append(windowState.mGivenInsetsPending); + sb.append(", isDrawn: ").append(windowState.isDrawn()); + sb.append(", mGivenInsetsPending: ").append(windowState.mGivenInsetsPending); } - sb.append(", mIsImeLayoutDrawn: ").append(mIsImeLayoutDrawn); - sb.append(", mShowImeRunner: ").append(mShowImeRunner); - sb.append(", mImeRequester: ").append(mImeRequester); sb.append(", dcTarget: ").append(dcTarget); sb.append(", controlTarget: ").append(controlTarget); - sb.append("\n"); - sb.append("isReadyToShowIme(): ").append(isReadyToShowIme()); if (mImeRequester != null && dcTarget != null && controlTarget != null) { - sb.append(", controlTarget == DisplayContent.controlTarget: "); + sb.append("\n"); + sb.append("controlTarget == DisplayContent.controlTarget: "); sb.append(controlTarget == mDisplayContent.getImeTarget(IME_TARGET_CONTROL)); sb.append(", hasPendingControls: "); sb.append(mStateController.hasPendingControls(controlTarget)); - sb.append(", serverVisible: "); - sb.append(mServerVisible); - sb.append(", frozen: "); - sb.append(mFrozen); - sb.append(", leash is: "); - sb.append(getLeash(controlTarget) != null ? "non-null" : "null"); - sb.append(", control is: "); - sb.append(mControl != null ? "non-null" : "null"); - sb.append(", mIsLeashReadyForDispatching: "); - sb.append(mIsLeashReadyForDispatching); + final boolean hasLeash = getLeash(controlTarget) != null; + sb.append(", leash is: ").append(hasLeash ? "non-null" : "null"); + if (!hasLeash) { + sb.append(", control is: ").append(mControl != null ? "non-null" : "null"); + sb.append(", mIsLeashReadyForDispatching: ").append(mIsLeashReadyForDispatching); + } sb.append(", isImeLayeringTarget: "); sb.append(isImeLayeringTarget(mImeRequester, dcTarget)); sb.append(", isAboveImeLayeringTarget: "); @@ -425,7 +421,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { sb.append(", isImeInputTarget: "); sb.append(isImeInputTarget(mImeRequester)); sb.append(", sameAsImeControlTarget: "); - sb.append(sameAsImeControlTarget()); + sb.append(sameAsImeControlTarget(mImeRequester)); } Slog.d(TAG, sb.toString()); } @@ -445,19 +441,18 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { && dcTarget.getWindow().mSubLayer > target.getWindow().mSubLayer; } - private boolean isImeFallbackTarget(InsetsControlTarget target) { + private boolean isImeFallbackTarget(@NonNull InsetsControlTarget target) { return target == mDisplayContent.getImeFallback(); } - private boolean isImeInputTarget(InsetsControlTarget target) { + private boolean isImeInputTarget(@NonNull InsetsControlTarget target) { return target == mDisplayContent.getImeInputTarget(); } - private boolean sameAsImeControlTarget() { - final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL); - return target == mImeRequester - && (mImeRequester.getWindow() == null - || !isImeTargetWindowClosing(mImeRequester.getWindow())); + private boolean sameAsImeControlTarget(@NonNull InsetsControlTarget target) { + final InsetsControlTarget controlTarget = getControlTarget(); + return controlTarget == target + && (target.getWindow() == null || !isImeTargetWindowClosing(target.getWindow())); } private static boolean isImeTargetWindowClosing(@NonNull WindowState win) { @@ -467,16 +462,15 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { || win.mActivityRecord.willCloseOrEnterPip()); } - private boolean isTargetChangedWithinActivity(InsetsControlTarget target) { + private boolean isTargetChangedWithinActivity(@NonNull InsetsControlTarget target) { // We don't consider the target out of the activity. - if (target == null || target.getWindow() == null) { + if (target.getWindow() == null) { return false; } return mImeRequester != target - && mImeRequester != null && mShowImeRunner != null + && mImeRequester != null && mImeRequester.getWindow() != null - && mImeRequester.getWindow().mActivityRecord - == target.getWindow().mActivityRecord; + && mImeRequester.getWindow().mActivityRecord == target.getWindow().mActivityRecord; } // --------------------------------------------------------------------------------------- @@ -509,7 +503,6 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { if (imeRequesterWindow != null) { imeRequesterWindow.dumpDebug(proto, IME_TARGET_FROM_IME, logLevel); } - proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn); proto.end(token); } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index dfee16440518..7a1f57bea680 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -389,7 +389,7 @@ class InsetsStateController { newControlTargets.clear(); // Check for and try to run the scheduled show IME request (if it exists), as we // now applied the surface transaction and notified the target of the new control. - getImeSourceProvider().checkShowImePostLayout(); + getImeSourceProvider().checkAndStartShowImePostLayout(); }); } diff --git a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java index 498182dab9c3..3606a34e23e0 100644 --- a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java +++ b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java @@ -41,8 +41,12 @@ class PerfettoTransitionTracer implements TransitionTracer { PerfettoTransitionTracer() { Producer.init(InitArguments.DEFAULTS); - mDataSource.register( - new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)); + DataSourceParams params = + new DataSourceParams.Builder() + .setBufferExhaustedPolicy( + PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT) + .build(); + mDataSource.register(params); } /** diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java index 86804360f6f4..1e6ee7dc318f 100644 --- a/services/core/java/com/android/server/wm/SnapshotCache.java +++ b/services/core/java/com/android/server/wm/SnapshotCache.java @@ -92,6 +92,7 @@ abstract class SnapshotCache<TYPE extends WindowContainer> { if (entry != null) { mAppIdMap.remove(entry.topApp); mRunningCache.remove(id); + entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE); } } } diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java index 357897127f3a..42ca7b44287e 100644 --- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -253,6 +253,7 @@ class SnapshotPersistQueue { PersistInfoProvider provider) { super(provider, userId); mId = id; + snapshot.addReference(TaskSnapshot.REFERENCE_PERSIST); mSnapshot = snapshot; } @@ -289,6 +290,7 @@ class SnapshotPersistQueue { if (failed) { deleteSnapshot(mId, mUserId, mPersistInfoProvider); } + mSnapshot.removeReference(TaskSnapshot.REFERENCE_PERSIST); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 8defec3dbeab..a555388ab233 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -297,6 +297,10 @@ class Task extends TaskFragment { ActivityRecord mTranslucentActivityWaiting = null; ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>(); + // The topmost Activity that was converted to translucent for scene transition, which should + // be converted from translucent once the transition is completed, or the app died. + private ActivityRecord mPendingConvertFromTranslucentActivity = null; + /** * Set when we know we are going to be calling updateConfiguration() * soon, so want to skip intermediate config checks. @@ -4988,6 +4992,27 @@ class Task extends TaskFragment { } } + void abortTranslucentActivityWaiting(@NonNull ActivityRecord r) { + if (r != mTranslucentActivityWaiting && r != mPendingConvertFromTranslucentActivity) { + return; + } + + if (mTranslucentActivityWaiting != null) { + if (!mTranslucentActivityWaiting.finishing) { + mTranslucentActivityWaiting.setOccludesParent(true); + } + mTranslucentActivityWaiting = null; + } + if (mPendingConvertFromTranslucentActivity != null) { + if (!mPendingConvertFromTranslucentActivity.finishing) { + mPendingConvertFromTranslucentActivity.setOccludesParent(true); + } + mPendingConvertFromTranslucentActivity = null; + } + mUndrawnActivitiesBelowTopTranslucent.clear(); + mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG); + } + void checkTranslucentActivityWaiting(ActivityRecord top) { if (mTranslucentActivityWaiting != top) { mUndrawnActivitiesBelowTopTranslucent.clear(); @@ -5002,10 +5027,19 @@ class Task extends TaskFragment { void convertActivityToTranslucent(ActivityRecord r) { mTranslucentActivityWaiting = r; + mPendingConvertFromTranslucentActivity = r; mUndrawnActivitiesBelowTopTranslucent.clear(); mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT); } + void convertActivityFromTranslucent(ActivityRecord r) { + if (r != mPendingConvertFromTranslucentActivity) { + Slog.e(TAG, "convertFromTranslucent expects " + mPendingConvertFromTranslucentActivity + + " but is " + r); + } + mPendingConvertFromTranslucentActivity = null; + } + /** * Called as activities below the top translucent activity are redrawn. When the last one is * redrawn notify the top activity by calling diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 21e7a8d63773..586f3c35c0c4 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -247,6 +247,7 @@ class TaskChangeNotificationController { break; case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG: forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg); + ((TaskSnapshot) msg.obj).removeReference(TaskSnapshot.REFERENCE_BROADCAST); break; case NOTIFY_BACK_PRESSED_ON_TASK_ROOT: forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg); @@ -485,6 +486,7 @@ class TaskChangeNotificationController { * Notify listeners that the snapshot of a task has changed. */ void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { + snapshot.addReference(TaskSnapshot.REFERENCE_BROADCAST); final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG, taskId, 0, snapshot); forAllLocalListeners(mNotifyTaskSnapshotChanged, msg); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 2c27b98250c9..eff831552320 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -223,7 +223,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { @VisibleForTesting Task getTopRootTask() { - return getRootTask(t -> true); + return getRootTask(alwaysTruePredicate()); } @Nullable diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 6a7f60b3447d..a444c96eea97 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -354,14 +354,21 @@ class TaskFragment extends WindowContainer<WindowContainer> { /** * Whether the activity navigation should be isolated. That is, Activities cannot be launched - * on an isolated TaskFragment, unless the activity is launched from an Activity in the same - * isolated TaskFragment, or explicitly requested to be launched to. - * <p> - * Note that only an embedded TaskFragment can be isolated. + * on an isolated TaskFragment unless explicitly requested to be launched to. */ private boolean mIsolatedNav; /** + * Whether the TaskFragment to be pinned. + * <p> + * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other + * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the + * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or + * explicitly requested to. Non-embeddable activities are not restricted to. + */ + private boolean mPinned; + + /** * Whether the TaskFragment should move to bottom of task when any activity below it is * launched in clear top mode. */ @@ -515,6 +522,18 @@ class TaskFragment extends WindowContainer<WindowContainer> { } /** + * Sets whether this TaskFragment {@link #isPinned()}. + * <p> + * Note that this is no-op if the TaskFragment is not {@link #isEmbedded() embedded}. + */ + void setPinned(boolean pinned) { + if (!isEmbedded()) { + return; + } + mPinned = pinned; + } + + /** * Sets whether transitions are allowed when the TaskFragment is empty. If {@code true}, * transitions are allowed when the TaskFragment is empty. If {@code false}, transitions * will wait until the TaskFragment becomes non-empty or other conditions are met. Default @@ -532,6 +551,15 @@ class TaskFragment extends WindowContainer<WindowContainer> { return isEmbedded() && mIsolatedNav; } + /** + * Indicates whether this TaskFragment is pinned. + * + * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED + */ + boolean isPinned() { + return isEmbedded() && mPinned; + } + TaskFragment getAdjacentTaskFragment() { return mAdjacentTaskFragment; } @@ -564,7 +592,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { } void setResumedActivity(ActivityRecord r, String reason) { - warnForNonLeafTaskFragment("setResumedActivity"); if (mResumedActivity == r) { return; } @@ -850,15 +877,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null; } - /** - * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}. - */ - private void warnForNonLeafTaskFragment(String func) { - if (!isLeafTaskFragment()) { - Slog.w(TAG, func + " on non-leaf task fragment " + this); - } - } - boolean hasDirectChildActivities() { for (int i = mChildren.size() - 1; i >= 0; --i) { if (mChildren.get(i).asActivityRecord() != null) { @@ -935,7 +953,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { */ void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state, String reason) { - warnForNonLeafTaskFragment("onActivityStateChanged"); if (record == mResumedActivity && state != RESUMED) { setResumedActivity(null, reason + " - onActivityStateChanged"); } @@ -965,7 +982,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { * @return {@code true} if the process of the pausing activity is died. */ boolean handleAppDied(WindowProcessController app) { - warnForNonLeafTaskFragment("handleAppDied"); boolean isPausingDied = false; if (mPausingActivity != null && mPausingActivity.app == app) { ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s", @@ -2895,6 +2911,13 @@ class TaskFragment extends WindowContainer<WindowContainer> { return !mCreatedByOrganizer || mIsRemovalRequested; } + /** + * Returns whether this TaskFragment is going to be removed. + */ + boolean isRemovalRequested() { + return mIsRemovalRequested; + } + @Override void removeChild(WindowContainer child) { removeChild(child, true /* removeSelfIfPossible */); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java index b69ac1bb2795..64b9df59990b 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java @@ -35,9 +35,11 @@ class TaskSnapshotCache extends SnapshotCache<Task> { void putSnapshot(Task task, TaskSnapshot snapshot) { synchronized (mLock) { + snapshot.addReference(TaskSnapshot.REFERENCE_CACHE); final CacheEntry entry = mRunningCache.get(task.mTaskId); if (entry != null) { mAppIdMap.remove(entry.topApp); + entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE); } final ActivityRecord top = task.getTopMostActivity(); mAppIdMap.put(top, task.mTaskId); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index d70ca02cc23d..edbba9244738 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -116,6 +116,7 @@ import com.android.internal.util.ToBooleanFunction; import com.android.server.wm.SurfaceAnimator.Animatable; import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; +import com.android.server.wm.utils.AlwaysTruePredicate; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -2019,29 +2020,34 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound); } + @SuppressWarnings("unchecked") + static <T> Predicate<T> alwaysTruePredicate() { + return (Predicate<T>) AlwaysTruePredicate.INSTANCE; + } + ActivityRecord getActivityAbove(ActivityRecord r) { - return getActivity((above) -> true, r, + return getActivity(alwaysTruePredicate(), r /* boundary */, false /*includeBoundary*/, false /*traverseTopToBottom*/); } ActivityRecord getActivityBelow(ActivityRecord r) { - return getActivity((below) -> true, r, + return getActivity(alwaysTruePredicate(), r /* boundary */, false /*includeBoundary*/, true /*traverseTopToBottom*/); } ActivityRecord getBottomMostActivity() { - return getActivity((r) -> true, false /*traverseTopToBottom*/); + return getActivity(alwaysTruePredicate(), false /* traverseTopToBottom */); } ActivityRecord getTopMostActivity() { - return getActivity((r) -> true, true /*traverseTopToBottom*/); + return getActivity(alwaysTruePredicate(), true /* traverseTopToBottom */); } ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) { // Break down into 4 calls to avoid object creation due to capturing input params. if (includeFinishing) { if (includeOverlays) { - return getActivity((r) -> true); + return getActivity(alwaysTruePredicate()); } return getActivity((r) -> !r.isTaskOverlay()); } else if (includeOverlays) { @@ -2220,21 +2226,17 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } - Task getTaskAbove(Task t) { - return getTask( - (above) -> true, t, false /*includeBoundary*/, false /*traverseTopToBottom*/); - } - Task getTaskBelow(Task t) { - return getTask((below) -> true, t, false /*includeBoundary*/, true /*traverseTopToBottom*/); + return getTask(alwaysTruePredicate(), t /* boundary */, + false /* includeBoundary */, true /* traverseTopToBottom */); } Task getBottomMostTask() { - return getTask((t) -> true, false /*traverseTopToBottom*/); + return getTask(alwaysTruePredicate(), false /* traverseTopToBottom */); } Task getTopMostTask() { - return getTask((t) -> true, true /*traverseTopToBottom*/); + return getTask(alwaysTruePredicate(), true /* traverseTopToBottom */); } Task getTask(Predicate<Task> callback) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e02e5bef688c..b6035519fdba 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8302,7 +8302,6 @@ public class WindowManagerService extends IWindowManager.Stub ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET); - Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget(); imeTarget = controlTarget.getWindow(); // If InsetsControlTarget doesn't have a window, it's using remoteControlTarget diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 90e7bd7b99e6..99c47360418b 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -39,6 +39,7 @@ import static android.window.TaskFragmentOperation.OP_TYPE_SET_DECOR_SURFACE_BOO import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION; import static android.window.TaskFragmentOperation.OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH; +import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED; import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS; import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN; @@ -1627,6 +1628,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } break; } + case OP_TYPE_SET_PINNED: { + final boolean pinned = operation.getBooleanValue(); + taskFragment.setPinned(pinned); + break; + } } return effects; } diff --git a/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java b/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java new file mode 100644 index 000000000000..49dcb6cab633 --- /dev/null +++ b/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.utils; + +import java.util.function.Predicate; + +/** A simple Predicate to avoid synthetic allocation of lambda expression "o -> true". */ +public class AlwaysTruePredicate implements Predicate<Object> { + + public static final AlwaysTruePredicate INSTANCE = new AlwaysTruePredicate(); + + private AlwaysTruePredicate() { + } + + @Override + public boolean test(Object o) { + return true; + } +} diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index a01c1231b373..74ca9ad687ea 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -423,7 +423,7 @@ private: std::set<int32_t> disabledInputDevices{}; // Associated Pointer controller display. - ui::LogicalDisplayId pointerDisplayId{ui::ADISPLAY_ID_DEFAULT}; + ui::LogicalDisplayId pointerDisplayId{ui::LogicalDisplayId::DEFAULT}; // True if stylus button reporting through motion events is enabled. bool stylusButtonMotionEventsEnabled{true}; @@ -1886,7 +1886,7 @@ static jobject nativeCreateInputMonitor(JNIEnv* env, jobject nativeImplObj, jint jstring nameObj, jint pid) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - if (displayId == ui::ADISPLAY_ID_NONE.val()) { + if (ui::LogicalDisplayId{displayId} == ui::LogicalDisplayId::INVALID) { std::string message = "InputChannel used as a monitor must be associated with a display"; jniThrowRuntimeException(env, message.c_str()); return nullptr; @@ -2727,6 +2727,11 @@ static void nativeSetInputMethodConnectionIsActive(JNIEnv* env, jobject nativeIm im->setInputMethodConnectionIsActive(isActive); } +static jint nativeGetLastUsedInputDeviceId(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); + return static_cast<jint>(im->getInputManager()->getReader().getLastUsedInputDeviceId()); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { @@ -2835,6 +2840,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"setAccessibilityStickyKeysEnabled", "(Z)V", (void*)nativeSetAccessibilityStickyKeysEnabled}, {"setInputMethodConnectionIsActive", "(Z)V", (void*)nativeSetInputMethodConnectionIsActive}, + {"getLastUsedInputDeviceId", "()I", (void*)nativeGetLastUsedInputDeviceId}, }; #define FIND_CLASS(var, className) \ diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 6143f1dd5b1c..610b502f2a07 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -746,6 +746,20 @@ minOccurs="0" maxOccurs="1"> <xs:annotation name="final"/> </xs:element> + <!-- list of supported modes for low power. Each point corresponds to one mode. + Mode format is : first = refreshRate, second = vsyncRate. E.g. : + <lowPowerSupportedModes> + <point> + <first>60</first> // refreshRate + <second>60</second> //vsyncRate + </point> + .... + </lowPowerSupportedModes> + --> + <xs:element type="nonNegativeFloatToFloatMap" name="lowPowerSupportedModes" minOccurs="0"> + <xs:annotation name="nullable"/> + <xs:annotation name="final"/> + </xs:element> </xs:complexType> <xs:complexType name="refreshRateZoneProfiles"> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 45ec8f250efa..203a6d99dba1 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -360,6 +360,7 @@ package com.android.server.display.config { method public final java.math.BigInteger getDefaultRefreshRateInHbmHdr(); method public final java.math.BigInteger getDefaultRefreshRateInHbmSunlight(); method public final com.android.server.display.config.BlockingZoneConfig getHigherBlockingZoneConfigs(); + method @Nullable public final com.android.server.display.config.NonNegativeFloatToFloatMap getLowPowerSupportedModes(); method public final com.android.server.display.config.BlockingZoneConfig getLowerBlockingZoneConfigs(); method public final com.android.server.display.config.RefreshRateZoneProfiles getRefreshRateZoneProfiles(); method public final void setDefaultPeakRefreshRate(java.math.BigInteger); @@ -367,6 +368,7 @@ package com.android.server.display.config { method public final void setDefaultRefreshRateInHbmHdr(java.math.BigInteger); method public final void setDefaultRefreshRateInHbmSunlight(java.math.BigInteger); method public final void setHigherBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig); + method public final void setLowPowerSupportedModes(@Nullable com.android.server.display.config.NonNegativeFloatToFloatMap); method public final void setLowerBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig); method public final void setRefreshRateZoneProfiles(com.android.server.display.config.RefreshRateZoneProfiles); } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index 1d225ba09bbd..221a99102daa 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -36,9 +36,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static java.util.Objects.requireNonNull; + import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -72,7 +73,10 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe super.setUp(); mVisibilityApplier = (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier(); - mInputMethodManagerService.setAttachedClientForTesting(mock(ClientState.class)); + synchronized (ImfLock.class) { + mInputMethodManagerService.setAttachedClientForTesting(requireNonNull( + mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient))); + } } @Test diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index cff22654e30c..28a99f2f8e87 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -45,6 +46,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.view.InputChannel; import android.view.inputmethod.EditorInfo; import android.window.ImeOnBackInvokedDispatcher; @@ -53,6 +55,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.compat.IPlatformCompat; import com.android.internal.inputmethod.IInputMethod; import com.android.internal.inputmethod.IInputMethodClient; +import com.android.internal.inputmethod.IInputMethodSession; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.IRemoteInputConnection; import com.android.internal.inputmethod.InputBindResult; @@ -104,6 +107,7 @@ public class InputMethodManagerServiceTestBase { @Mock protected UserManagerInternal mMockUserManagerInternal; @Mock protected InputMethodBindingController mMockInputMethodBindingController; @Mock protected IInputMethodClient mMockInputMethodClient; + @Mock protected IInputMethodSession mMockInputMethodSession; @Mock protected IBinder mWindowToken; @Mock protected IRemoteInputConnection mMockRemoteInputConnection; @Mock protected IRemoteAccessibilityInputConnection mMockRemoteAccessibilityInputConnection; @@ -246,6 +250,7 @@ public class InputMethodManagerServiceTestBase { // Call InputMethodManagerService#addClient() as a preparation to start interacting with it. mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection, 0); + createSessionForClient(mMockInputMethodClient); } @After @@ -295,4 +300,13 @@ public class InputMethodManagerServiceTestBase { .hideSoftInput(any() /* hideInputToken */, notNull() /* statsToken */, anyInt() /* flags */, any() /* resultReceiver */); } + + protected void createSessionForClient(IInputMethodClient client) { + synchronized (ImfLock.class) { + ClientState cs = mInputMethodManagerService.getClientStateLocked(client); + cs.mCurSession = new InputMethodManagerService.SessionState(cs, + mMockInputMethodInvoker, mMockInputMethodSession, mock( + InputChannel.class)); + } + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index a0a611ff4eb1..46d08b0ce018 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -21,7 +21,6 @@ import static com.android.internal.display.BrightnessSynchronizer.brightnessIntT import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN; -import static com.android.server.display.config.SensorData.SupportedMode; import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat; import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat; @@ -58,6 +57,7 @@ import com.android.server.display.config.HdrBrightnessData; import com.android.server.display.config.HysteresisLevels; import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint; import com.android.server.display.config.RefreshRateData; +import com.android.server.display.config.SupportedModeData; import com.android.server.display.config.ThermalStatus; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.feature.flags.Flags; @@ -613,7 +613,7 @@ public final class DisplayDeviceConfigTest { assertEquals(mDisplayDeviceConfig.getProximitySensor().minRefreshRate, 60, SMALL_DELTA); assertEquals(mDisplayDeviceConfig.getProximitySensor().maxRefreshRate, 90, SMALL_DELTA); assertThat(mDisplayDeviceConfig.getProximitySensor().supportedModes).hasSize(2); - SupportedMode mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0); + SupportedModeData mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0); assertEquals(mode.refreshRate, 60, SMALL_DELTA); assertEquals(mode.vsyncRate, 65, SMALL_DELTA); mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(1); @@ -933,6 +933,21 @@ public final class DisplayDeviceConfigTest { assertEquals(0.2f, mDisplayDeviceConfig.getNitsFromBacklight(0.0f), ZERO_DELTA); } + @Test + public void testLowPowerSupportedModesFromConfigFile() throws IOException { + setupDisplayDeviceConfigFromDisplayConfigFile(); + + RefreshRateData refreshRateData = mDisplayDeviceConfig.getRefreshRateData(); + assertNotNull(refreshRateData); + assertThat(refreshRateData.lowPowerSupportedModes).hasSize(2); + SupportedModeData supportedModeData = refreshRateData.lowPowerSupportedModes.get(0); + assertThat(supportedModeData.refreshRate).isEqualTo(60); + assertThat(supportedModeData.vsyncRate).isEqualTo(60); + supportedModeData = refreshRateData.lowPowerSupportedModes.get(1); + assertThat(supportedModeData.refreshRate).isEqualTo(60); + assertThat(supportedModeData.vsyncRate).isEqualTo(120); + } + private String getValidLuxThrottling() { return "<luxThrottling>\n" + " <brightnessLimitMap>\n" @@ -1089,6 +1104,19 @@ public final class DisplayDeviceConfigTest { + "</proxSensor>\n"; } + private String getLowPowerConfig() { + return "<lowPowerSupportedModes>\n" + + " <point>\n" + + " <first>60</first>\n" + + " <second>60</second>\n" + + " </point>\n" + + " <point>\n" + + " <first>60</first>\n" + + " <second>120</second>\n" + + " </point>\n" + + "</lowPowerSupportedModes>\n"; + } + private String getHdrBrightnessConfig() { return "<hdrBrightnessConfig>\n" + " <brightnessMap>\n" @@ -1620,6 +1648,7 @@ public final class DisplayDeviceConfigTest { + "</displayBrightnessPoint>\n" + "</blockingZoneThreshold>\n" + "</higherBlockingZoneConfigs>\n" + + getLowPowerConfig() + "</refreshRate>\n" + "<screenOffBrightnessSensorValueToLux>\n" + "<item>-1</item>\n" diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index ae6361bdd556..df9671235071 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -46,6 +46,7 @@ import com.android.server.display.brightness.strategy.AutomaticBrightnessStrateg import com.android.server.display.brightness.strategy.BoostBrightnessStrategy; import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy; import com.android.server.display.brightness.strategy.DozeBrightnessStrategy; +import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy; import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy; import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy; import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy; @@ -90,6 +91,8 @@ public final class DisplayBrightnessStrategySelectorTest { @Mock private AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy; @Mock + private FallbackBrightnessStrategy mFallbackBrightnessStrategy; + @Mock private Resources mResources; @Mock private DisplayManagerFlags mDisplayManagerFlags; @@ -135,7 +138,7 @@ public final class DisplayBrightnessStrategySelectorTest { @Override AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context, - int displayId) { + int displayId, DisplayManagerFlags displayManagerFlags) { return mAutomaticBrightnessStrategy; } @@ -155,6 +158,11 @@ public final class DisplayBrightnessStrategySelectorTest { AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() { return mAutoBrightnessFallbackStrategy; } + + @Override + FallbackBrightnessStrategy getFallbackBrightnessStrategy() { + return mFallbackBrightnessStrategy; + } }; @Rule @@ -355,6 +363,25 @@ public final class DisplayBrightnessStrategySelectorTest { } @Test + public void selectStrategy_selectsFallbackStrategyAsAnUltimateFallback() { + when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( + DisplayManagerInternal.DisplayPowerRequest.class); + displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; + displayPowerRequest.screenBrightnessOverride = Float.NaN; + when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); + when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); + when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(false); + when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false); + assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( + new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, + 0.1f, false)), + mFallbackBrightnessStrategy); + } + + @Test public void selectStrategyCallsPostProcessorForAllStrategies() { when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index 3e78118cd5df..19bff56a4b29 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -18,8 +18,10 @@ package com.android.server.display.brightness.strategy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -45,6 +47,7 @@ import com.android.server.display.DisplayBrightnessState; import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.brightness.StrategyExecutionRequest; +import com.android.server.display.feature.DisplayManagerFlags; import org.junit.After; import org.junit.Before; @@ -64,6 +67,9 @@ public class AutomaticBrightnessStrategyTest { @Mock private AutomaticBrightnessController mAutomaticBrightnessController; + @Mock + private DisplayManagerFlags mDisplayManagerFlags; + private BrightnessConfiguration mBrightnessConfiguration; private float mDefaultScreenAutoBrightnessAdjustment; private Context mContext; @@ -80,7 +86,8 @@ public class AutomaticBrightnessStrategyTest { Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Float.NaN); Settings.System.putFloat(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.5f); - mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID); + mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID, + mDisplayManagerFlags); mBrightnessConfiguration = new BrightnessConfiguration.Builder( new float[]{0f, 1f}, new float[]{0, PowerManager.BRIGHTNESS_ON}).build(); @@ -247,6 +254,46 @@ public class AutomaticBrightnessStrategyTest { } @Test + public void testAutoBrightnessState_modeSwitch() { + // Setup the test + when(mDisplayManagerFlags.areAutoBrightnessModesEnabled()).thenReturn(true); + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + boolean allowAutoBrightnessWhileDozing = false; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; + float pendingBrightnessAdjustment = 0.1f; + Settings.System.putFloat(mContext.getContentResolver(), + Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment); + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(); + + // Validate no interaction when automaticBrightnessController is in idle mode + when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController, never()).switchMode(anyInt()); + + // Validate interaction when automaticBrightnessController is in non-idle mode, and display + // state is ON + when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false); + mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController).switchMode( + AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT); + + // Validate interaction when automaticBrightnessController is in non-idle mode, and display + // state is DOZE + mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController).switchMode( + AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE); + } + + @Test public void accommodateUserBrightnessChangesWorksAsExpected() { // Verify the state if automaticBrightnessController is configured. assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive()); @@ -390,7 +437,8 @@ public class AutomaticBrightnessStrategyTest { @Test public void testVerifyNoAutoBrightnessAdjustmentsArePopulatedForNonDefaultDisplay() { int newDisplayId = 1; - mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId); + mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId, + mDisplayManagerFlags); mAutomaticBrightnessStrategy.putAutoBrightnessAdjustmentSetting(0.3f); assertEquals(0.5f, mAutomaticBrightnessStrategy.getAutoBrightnessAdjustment(), 0.0f); @@ -429,8 +477,7 @@ public class AutomaticBrightnessStrategyTest { updateBrightness_constructsDisplayBrightnessState_withAdjustmentAutoAdjustmentFlag() { BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID); mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy( - mContext, DISPLAY_ID, displayId -> brightnessEvent); - new AutomaticBrightnessStrategy(mContext, DISPLAY_ID); + mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags); mAutomaticBrightnessStrategy.setAutomaticBrightnessController( mAutomaticBrightnessController); float brightness = 0.4f; @@ -461,8 +508,7 @@ public class AutomaticBrightnessStrategyTest { updateBrightness_constructsDisplayBrightnessState_withAdjustmentTempAdjustmentFlag() { BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID); mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy( - mContext, DISPLAY_ID, displayId -> brightnessEvent); - new AutomaticBrightnessStrategy(mContext, DISPLAY_ID); + mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags); mAutomaticBrightnessStrategy.setAutomaticBrightnessController( mAutomaticBrightnessController); float brightness = 0.4f; diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java new file mode 100644 index 000000000000..c4767ae5172b --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.strategy; + + +import static org.junit.Assert.assertEquals; + +import android.hardware.display.DisplayManagerInternal; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; +import com.android.server.display.brightness.StrategyExecutionRequest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) + +public class FallbackBrightnessStrategyTest { + private FallbackBrightnessStrategy mFallbackBrightnessStrategy; + + @Before + public void before() { + mFallbackBrightnessStrategy = new FallbackBrightnessStrategy(); + } + + @Test + public void updateBrightness_currentBrightnessIsSet() { + DisplayManagerInternal.DisplayPowerRequest + displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest(); + float currentBrightness = 0.2f; + BrightnessReason brightnessReason = new BrightnessReason(); + brightnessReason.setReason(BrightnessReason.REASON_MANUAL); + DisplayBrightnessState expectedDisplayBrightnessState = + new DisplayBrightnessState.Builder() + .setBrightness(currentBrightness) + .setBrightnessReason(brightnessReason) + .setSdrBrightness(currentBrightness) + .setDisplayBrightnessStrategyName(mFallbackBrightnessStrategy.getName()) + .setShouldUpdateScreenBrightnessSetting(true) + .build(); + DisplayBrightnessState updatedDisplayBrightnessState = + mFallbackBrightnessStrategy.updateBrightness( + new StrategyExecutionRequest(displayPowerRequest, currentBrightness)); + assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index cd1e9e85afb5..714b423fae70 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -131,7 +131,8 @@ public class DisplayModeDirectorTest { /* defaultRefreshRate= */ 0, /* defaultPeakRefreshRate= */ 0, /* defaultRefreshRateInHbmHdr= */ 0, - /* defaultRefreshRateInHbmSunlight= */ 0); + /* defaultRefreshRateInHbmSunlight= */ 0, + /* lowPowerSupportedModes =*/ List.of()); public static Collection<Object[]> getAppRequestedSizeTestCases() { var appRequestedSizeTestCases = Arrays.asList(new Object[][] { @@ -157,7 +158,7 @@ public class DisplayModeDirectorTest { APP_MODE_HIGH_90.getPhysicalHeight()), Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()), - Vote.PRIORITY_LOW_POWER_MODE, + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(), LIMIT_MODE_70.getPhysicalHeight()))}, {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(), @@ -169,7 +170,7 @@ public class DisplayModeDirectorTest { APP_MODE_65.getPhysicalHeight()), Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()), - Vote.PRIORITY_LOW_POWER_MODE, + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(), LIMIT_MODE_70.getPhysicalHeight()))}, {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(), @@ -181,7 +182,7 @@ public class DisplayModeDirectorTest { APP_MODE_65.getPhysicalHeight()), Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()), - Vote.PRIORITY_LOW_POWER_MODE, + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forSizeAndPhysicalRefreshRatesRange( 0, 0, LIMIT_MODE_70.getPhysicalWidth(), @@ -197,7 +198,7 @@ public class DisplayModeDirectorTest { APP_MODE_65.getPhysicalHeight()), Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()), - Vote.PRIORITY_LOW_POWER_MODE, + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forSizeAndPhysicalRefreshRatesRange( 0, 0, LIMIT_MODE_70.getPhysicalWidth(), @@ -213,7 +214,7 @@ public class DisplayModeDirectorTest { APP_MODE_HIGH_90.getPhysicalHeight()), Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()), - Vote.PRIORITY_LOW_POWER_MODE, + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forSizeAndPhysicalRefreshRatesRange( 0, 0, LIMIT_MODE_70.getPhysicalWidth(), @@ -229,7 +230,7 @@ public class DisplayModeDirectorTest { APP_MODE_HIGH_90.getPhysicalHeight()), Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()), - Vote.PRIORITY_LOW_POWER_MODE, + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forSizeAndPhysicalRefreshRatesRange( 0, 0, LIMIT_MODE_70.getPhysicalWidth(), @@ -245,7 +246,7 @@ public class DisplayModeDirectorTest { Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(APP_MODE_65.getPhysicalWidth(), APP_MODE_65.getPhysicalHeight()), - Vote.PRIORITY_LOW_POWER_MODE, + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forPhysicalRefreshRates( 0, 64.99f))}}); @@ -598,7 +599,7 @@ public class DisplayModeDirectorTest { < Vote.PRIORITY_APP_REQUEST_SIZE); assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH - > Vote.PRIORITY_LOW_POWER_MODE); + > Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE); Display.Mode[] modes = new Display.Mode[4]; modes[0] = new Display.Mode( @@ -676,9 +677,9 @@ public class DisplayModeDirectorTest { @Test public void testLPMHasHigherPriorityThanUser() { - assertTrue(Vote.PRIORITY_LOW_POWER_MODE + assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); - assertTrue(Vote.PRIORITY_LOW_POWER_MODE + assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE > Vote.PRIORITY_APP_REQUEST_SIZE); Display.Mode[] modes = new Display.Mode[4]; @@ -700,7 +701,7 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(2); @@ -715,7 +716,7 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(4); @@ -730,7 +731,7 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(2); @@ -745,7 +746,7 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), appRequestedMode.getPhysicalHeight())); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.baseModeId).isEqualTo(4); @@ -906,7 +907,7 @@ public class DisplayModeDirectorTest { Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, Vote.forRenderFrameRates(60, 60)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); @@ -946,7 +947,7 @@ public class DisplayModeDirectorTest { votesByDisplay.put(DISPLAY_ID, votes); votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, Vote.forRenderFrameRates(30, 90)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); assertThat(director.getModeSwitchingType()) @@ -987,7 +988,7 @@ public class DisplayModeDirectorTest { votesByDisplay.put(DISPLAY_ID, votes); votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, Vote.forRenderFrameRates(30, 90)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); assertThat(director.getModeSwitchingType()) @@ -1029,7 +1030,7 @@ public class DisplayModeDirectorTest { votesByDisplay.put(DISPLAY_ID, votes); votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, Vote.forRenderFrameRates(30, 90)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); assertThat(director.getModeSwitchingType()) @@ -1900,7 +1901,7 @@ public class DisplayModeDirectorTest { director.start(createMockSensorManager()); SparseArray<Vote> votes = new SparseArray<>(); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 50f)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 50f)); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID_2, votes); @@ -2298,7 +2299,7 @@ public class DisplayModeDirectorTest { votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(50); @@ -2311,7 +2312,7 @@ public class DisplayModeDirectorTest { votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90)); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(80); assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(80); @@ -2323,7 +2324,7 @@ public class DisplayModeDirectorTest { votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE, Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY)); votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90)); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90); assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90); @@ -2343,7 +2344,7 @@ public class DisplayModeDirectorTest { votesByDisplay.put(DISPLAY_ID, votes); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(70)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); @@ -2360,7 +2361,7 @@ public class DisplayModeDirectorTest { votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); @@ -2374,7 +2375,7 @@ public class DisplayModeDirectorTest { Vote.forRenderFrameRates(0, 52)); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); @@ -2392,7 +2393,7 @@ public class DisplayModeDirectorTest { Vote.forRenderFrameRates(0, 58)); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0); @@ -2521,7 +2522,7 @@ public class DisplayModeDirectorTest { votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(120, 120)); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(120); @@ -2542,7 +2543,7 @@ public class DisplayModeDirectorTest { SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60)); votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE, Vote.forRenderFrameRates(0, 30)); director.injectVotesByDisplay(votesByDisplay); @@ -3168,7 +3169,8 @@ public class DisplayModeDirectorTest { /* defaultRefreshRate= */ 60, /* defaultPeakRefreshRate= */ 65, /* defaultRefreshRateInHbmHdr= */ 65, - /* defaultRefreshRateInHbmSunlight= */ 75); + /* defaultRefreshRateInHbmSunlight= */ 75, + /* lowPowerSupportedModes= */ List.of()); when(displayDeviceConfig.getRefreshRateData()).thenReturn(refreshRateData); when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50); when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55); @@ -3390,9 +3392,10 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> displayListenerCaptor = ArgumentCaptor.forClass(DisplayListener.class); - verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), + verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class)); - DisplayListener displayListener = displayListenerCaptor.getValue(); + // DisplayObserver should register first + DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0); float refreshRate = 60; mInjector.mDisplayInfo.layoutLimitedRefreshRate = @@ -3417,9 +3420,10 @@ public class DisplayModeDirectorTest { ArgumentCaptor<DisplayListener> displayListenerCaptor = ArgumentCaptor.forClass(DisplayListener.class); - verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), + verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class)); - DisplayListener displayListener = displayListenerCaptor.getValue(); + // DisplayObserver should register first + DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0); mInjector.mDisplayInfo.layoutLimitedRefreshRate = new RefreshRateRange(10, 10); mInjector.mDisplayInfoValid = false; diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java index 2d317af3d85d..ee79d196cfd9 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java @@ -407,7 +407,8 @@ public class DisplayObserverTest { assertThat(mObserver).isNull(); mObserver = invocation.getArgument(0); return null; - }).when(mInjector).registerDisplayListener(any(), any()); + }).when(mInjector).registerDisplayListener( + any(DisplayModeDirector.DisplayObserver.class), any()); doAnswer(c -> { DisplayInfo info = c.getArgument(1); diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt index 4d910cefdb79..e431c8c3555c 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt @@ -27,8 +27,11 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.filters.SmallTest import com.android.internal.util.test.FakeSettingsProvider import com.android.server.display.DisplayDeviceConfig +import com.android.server.display.config.RefreshRateData +import com.android.server.display.config.SupportedModeData import com.android.server.display.feature.DisplayManagerFlags import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider +import com.android.server.display.mode.SupportedRefreshRatesVote.RefreshRates import com.android.server.testutils.TestHandler import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -69,6 +72,13 @@ private val RANGES_MIN90_90TO120 = RefreshRateRanges(RANGE_90_INF, RANGE_90_120) private val RANGES_MIN60_60TO90 = RefreshRateRanges(RANGE_60_INF, RANGE_60_90) private val RANGES_MIN90_90TO90 = RefreshRateRanges(RANGE_90_INF, RANGE_90_90) +private val LOW_POWER_GLOBAL_VOTE = Vote.forRenderFrameRates(0f, 60f) +private val LOW_POWER_REFRESH_RATE_DATA = createRefreshRateData( + lowPowerSupportedModes = listOf(SupportedModeData(60f, 60f), SupportedModeData(60f, 240f))) +private val LOW_POWER_EMPTY_REFRESH_RATE_DATA = createRefreshRateData() +private val EXPECTED_SUPPORTED_MODES_VOTE = SupportedRefreshRatesVote( + listOf(RefreshRates(60f, 60f), RefreshRates(60f, 240f))) + @SmallTest @RunWith(TestParameterInjector::class) class SettingsObserverTest { @@ -103,7 +113,7 @@ class SettingsObserverTest { val displayModeDirector = DisplayModeDirector( spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider) val ddcByDisplay = SparseArray<DisplayDeviceConfig>() - whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported) + whenever(mockDeviceConfig.refreshRateData).thenReturn(testCase.refreshRateData) ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig) displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay) val settingsObserver = displayModeDirector.SettingsObserver( @@ -113,27 +123,30 @@ class SettingsObserverTest { false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1) assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID, - Vote.PRIORITY_LOW_POWER_MODE)).isEqualTo(testCase.expectedVote) + Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE)).isEqualTo(testCase.globalVote) + assertThat(displayModeDirector.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_LOW_POWER_MODE_MODES)).isEqualTo(testCase.displayVote) } enum class LowPowerTestCase( - val vrrSupported: Boolean, + val refreshRateData: RefreshRateData, val vsyncLowPowerVoteEnabled: Boolean, val lowPowerModeEnabled: Boolean, - internal val expectedVote: Vote? + internal val globalVote: Vote?, + internal val displayVote: Vote? ) { - ALL_ENABLED(true, true, true, - SupportedRefreshRatesVote(listOf( - SupportedRefreshRatesVote.RefreshRates(60f, 240f), - SupportedRefreshRatesVote.RefreshRates(60f, 60f) - ))), - LOW_POWER_OFF(true, true, false, null), - DVRR_NOT_SUPPORTED_LOW_POWER_ON(false, true, true, - RefreshRateVote.RenderVote(0f, 60f)), - DVRR_NOT_SUPPORTED_LOW_POWER_OFF(false, true, false, null), - VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(true, false, true, - RefreshRateVote.RenderVote(0f, 60f)), - VSYNC_VOTE_DISABLED_LOW_POWER_OFF(true, false, false, null), + ALL_ENABLED(LOW_POWER_REFRESH_RATE_DATA, true, true, + LOW_POWER_GLOBAL_VOTE, EXPECTED_SUPPORTED_MODES_VOTE), + LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, true, false, + null, null), + EMPTY_REFRESH_LOW_POWER_ON(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, true, + LOW_POWER_GLOBAL_VOTE, null), + EMPTY_REFRESH__LOW_POWER_OFF(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, false, + null, null), + VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(LOW_POWER_REFRESH_RATE_DATA, false, true, + LOW_POWER_GLOBAL_VOTE, null), + VSYNC_VOTE_DISABLED_LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, false, false, + null, null), } @Test diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt index 6b90bde188c5..1206e30b9e88 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt @@ -16,6 +16,9 @@ package com.android.server.display.mode +import com.android.server.display.config.RefreshRateData +import com.android.server.display.config.SupportedModeData + internal fun createVotesSummary( isDisplayResolutionRangeVotingEnabled: Boolean = true, supportedModesVoteEnabled: Boolean = true, @@ -24,4 +27,16 @@ internal fun createVotesSummary( ): VoteSummary { return VoteSummary(isDisplayResolutionRangeVotingEnabled, supportedModesVoteEnabled, loggingEnabled, supportsFrameRateOverride) -}
\ No newline at end of file +} + +fun createRefreshRateData( + defaultRefreshRate: Int = 60, + defaultPeakRefreshRate: Int = 60, + defaultRefreshRateInHbmHdr: Int = 60, + defaultRefreshRateInHbmSunlight: Int = 60, + lowPowerSupportedModes: List<SupportedModeData> = emptyList() +): RefreshRateData { + return RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate, + defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight, + lowPowerSupportedModes) +} diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java index 88ab871529ee..874e99173c63 100644 --- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java +++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java @@ -273,28 +273,36 @@ public class DreamControllerTest { } @Test - public void setDreamHasFocus_true_dreamHasFocus() { + public void setDreamIsObscured_true_dreamIsNotFrontmost() { mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); - mDreamController.setDreamHasFocus(true); - assertTrue(mDreamController.dreamHasFocus()); + mDreamController.setDreamIsObscured(true); + assertFalse(mDreamController.dreamIsFrontmost()); } @Test - public void setDreamHasFocus_false_dreamDoesNotHaveFocus() { + public void setDreamIsObscured_false_dreamIsFrontmost() { mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); - mDreamController.setDreamHasFocus(false); - assertFalse(mDreamController.dreamHasFocus()); + mDreamController.setDreamIsObscured(false); + assertTrue(mDreamController.dreamIsFrontmost()); } @Test - public void setDreamHasFocus_notDreaming_dreamDoesNotHaveFocus() { - mDreamController.setDreamHasFocus(true); - // Dream still doesn't have focus because it was never started. - assertFalse(mDreamController.dreamHasFocus()); + public void setDreamIsObscured_notDreaming_dreamIsNotFrontmost() { + mDreamController.setDreamIsObscured(true); + // Dream still isn't frontmost because it was never started. + assertFalse(mDreamController.dreamIsFrontmost()); + } + + @Test + public void startDream_dreamIsFrontmost() { + mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/, + 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/); + + assertTrue(mDreamController.dreamIsFrontmost()); } private ServiceConnection captureServiceConnection() { diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index 27c522d68119..b56af87ee020 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -25,6 +25,13 @@ value="/data/local/tmp/cts/content/broken_shortcut.xml" /> </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <option name="force-skip-system-props" value="true" /> + <option name="set-global-setting" key="verifier_engprod" value="1" /> + <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" /> + <option name="restore-settings" value="true" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java index be5e2623ac20..c1ae85252ffc 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java @@ -24,8 +24,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.os.Looper; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -72,6 +74,11 @@ public class ActiveSourceActionTest { protected void writeStringSystemProperty(String key, String value) { // do nothing } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; Looper looper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java index 5be3c8e4671c..a5f7bb117e7d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java @@ -22,8 +22,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.Looper; import android.os.test.TestLooper; @@ -84,6 +86,11 @@ public class ArcInitiationActionFromAvrTest { protected Looper getServiceLooper() { return mTestLooper.getLooper(); } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java index 7845c307c15f..857ee1aa176f 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java @@ -22,8 +22,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; @@ -90,6 +92,11 @@ public class ArcTerminationActionFromAvrTest { protected Looper getServiceLooper() { return mTestLooper.getLooper(); } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; Looper looper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java index 98789ac96e98..6ace9f14757c 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java @@ -33,8 +33,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.DeviceFeatures; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; @@ -140,17 +142,8 @@ public abstract class BaseAbsoluteVolumeBehaviorTest { // do nothing } - /** - * Override displayOsd to prevent it from broadcasting an intent, which - * can trigger a SecurityException. - */ @Override - void displayOsd(int messageId) { - // do nothing - } - - @Override - void displayOsd(int messageId, int extra) { + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { // do nothing } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java index 9b65762e48ec..2dd593c8cb29 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java @@ -18,6 +18,8 @@ package com.android.server.hdmi; import static org.junit.Assert.assertEquals; import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.content.Intent; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.Looper; @@ -103,6 +105,11 @@ public class DetectTvSystemAudioModeSupportActionTest { protected Looper getServiceLooper() { return mTestLooper.getLooper(); } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java index 922706e16a75..e669e7c019d6 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java @@ -25,8 +25,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; @@ -90,6 +92,11 @@ public class DevicePowerStatusActionTest { protected void writeStringSystemProperty(String key, String value) { // do nothing } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; Looper looper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java index 68ef80fa62c9..29d20a64e439 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java @@ -30,7 +30,9 @@ import static com.android.server.hdmi.DeviceSelectActionFromPlayback.STATE_WAIT_ import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; @@ -123,6 +125,11 @@ public class DeviceSelectActionFromPlaybackTest { boolean isPowerStandby() { return false; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java index 26b448a491f2..d32b75bc57da 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java @@ -29,7 +29,9 @@ import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_RE import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -132,6 +134,11 @@ public class DeviceSelectActionFromTvTest { boolean isPowerStandby() { return false; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java index a621055a99eb..c7574bdc3f6c 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -98,6 +99,8 @@ public class HdmiCecAtomLoggingTest { audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager())); doNothing().when(mHdmiControlServiceSpy) .writeStringSystemProperty(anyString(), anyString()); + doNothing().when(mHdmiControlServiceSpy) + .sendBroadcastAsUser(any(Intent.class)); doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter(); HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy); doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java index 30ce9616d9b5..e1e101fc1724 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java @@ -19,6 +19,7 @@ import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_PENDING; import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_UNKNOWN; import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -30,6 +31,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -90,6 +92,8 @@ public class HdmiCecAtomLoggingTvTest { audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager())); doNothing().when(mHdmiControlServiceSpy) .writeStringSystemProperty(anyString(), anyString()); + doNothing().when(mHdmiControlServiceSpy) + .sendBroadcastAsUser(any(Intent.class)); doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter(); HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java index 0870bac6ef38..7ed596ea8bab 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java @@ -48,6 +48,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.Binder; @@ -110,6 +111,8 @@ public class HdmiCecControllerTest { doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion(); doNothing().when(mHdmiControlServiceSpy) .writeStringSystemProperty(anyString(), anyString()); + doNothing().when(mHdmiControlServiceSpy) + .sendBroadcastAsUser(any(Intent.class)); mHdmiControlServiceSpy.setDeviceConfig(new FakeDeviceConfigWrapper()); mNativeWrapper = new FakeNativeWrapper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index 55208972895d..5502de8f46e9 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -26,7 +26,9 @@ import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -114,6 +116,11 @@ public class HdmiCecLocalDeviceAudioSystemTest { return defVal; } } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiControlService.getHdmiCecConfig().setIntValue( diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 28da97c58383..8df7d548e21e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -28,7 +28,9 @@ import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -138,6 +140,11 @@ public class HdmiCecLocalDevicePlaybackTest { boolean canGoToStandby() { return true; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context)); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java index 3dd83125619a..192be2a4200b 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java @@ -37,7 +37,9 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.tv.cec.V1_0.Result; @@ -169,6 +171,11 @@ public class HdmiCecLocalDeviceTest { void wakeUp() { mWakeupMessageReceived = true; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiControlService.setIoLooper(mTestLooper.getLooper()); mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context)); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 4faeea50c1e1..b5f0a527de7b 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -41,7 +41,9 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -184,17 +186,8 @@ public class HdmiCecLocalDeviceTvTest { return mEarcBlocksArc; } - /** - * Override displayOsd to prevent it from broadcasting an intent, which - * can trigger a SecurityException. - */ @Override - void displayOsd(int messageId) { - // do nothing - } - - @Override - void displayOsd(int messageId, int extra) { + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { // do nothing } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java index c002067ae9e7..9412ee0d4ac7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java @@ -21,8 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -88,6 +90,11 @@ public class HdmiCecPowerStatusControllerTest { boolean isPowerStandby() { return false; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index e1b66b53ecbe..126a65863f59 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -52,6 +52,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -121,6 +122,8 @@ public class HdmiControlServiceTest { audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager())); doNothing().when(mHdmiControlServiceSpy) .writeStringSystemProperty(anyString(), anyString()); + doNothing().when(mHdmiControlServiceSpy) + .sendBroadcastAsUser(any(Intent.class)); mMyLooper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java index 46418026e540..298ff460c9eb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java @@ -26,8 +26,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; @@ -104,6 +106,11 @@ public class OneTouchPlayActionTest { protected void writeStringSystemProperty(String key, String value) { // do nothing } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; Looper looper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java index 9f0a44ce008a..1d4a72fc30e2 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java @@ -25,8 +25,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -78,6 +80,11 @@ public class PowerStatusMonitorActionTest { protected void writeStringSystemProperty(String key, String value) { // do nothing } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; Looper looper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java index 043db1eb298d..cafe1e7dc197 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java @@ -21,7 +21,9 @@ import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.os.Looper; @@ -115,6 +117,11 @@ public class RequestSadActionTest { boolean isPowerStandbyOrTransient() { return false; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiControlService.setIoLooper(mMyLooper); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java index 061e1f90fa58..864a182e8d0d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java @@ -21,7 +21,9 @@ import static com.android.server.hdmi.ResendCecCommandAction.SEND_COMMAND_RETRY_ import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; @@ -72,6 +74,11 @@ public class ResendCecCommandActionPlaybackTest { protected void writeStringSystemProperty(String key, String value) { // do nothing } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mIsPowerStandby = false; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java index b25ea2c5078c..06709cdd6ac4 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java @@ -21,7 +21,9 @@ import static com.android.server.hdmi.ResendCecCommandAction.SEND_COMMAND_RETRY_ import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.Looper; @@ -75,6 +77,11 @@ public class ResendCecCommandActionTvTest { boolean verifyPhysicalAddresses(HdmiCecMessage message) { return true; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mMyLooper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java index f608c235b0be..5163e29b86f1 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java @@ -29,7 +29,9 @@ import static com.android.server.hdmi.RoutingControlAction.STATE_WAIT_FOR_ROUTIN import static com.google.common.truth.Truth.assertThat; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; @@ -177,6 +179,11 @@ public class RoutingControlActionTest { protected HdmiCecConfig getHdmiCecConfig() { return hdmiCecConfig; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; mHdmiControlService.setIoLooper(mMyLooper); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java index a73f4aa35cf9..e4297effed92 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java @@ -24,12 +24,14 @@ import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.DeviceFeatures; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; @@ -87,6 +89,8 @@ public class SetAudioVolumeLevelDiscoveryActionTest { audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager())); doNothing().when(mHdmiControlServiceSpy) .writeStringSystemProperty(anyString(), anyString()); + doNothing().when(mHdmiControlServiceSpy) + .sendBroadcastAsUser(any(Intent.class)); mLooper = mTestLooper.getLooper(); mHdmiControlServiceSpy.setIoLooper(mLooper); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java index 02bed229d181..4dcc6a400c1e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java @@ -25,8 +25,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; import android.os.Looper; @@ -82,17 +84,8 @@ public class SystemAudioAutoInitiationActionTest { // do nothing } - /** - * Override displayOsd to prevent it from broadcasting an intent, which - * can trigger a SecurityException. - */ @Override - void displayOsd(int messageId) { - // do nothing - } - - @Override - void displayOsd(int messageId, int extra) { + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { // do nothing } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java index df27e7828385..4aa074b1a52b 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java @@ -23,7 +23,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.content.Context; +import android.content.Intent; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.Looper; @@ -143,6 +145,11 @@ public class SystemAudioInitiationActionFromAvrTest { protected boolean isStandbyMessageReceived() { return mStandbyMessageReceived; } + + @Override + protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { + // do nothing + } }; Looper looper = mTestLooper.getLooper(); diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java index 07fb9fc2f509..570256bf43e6 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java @@ -19,9 +19,16 @@ package com.android.server.net; import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; +import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW; +import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT; +import static android.net.ConnectivityManager.FIREWALL_RULE_DENY; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.util.DebugUtils.valueToString; import static org.junit.Assert.assertEquals; @@ -51,7 +58,10 @@ import android.os.PermissionEnforcer; import android.os.Process; import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; import androidx.test.filters.SmallTest; @@ -62,6 +72,7 @@ import com.android.modules.utils.build.SdkLevel; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -84,6 +95,9 @@ public class NetworkManagementServiceTest { @Mock private IBatteryStats.Stub mBatteryStatsService; @Mock private INetd.Stub mNetdService; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + private static final int TEST_UID = 111; @NonNull @@ -254,6 +268,7 @@ public class NetworkManagementServiceTest { } @Test + @DisableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS) public void testMeteredNetworkRestrictions() throws RemoteException { // Make sure the mocked netd method returns true. doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean()); @@ -295,6 +310,69 @@ public class NetworkManagementServiceTest { } @Test + @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS) + public void testMeteredNetworkRestrictionsByAdminChain() { + mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID, + FIREWALL_RULE_DENY); + verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID, + FIREWALL_RULE_DENY); + assertTrue("Should be true since mobile data usage is restricted by admin chain", + mNMService.isNetworkRestricted(TEST_UID)); + + mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID, + FIREWALL_RULE_DEFAULT); + verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID, + FIREWALL_RULE_DEFAULT); + assertFalse("Should be false since mobile data usage is no longer restricted by admin", + mNMService.isNetworkRestricted(TEST_UID)); + } + + @Test + @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS) + public void testMeteredNetworkRestrictionsByUserChain() { + mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID, + FIREWALL_RULE_DENY); + verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID, + FIREWALL_RULE_DENY); + assertTrue("Should be true since mobile data usage is restricted by user chain", + mNMService.isNetworkRestricted(TEST_UID)); + + mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID, + FIREWALL_RULE_DEFAULT); + verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID, + FIREWALL_RULE_DEFAULT); + assertFalse("Should be false since mobile data usage is no longer restricted by user", + mNMService.isNetworkRestricted(TEST_UID)); + } + + @Test + @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS) + public void testDataSaverRestrictionsWithAllowChain() { + mNMService.setDataSaverModeEnabled(true); + verify(mCm).setDataSaverEnabled(true); + + assertTrue("Should be true since data saver is on and the uid is not allowlisted", + mNMService.isNetworkRestricted(TEST_UID)); + + mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW); + verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW); + assertFalse("Should be false since data saver is on and the uid is allowlisted", + mNMService.isNetworkRestricted(TEST_UID)); + + // remove uid from allowlist and turn datasaver off again + + mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, + FIREWALL_RULE_DEFAULT); + verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, + FIREWALL_RULE_DEFAULT); + mNMService.setDataSaverModeEnabled(false); + verify(mCm).setDataSaverEnabled(false); + + assertFalse("Network should not be restricted when data saver is off", + mNMService.isNetworkRestricted(TEST_UID)); + } + + @Test public void testFirewallChains() { final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>(); // Dozable chain diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java index 4af20a97a9aa..a3d57c3df51e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java @@ -67,6 +67,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.UserInfo; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Icon; import android.media.AudioAttributes; @@ -93,6 +94,7 @@ import android.view.accessibility.IAccessibilityManagerClient; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; import com.android.internal.config.sysui.TestableFlagResolver; import com.android.internal.logging.InstanceIdSequence; @@ -210,6 +212,16 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt()); assertTrue(mAccessibilityManager.isEnabled()); + // Enable LED pulse setting by default + Settings.System.putInt(getContext().getContentResolver(), + Settings.System.NOTIFICATION_LIGHT_PULSE, 1); + + Resources resources = spy(getContext().getResources()); + when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(true); + when(resources.getBoolean( + com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true); + when(getContext().getResources()).thenReturn(resources); + // TODO (b/291907312): remove feature flag // Disable feature flags by default. Tests should enable as needed. mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, @@ -239,7 +251,6 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { mAttentionHelper.setKeyguardManager(mKeyguardManager); mAttentionHelper.setScreenOn(false); mAttentionHelper.setInCallStateOffHook(false); - mAttentionHelper.mNotificationPulseEnabled = true; if (Flags.crossAppPoliteNotifications()) { // Capture BroadcastReceiver for avalanche triggers @@ -611,6 +622,14 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt()); } + private void verifyAttentionLights() { + verify(mLight, times(1)).pulse(); + } + + private void verifyNeverAttentionLights() { + verify(mLight, never()).pulse(); + } + // // Tests // @@ -1524,7 +1543,25 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { @Test public void testLightsLightsOffGlobally() { - mAttentionHelper.mNotificationPulseEnabled = false; + Settings.System.putInt(getContext().getContentResolver(), + Settings.System.NOTIFICATION_LIGHT_PULSE, 0); + initAttentionHelper(mTestFlagResolver); + + NotificationRecord r = getLightsNotification(); + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyNeverLights(); + assertFalse(r.isInterruptive()); + assertEquals(-1, r.getLastAudiblyAlertedMs()); + } + + @Test + public void testLightsLightsResConfigDisabled() { + Resources resources = spy(getContext().getResources()); + when(resources.getBoolean( + com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(false); + when(getContext().getResources()).thenReturn(resources); + initAttentionHelper(mTestFlagResolver); + NotificationRecord r = getLightsNotification(); mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); verifyNeverLights(); @@ -1533,6 +1570,29 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase { } @Test + public void testLightsUseAttentionLight() { + NotificationRecord r = getLightsNotification(); + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyAttentionLights(); + assertEquals(-1, r.getLastAudiblyAlertedMs()); + } + + @Test + public void testLightsUseAttentionLightDisabled() { + Resources resources = spy(getContext().getResources()); + when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(false); + when(resources.getBoolean( + com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true); + when(getContext().getResources()).thenReturn(resources); + initAttentionHelper(mTestFlagResolver); + + NotificationRecord r = getLightsNotification(); + mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS); + verifyNeverAttentionLights(); + assertEquals(-1, r.getLastAudiblyAlertedMs()); + } + + @Test public void testLightsDndIntercepted() { NotificationRecord r = getLightsNotification(); r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS); 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 200952c05610..926a9947b741 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -15638,4 +15638,39 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false)) .isTrue(); } + + @Test + public void testClearUIJFromUninstallingPackage() throws Exception { + NotificationRecord r = + generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar"); + mService.addNotification(r); + + when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt())) + .thenThrow(PackageManager.NameNotFoundException.class); + when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false); + + mInternalService.cancelNotification(mPkg, mPkg, mUid, 0, r.getSbn().getTag(), + r.getSbn().getId(), mUserId); + + // no exception + } + + @Test + public void testPostFromMissingPackage_throws() throws Exception { + NotificationRecord r = + generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar"); + + when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt())) + .thenThrow(PackageManager.NameNotFoundException.class); + when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false); + + try { + mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(), + r.getSbn().getId(), r.getSbn().getNotification(), + r.getSbn().getUserId()); + fail("Allowed to post a notification for an absent package"); + } catch (SecurityException e) { + // yay + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java index 7380aecbf47b..d8d5729700ca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java @@ -65,7 +65,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { performSurfacePlacementAndWaitForWindowAnimator(); mImeProvider.scheduleShowImePostLayout(appWin, ImeTracker.Token.empty()); - assertTrue(mImeProvider.isReadyToShowIme()); + assertTrue(mImeProvider.isScheduledAndReadyToShowIme()); } /** @@ -84,13 +84,13 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { // Schedule (without triggering) after everything is ready. mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty()); - assertTrue(mImeProvider.isReadyToShowIme()); + assertTrue(mImeProvider.isScheduledAndReadyToShowIme()); assertFalse(mImeProvider.isImeShowing()); // Manually trigger the show. - mImeProvider.checkShowImePostLayout(); - // No longer ready as it was already shown. - assertFalse(mImeProvider.isReadyToShowIme()); + mImeProvider.checkAndStartShowImePostLayout(); + // No longer scheduled as it was already shown. + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertTrue(mImeProvider.isImeShowing()); } @@ -104,7 +104,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { // Schedule before anything is ready. mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty()); - assertFalse(mImeProvider.isReadyToShowIme()); + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertFalse(mImeProvider.isImeShowing()); final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime"); @@ -115,8 +115,8 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { mDisplayContent.updateImeInputAndControlTarget(target); // Performing surface placement picks up the show scheduled above. performSurfacePlacementAndWaitForWindowAnimator(); - // No longer ready as it was already shown. - assertFalse(mImeProvider.isReadyToShowIme()); + // No longer scheduled as it was already shown. + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertTrue(mImeProvider.isImeShowing()); } @@ -137,19 +137,19 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { // Schedule before starting the afterPrepareSurfacesRunnable. mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty()); - assertFalse(mImeProvider.isReadyToShowIme()); + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertFalse(mImeProvider.isImeShowing()); // This tries to pick up the show scheduled above, but must fail as the // afterPrepareSurfacesRunnable was not started yet. mDisplayContent.applySurfaceChangesTransaction(); - assertFalse(mImeProvider.isReadyToShowIme()); + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertFalse(mImeProvider.isImeShowing()); // Starting the afterPrepareSurfacesRunnable picks up the show scheduled above. mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); - // No longer ready as it was already shown. - assertFalse(mImeProvider.isReadyToShowIme()); + // No longer scheduled as it was already shown. + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertTrue(mImeProvider.isImeShowing()); } @@ -169,7 +169,7 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { // Schedule before surface placement. mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty()); - assertFalse(mImeProvider.isReadyToShowIme()); + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertFalse(mImeProvider.isImeShowing()); // Performing surface placement picks up the show scheduled above, and succeeds. @@ -177,8 +177,8 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { // applySurfaceChangesTransaction. Both of them try to trigger the show, // but only the second one can succeed, as it comes after onPostLayout. performSurfacePlacementAndWaitForWindowAnimator(); - // No longer ready as it was already shown. - assertFalse(mImeProvider.isReadyToShowIme()); + // No longer scheduled as it was already shown. + assertFalse(mImeProvider.isScheduledAndReadyToShowIme()); assertTrue(mImeProvider.isImeShowing()); } diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml index 1dc103765c34..82de070921f0 100644 --- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml +++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml @@ -80,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml index 57a58c8377ec..4ffb11ab92ae 100644 --- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml +++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml @@ -80,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml index 2cb86e05f68b..0fa4d07b2eca 100644 --- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml +++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml @@ -80,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml index 2cf85fa38e67..4d9fefbc7d88 100644 --- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml +++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml @@ -80,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml index b93e1bec21f8..b879c54dcab3 100644 --- a/tests/FlickerTests/IME/AndroidTestTemplate.xml +++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml @@ -82,6 +82,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/IME/OWNERS b/tests/FlickerTests/IME/OWNERS index ae1098d496df..e3a2e674ae7a 100644 --- a/tests/FlickerTests/IME/OWNERS +++ b/tests/FlickerTests/IME/OWNERS @@ -1,3 +1,3 @@ # ime # Bug component: 34867 -include /services/core/java/com/android/server/inputmethod/OWNERS +file:/services/core/java/com/android/server/inputmethod/OWNERS diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt index da8368f3cedf..2b6ddcb43f18 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt @@ -32,6 +32,9 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized +/** + * To run this test: `atest FlickerTestsIme1:CloseImeOnDismissPopupDialogTest` + */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt index 2f3ec6301215..0344197c1425 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt @@ -33,8 +33,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window closing to home transitions. To run this test: `atest - * FlickerTests:CloseImeWindowToHomeTest` + * Test IME window closing to home transitions. + * To run this test: `atest FlickerTestsIme1:CloseImeOnGoHomeTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt index 8821b69cdb3e..fde1373b032b 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt @@ -42,7 +42,7 @@ import org.junit.runners.Parameterized * * More details on b/190352379 * - * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest` + * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartOnGoHomeTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt index d75eba68c7cc..dc5013519dbf 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt @@ -42,7 +42,7 @@ import org.junit.runners.Parameterized * * More details on b/190352379 * - * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest` + * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartToAppOnPressBackTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt index 41d9e30a17ee..dc2bd1bc9996 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt @@ -34,8 +34,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window closing back to app window transitions. To run this test: `atest - * FlickerTests:CloseImeWindowToAppTest` + * Test IME window closing back to app window transitions. + * To run this test: `atest FlickerTestsIme1:CloseImeToAppOnPressBackTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt index 0e7fb7975df8..05771e88fc83 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized * Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify * there is no flickering when back to the simple activity without requesting IME to show. * - * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest` + * To run this test: `atest FlickerTestsIme1:CloseImeToHomeOnFinishActivityTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt index 47a7e1b65b2d..336fe6f991ca 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt @@ -36,8 +36,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window shown on the app with fixing portrait orientation. To run this test: `atest - * FlickerTests:OpenImeWindowToFixedPortraitAppTest` + * Test IME window shown on the app with fixing portrait orientation. + * To run this test: `atest FlickerTestsIme2:OpenImeWindowToFixedPortraitAppTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt index 48ec4d1fed2c..b8f11dcf8970 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt @@ -38,8 +38,9 @@ import org.junit.runners.Parameterized /** * Test IME window layer will become visible when switching from the fixed orientation activity - * (e.g. Launcher activity). To run this test: `atest - * FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest` + * (e.g. Launcher activity). + * To run this test: + * `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt index 03f3a68a573f..34a708578396 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt @@ -33,7 +33,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest` + * Test IME window opening transitions. + * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromOverviewTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt index 7b62c8967628..7c72c3187a7f 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt @@ -35,8 +35,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest - * FlickerTests:SwitchImeWindowsFromGestureNavTest` + * Test IME windows switching with 2-Buttons or gestural navigation. + * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt index 53bfb4ecf66f..fe5320cd1a46 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt @@ -36,7 +36,7 @@ import org.junit.runners.Parameterized /** * Launch an app that automatically displays the IME * - * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest` + * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppTest` * * Actions: * ``` diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt index d22bdcf25529..92b6b934874f 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt @@ -35,8 +35,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window closing on lock and opening on screen unlock. To run this test: `atest - * FlickerTests:CloseImeWindowToHomeTest` + * Test IME window closing on lock and opening on screen unlock. + * To run this test: `atest FlickerTestsIme2:ShowImeOnUnlockScreenTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt index 12290af8fd46..9eaf998ed63f 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt @@ -31,7 +31,10 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */ +/** + * Test IME window opening transitions. + * To run this test: `atest FlickerTestsIme2:ShowImeWhenFocusingOnInputFieldTest` + */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt index 0948351ac65b..7186a2c48c4c 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt @@ -41,7 +41,7 @@ import org.junit.runners.Parameterized /** * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity. - * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest` + * To run this test: `atest FlickerTestsIme2:ShowImeWhileDismissingThemedPopupDialogTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt index 7aa525fcccef..c96c760e2d7b 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt @@ -37,8 +37,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window layer will be associated with the app task when going to the overview screen. To - * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest` + * Test IME window layer will be associated with the app task when going to the overview screen. + * To run this test: `atest FlickerTestsIme2:ShowImeWhileEnteringOverviewTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml index 9c6a17d37a75..04b312a896b9 100644 --- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml +++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml @@ -80,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt index ffaeeadb1042..8c9ab9aadb8e 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized * * This test assumes the device doesn't have AOD enabled * - * To run this test: `atest FlickerTests:OpenAppFromLockNotificationCold` + * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationColdTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt index 6e67e193ed8c..e595100a2cbe 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized * * This test assumes the device doesn't have AOD enabled * - * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWarm` + * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWarmTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt index f1df8a68fb63..fbe1d34272c9 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt @@ -40,7 +40,8 @@ import org.junit.runners.Parameterized * * This test assumes the device doesn't have AOD enabled * - * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp` + * To run this test: + * `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWithOverlayAppTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt index b6d09d0bf3bb..c8ca644dde90 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt @@ -36,7 +36,7 @@ import org.junit.runners.Parameterized /** * Test cold launching an app from a notification. * - * To run this test: `atest FlickerTests:OpenAppFromNotificationCold` + * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationColdTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt index 1e607bfb2f49..c29e71ce4c79 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized /** * Test cold launching an app from a notification. * - * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm` + * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationWarmTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml index ecbed28085a2..8acdabc2337d 100644 --- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml +++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml @@ -80,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml index 1eacdfd89384..91ece214aad5 100644 --- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml +++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml @@ -80,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 9198ae184b18..3e500d9c8bd4 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -18,7 +18,10 @@ package="com.android.server.wm.flicker.testapp"> <uses-sdk android:minSdkVersion="29" - android:targetSdkVersion="29"/> + android:targetSdkVersion="35"/> + + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <application android:allowBackup="false" android:supportsRtl="true"> <uses-library android:name="androidx.window.extensions" android:required="false"/> @@ -107,7 +110,7 @@ android:immersive="true" android:resizeableActivity="true" android:screenOrientation="portrait" - android:theme="@android:style/Theme.NoTitleBar" + android:theme="@style/OptOutEdgeToEdge.NoTitleBar" android:configChanges="screenSize" android:label="PortraitImmersiveActivity" android:exported="true"> @@ -119,7 +122,7 @@ <activity android:name=".LaunchTransparentActivity" android:resizeableActivity="false" android:screenOrientation="portrait" - android:theme="@android:style/Theme" + android:theme="@style/OptOutEdgeToEdge" android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity" android:label="LaunchTransparentActivity" android:exported="true"> @@ -273,7 +276,7 @@ android:exported="true" android:label="MailActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity" - android:theme="@style/Theme.AppCompat.Light"> + android:theme="@style/OptOutEdgeToEdge.AppCompatTheme"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> @@ -282,7 +285,7 @@ <activity android:name=".GameActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity" android:immersive="true" - android:theme="@android:style/Theme.NoTitleBar" + android:theme="@style/OptOutEdgeToEdge.NoTitleBar" android:configChanges="screenSize" android:label="GameActivity" android:exported="true"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml index 9b742d96e35b..47d113717ae0 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml @@ -16,7 +16,19 @@ --> <resources> - <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="OptOutEdgeToEdge" parent="@android:style/Theme.DeviceDefault"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> + + <style name="OptOutEdgeToEdge.NoTitleBar" parent="@android:style/Theme.NoTitleBar"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> + + <style name="OptOutEdgeToEdge.AppCompatTheme" parent="@style/Theme.AppCompat.Light"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> + + <style name="DefaultTheme" parent="@style/OptOutEdgeToEdge"> <item name="android:windowBackground">@android:color/darker_gray</item> </style> @@ -32,7 +44,7 @@ <item name="android:windowLayoutInDisplayCutoutMode">never</item> </style> - <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="DialogTheme" parent="@style/OptOutEdgeToEdge"> <item name="android:windowAnimationStyle">@null</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@null</item> @@ -43,18 +55,18 @@ <item name="android:windowSoftInputMode">stateUnchanged</item> </style> - <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="TransparentTheme" parent="@style/OptOutEdgeToEdge"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:backgroundDimEnabled">false</item> </style> - <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault"> + <style name="no_starting_window" parent="@style/OptOutEdgeToEdge"> <item name="android:windowDisablePreview">true</item> </style> - <style name="SplashscreenAppTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="SplashscreenAppTheme" parent="@style/OptOutEdgeToEdge"> <!-- Splashscreen Attributes --> <item name="android:windowSplashScreenAnimatedIcon">@drawable/avd_anim</item> <!-- Here we want to match the duration of our AVD --> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java index c92b82b896f2..a86ba5f76374 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java @@ -125,7 +125,7 @@ public class BubbleHelper { .setContentTitle("BubbleChat") .setContentIntent(PendingIntent.getActivity(mContext, 0, new Intent(mContext, LaunchBubbleActivity.class), - PendingIntent.FLAG_UPDATE_CURRENT)) + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE)) .setStyle(new Notification.MessagingStyle(chatBot) .setConversationTitle("BubbleChat") .addMessage("BubbleChat", @@ -140,7 +140,7 @@ public class BubbleHelper { Intent target = new Intent(mContext, BubbleActivity.class); target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id); PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); return new Notification.BubbleMetadata.Builder() .setIntent(bubbleIntent) diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java index dea34442464d..37332c9712f5 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java @@ -17,6 +17,9 @@ package com.android.server.wm.flicker.testapp; +import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.app.Activity; import android.app.Person; import android.content.Context; @@ -24,6 +27,7 @@ import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.drawable.Icon; +import android.os.Build; import android.os.Bundle; import android.view.View; @@ -36,6 +40,13 @@ public class LaunchBubbleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) { + // POST_NOTIFICATIONS permission required for notification post sdk 33. + requestPermissions(new String[] { POST_NOTIFICATIONS }, 0); + } + addInboxShortcut(getApplicationContext()); mBubbleHelper = BubbleHelper.getInstance(this); setContentView(R.layout.activity_main); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java index a4dd5753539d..d6427abcc65a 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java @@ -16,6 +16,9 @@ package com.android.server.wm.flicker.testapp; +import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.app.Activity; import android.app.Notification; import android.app.NotificationChannel; @@ -23,6 +26,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.view.WindowManager; import android.widget.Button; @@ -34,6 +38,13 @@ public class NotificationActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) { + // POST_NOTIFICATIONS permission required for notification post sdk 33. + requestPermissions(new String[] { POST_NOTIFICATIONS }, 0); + } + WindowManager.LayoutParams p = getWindow().getAttributes(); p.layoutInDisplayCutoutMode = WindowManager.LayoutParams .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java index 1ab8ddbe20e2..27eb5a06451a 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java @@ -198,7 +198,7 @@ public class PipActivity extends Activity { filter.addAction(ACTION_SET_REQUESTED_ORIENTATION); filter.addAction(ACTION_ENTER_PIP); filter.addAction(ACTION_ASPECT_RATIO); - registerReceiver(mBroadcastReceiver, filter); + registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED); handleIntentExtra(getIntent()); } @@ -222,8 +222,8 @@ public class PipActivity extends Activity { private RemoteAction buildRemoteAction(Icon icon, String label, String action) { final Intent intent = new Intent(action); - final PendingIntent pendingIntent = - PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); return new RemoteAction(icon, label, label, pendingIntent); } diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt index 16785d1d9598..6b360b79c327 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt @@ -25,6 +25,7 @@ class AndroidHeuristicsFilter( val aidlPolicy: FilterPolicyWithReason?, val featureFlagsPolicy: FilterPolicyWithReason?, val syspropsPolicy: FilterPolicyWithReason?, + val rFilePolicy: FilterPolicyWithReason?, fallback: OutputFilter ) : DelegatingFilter(fallback) { override fun getPolicyForClass(className: String): FilterPolicyWithReason { @@ -37,6 +38,9 @@ class AndroidHeuristicsFilter( if (syspropsPolicy != null && classes.isSyspropsClass(className)) { return syspropsPolicy } + if (rFilePolicy != null && classes.isRClass(className)) { + return rFilePolicy + } return super.getPolicyForClass(className) } } @@ -74,3 +78,10 @@ private fun ClassNodes.isSyspropsClass(className: String): Boolean { return className.startsWith("android/sysprop/") && className.endsWith("Properties") } + +/** + * @return if a given class "seems like" an R class or its nested classes. + */ +private fun ClassNodes.isRClass(className: String): Boolean { + return className.endsWith("/R") || className.contains("/R$") +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index 75b5fc8f77ea..c5acd81f1cf2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -17,6 +17,7 @@ package com.android.hoststubgen.filters import com.android.hoststubgen.ParseException import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.toHumanReadableClassName import com.android.hoststubgen.log import com.android.hoststubgen.normalizeTextLine import com.android.hoststubgen.whitespaceRegex @@ -31,13 +32,17 @@ import java.util.Objects * Print a class node as a "keep" policy. */ fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) { - pw.printf("class %s\t%s\n", cn.name, "keep") + pw.printf("class %s %s\n", cn.name.toHumanReadableClassName(), "keep") - for (f in cn.fields ?: emptyList()) { - pw.printf(" field %s\t%s\n", f.name, "keep") + cn.fields?.let { + for (f in it.sortedWith(compareBy({ it.name }))) { + pw.printf(" field %s %s\n", f.name, "keep") + } } - for (m in cn.methods ?: emptyList()) { - pw.printf(" method %s\t%s\t%s\n", m.name, m.desc, "keep") + cn.methods?.let { + for (m in it.sortedWith(compareBy({ it.name }, { it.desc }))) { + pw.printf(" method %s %s %s\n", m.name, m.desc, "keep") + } } } @@ -66,6 +71,7 @@ fun createFilterFromTextPolicyFile( var aidlPolicy: FilterPolicyWithReason? = null var featureFlagsPolicy: FilterPolicyWithReason? = null var syspropsPolicy: FilterPolicyWithReason? = null + var rFilePolicy: FilterPolicyWithReason? = null try { BufferedReader(FileReader(filename)).use { reader -> @@ -162,6 +168,14 @@ fun createFilterFromTextPolicyFile( syspropsPolicy = policy.withReason( "$FILTER_REASON (special-class sysprops)") } + SpecialClass.RFile -> { + if (rFilePolicy != null) { + throw ParseException( + "Policy for R file already defined") + } + rFilePolicy = policy.withReason( + "$FILTER_REASON (special-class R file)") + } } } } @@ -225,13 +239,9 @@ fun createFilterFromTextPolicyFile( throw e.withSourceInfo(filename, lineNo) } - var ret: OutputFilter = imf - if (aidlPolicy != null || featureFlagsPolicy != null || syspropsPolicy != null) { - log.d("AndroidHeuristicsFilter enabled") - ret = AndroidHeuristicsFilter( - classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, imf) - } - return ret + // Wrap the in-memory-filter with AHF. + return AndroidHeuristicsFilter( + classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, imf) } } @@ -240,6 +250,7 @@ private enum class SpecialClass { Aidl, FeatureFlags, Sysprops, + RFile, } private fun resolveSpecialClass(className: String): SpecialClass { @@ -250,6 +261,7 @@ private fun resolveSpecialClass(className: String): SpecialClass { ":aidl" -> return SpecialClass.Aidl ":feature_flags" -> return SpecialClass.FeatureFlags ":sysprops" -> return SpecialClass.Sysprops + ":r" -> return SpecialClass.RFile } throw ParseException("Invalid special class name \"$className\"") } diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index fa8fe6cd384f..931f0c5fa793 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -322,6 +322,78 @@ NestMembers: InnerClasses: public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_1 + x: newarray int + x: dup + x: iconst_0 + x: iconst_1 + x: iastore + x: putstatic #x // Field ARRAY:[I + x: return + LineNumberTable: +} +SourceFile: "R.java" +NestHost: class com/android/hoststubgen/test/tinyframework/R +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +## Class: com/android/hoststubgen/test/tinyframework/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 3 + public com.android.hoststubgen.test.tinyframework.R(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R; +} +SourceFile: "R.java" +NestMembers: + com/android/hoststubgen/test/tinyframework/R$Nested +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class Compiled from "TinyFrameworkCallerCheck.java" class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt index c605f767f527..906a81cf45e3 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -122,6 +122,100 @@ RuntimeVisibleAnnotations: NestMembers: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/R +## Class: com/android/hoststubgen/test/tinyframework/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.R(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestMembers: + com/android/hoststubgen/test/tinyframework/R$Nested ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class Compiled from "TinyFrameworkCallerCheck.java" class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt index 11d5939b7917..10bc91da2544 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -348,6 +348,108 @@ RuntimeVisibleAnnotations: NestMembers: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_1 + x: newarray int + x: dup + x: iconst_0 + x: iconst_1 + x: iastore + x: putstatic #x // Field ARRAY:[I + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/R +## Class: com/android/hoststubgen/test/tinyframework/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.R(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestMembers: + com/android/hoststubgen/test/tinyframework/R$Nested ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class Compiled from "TinyFrameworkCallerCheck.java" class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt index c605f767f527..906a81cf45e3 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt @@ -122,6 +122,100 @@ RuntimeVisibleAnnotations: NestMembers: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/R +## Class: com/android/hoststubgen/test/tinyframework/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.R(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestMembers: + com/android/hoststubgen/test/tinyframework/R$Nested ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class Compiled from "TinyFrameworkCallerCheck.java" class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt index 088bc80e11c5..fcf9a8c663ad 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt @@ -481,6 +481,136 @@ RuntimeVisibleAnnotations: NestMembers: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested + x: ldc #x // String <init> + x: ldc #x // String ()V + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested + x: ldc #x // String <clinit> + x: ldc #x // String ()V + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: iconst_1 + x: newarray int + x: dup + x: iconst_0 + x: iconst_1 + x: iastore + x: putstatic #x // Field ARRAY:[I + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/R +## Class: com/android/hoststubgen/test/tinyframework/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 4 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return + + public com.android.hoststubgen.test.tinyframework.R(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R + x: ldc #x // String <init> + x: ldc #x // String ()V + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestMembers: + com/android/hoststubgen/test/tinyframework/R$Nested ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class Compiled from "TinyFrameworkCallerCheck.java" class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt index d30208452a40..696b6d009dc2 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt @@ -19,6 +19,9 @@ class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com # Heuristics rule: Stub all the AIDL classes. class :aidl stubclass +# Heuristics rule: Stub all the R classes. +class :r stubclass + # Default is "remove", so let's put all the base classes / interfaces in the stub first. class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java new file mode 100644 index 000000000000..b1bedf4b6853 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java @@ -0,0 +1,22 @@ +/* + * 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.hoststubgen.test.tinyframework; + +public class R { + public static class Nested { + public static int[] ARRAY = new int[] {1}; + } +} diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java index 762180dcf74b..37925e82bdb6 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.android.hoststubgen.test.tinyframework.R.Nested; import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass; import org.junit.Rule; @@ -328,4 +329,9 @@ public class TinyFrameworkClassTest { assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2); assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3); } + + @Test + public void testRFileHeuristics() { + assertThat(Nested.ARRAY.length).isEqualTo(1); + } } |