diff options
559 files changed, 10315 insertions, 11820 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index c6ce799f0a24..0080a8d63bfc 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -81,6 +81,7 @@ aconfig_declarations_group { "android.view.inputmethod.flags-aconfig-java", "android.webkit.flags-aconfig-java", "android.widget.flags-aconfig-java", + "android.xr.flags-aconfig-java", "art_exported_aconfig_flags_lib", "backstage_power_flags_lib", "backup_flags_lib", @@ -791,6 +792,12 @@ java_aconfig_library { ], } +cc_aconfig_library { + name: "android.permission.flags-aconfig-cc", + aconfig_declarations: "android.permission.flags-aconfig", + host_supported: true, +} + // SQLite aconfig_declarations { name: "android.database.sqlite-aconfig", @@ -893,6 +900,20 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// XR +aconfig_declarations { + name: "android.xr.flags-aconfig", + package: "android.xr", + container: "system", + srcs: ["core/java/android/content/pm/xr.aconfig"], +} + +java_aconfig_library { + name: "android.xr.flags-aconfig-java", + aconfig_declarations: "android.xr.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // android.app aconfig_declarations { name: "android.app.flags-aconfig", 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 03a3a0d51891..46cc3f01d261 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 @@ -498,14 +498,6 @@ public final class QuotaController extends StateController { private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; - private long mQuotaBumpAdditionalDurationMs = - QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS; - private int mQuotaBumpAdditionalJobCount = QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT; - private int mQuotaBumpAdditionalSessionCount = - QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT; - private long mQuotaBumpWindowSizeMs = QcConstants.DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS; - private int mQuotaBumpLimit = QcConstants.DEFAULT_QUOTA_BUMP_LIMIT; - /** * List of system apps with the {@link android.Manifest.permission#INSTALL_PACKAGES} permission * granted for each user. @@ -1095,7 +1087,7 @@ public final class QuotaController extends StateController { // essentially run until they reach the maximum limit. if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) { return calculateTimeUntilQuotaConsumedLocked( - events, startMaxElapsed, maxExecutionTimeRemainingMs, false); + events, startMaxElapsed, maxExecutionTimeRemainingMs); } // Need to check both max time and period time in case one is less than the other. @@ -1104,9 +1096,9 @@ public final class QuotaController extends StateController { // bucket value. return Math.min( calculateTimeUntilQuotaConsumedLocked( - events, startMaxElapsed, maxExecutionTimeRemainingMs, false), + events, startMaxElapsed, maxExecutionTimeRemainingMs), calculateTimeUntilQuotaConsumedLocked( - events, startWindowElapsed, allowedTimeRemainingMs, true)); + events, startWindowElapsed, allowedTimeRemainingMs)); } /** @@ -1116,36 +1108,12 @@ public final class QuotaController extends StateController { * @param deadSpaceMs How much time can be allowed to count towards the quota */ private long calculateTimeUntilQuotaConsumedLocked(@NonNull List<TimedEvent> sessions, - final long windowStartElapsed, long deadSpaceMs, boolean allowQuotaBumps) { + final long windowStartElapsed, long deadSpaceMs) { long timeUntilQuotaConsumedMs = 0; long start = windowStartElapsed; - int numQuotaBumps = 0; - final long quotaBumpWindowStartElapsed = - sElapsedRealtimeClock.millis() - mQuotaBumpWindowSizeMs; final int numSessions = sessions.size(); - if (allowQuotaBumps) { - for (int i = numSessions - 1; i >= 0; --i) { - TimedEvent event = sessions.get(i); - - if (event instanceof QuotaBump) { - if (event.getEndTimeElapsed() >= quotaBumpWindowStartElapsed - && numQuotaBumps++ < mQuotaBumpLimit) { - deadSpaceMs += mQuotaBumpAdditionalDurationMs; - } else { - break; - } - } - } - } for (int i = 0; i < numSessions; ++i) { - TimedEvent event = sessions.get(i); - - if (event instanceof QuotaBump) { - continue; - } - - TimingSession session = (TimingSession) event; - + TimingSession session = (TimingSession) sessions.get(i); if (session.endTimeElapsed < windowStartElapsed) { // Outside of window. Ignore. continue; @@ -1330,41 +1298,15 @@ public final class QuotaController extends StateController { final long startWindowElapsed = nowElapsed - stats.windowSizeMs; final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS; int sessionCountInWindow = 0; - int numQuotaBumps = 0; - final long quotaBumpWindowStartElapsed = nowElapsed - mQuotaBumpWindowSizeMs; // The minimum time between the start time and the beginning of the events that were // looked at --> how much time the stats will be valid for. long emptyTimeMs = Long.MAX_VALUE; // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get // the most recent ones. final int loopStart = events.size() - 1; - // Process QuotaBumps first to ensure the limits are properly adjusted. - for (int i = loopStart; i >= 0; --i) { - TimedEvent event = events.get(i); - - if (event.getEndTimeElapsed() < quotaBumpWindowStartElapsed - || numQuotaBumps >= mQuotaBumpLimit) { - break; - } - - if (event instanceof QuotaBump) { - stats.allowedTimePerPeriodMs += mQuotaBumpAdditionalDurationMs; - stats.jobCountLimit += mQuotaBumpAdditionalJobCount; - stats.sessionCountLimit += mQuotaBumpAdditionalSessionCount; - emptyTimeMs = Math.min(emptyTimeMs, - event.getEndTimeElapsed() - quotaBumpWindowStartElapsed); - numQuotaBumps++; - } - } TimingSession lastSeenTimingSession = null; for (int i = loopStart; i >= 0; --i) { - TimedEvent event = events.get(i); - - if (event instanceof QuotaBump) { - continue; - } - - TimingSession session = (TimingSession) event; + TimingSession session = (TimingSession) events.get(i); // Window management. if (startWindowElapsed < session.endTimeElapsed) { @@ -2058,28 +2000,6 @@ public final class QuotaController extends StateController { } @VisibleForTesting - static final class QuotaBump implements TimedEvent { - // Event timestamp in elapsed realtime timebase. - public final long eventTimeElapsed; - - QuotaBump(long eventElapsed) { - this.eventTimeElapsed = eventElapsed; - } - - @Override - public long getEndTimeElapsed() { - return eventTimeElapsed; - } - - @Override - public void dump(IndentingPrintWriter pw) { - pw.print("Quota bump @ "); - pw.print(eventTimeElapsed); - pw.println(); - } - } - - @VisibleForTesting static final class ShrinkableDebits { /** The amount of quota remaining. Can be negative if limit changes. */ private long mDebitTally; @@ -2528,21 +2448,6 @@ public final class QuotaController extends StateController { updateStandbyBucket(userId, packageName, bucketIndex); }); } - - @Override - public void triggerTemporaryQuotaBump(String packageName, @UserIdInt int userId) { - synchronized (mLock) { - List<TimedEvent> events = mTimingEvents.get(userId, packageName); - if (events == null || events.size() == 0) { - // If the app hasn't run any jobs, there's no point giving it a quota bump. - return; - } - events.add(new QuotaBump(sElapsedRealtimeClock.millis())); - invalidateAllExecutionStatsLocked(userId, packageName); - } - // Update jobs out of band. - mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget(); - } } @VisibleForTesting @@ -3019,7 +2924,6 @@ public final class QuotaController extends StateController { mQcConstants.mRateLimitingConstantsUpdated = false; mQcConstants.mExecutionPeriodConstantsUpdated = false; mQcConstants.mEJLimitConstantsUpdated = false; - mQcConstants.mQuotaBumpConstantsUpdated = false; } @Override @@ -3046,7 +2950,6 @@ public final class QuotaController extends StateController { private boolean mRateLimitingConstantsUpdated = false; private boolean mExecutionPeriodConstantsUpdated = false; private boolean mEJLimitConstantsUpdated = false; - private boolean mQuotaBumpConstantsUpdated = false; /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */ private static final String QC_CONSTANT_PREFIX = "qc_"; @@ -3194,21 +3097,6 @@ public final class QuotaController extends StateController { @VisibleForTesting static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS = QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS = - QC_CONSTANT_PREFIX + "quota_bump_additional_duration_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = - QC_CONSTANT_PREFIX + "quota_bump_additional_job_count"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = - QC_CONSTANT_PREFIX + "quota_bump_additional_session_count"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_WINDOW_SIZE_MS = - QC_CONSTANT_PREFIX + "quota_bump_window_size_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_LIMIT = - QC_CONSTANT_PREFIX + "quota_bump_limit"; private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = 10 * 60 * 1000L; // 10 minutes @@ -3281,11 +3169,6 @@ public final class QuotaController extends StateController { private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0; private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS; - private static final long DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS = 1 * MINUTE_IN_MILLIS; - private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = 2; - private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = 1; - private static final long DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS = 8 * HOUR_IN_MILLIS; - private static final int DEFAULT_QUOTA_BUMP_LIMIT = 8; /** * How much time each app in the exempted bucket will have to run jobs within their standby @@ -3587,33 +3470,6 @@ public final class QuotaController extends StateController { */ public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; - /** - * How much additional session duration to give an app for each accepted quota bump. - */ - public long QUOTA_BUMP_ADDITIONAL_DURATION_MS = DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS; - - /** - * How many additional regular jobs to give an app for each accepted quota bump. - */ - public int QUOTA_BUMP_ADDITIONAL_JOB_COUNT = DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT; - - /** - * How many additional sessions to give an app for each accepted quota bump. - */ - public int QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = - DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT; - - /** - * The rolling window size within which to accept and apply quota bump events. - */ - public long QUOTA_BUMP_WINDOW_SIZE_MS = DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS; - - /** - * The maximum number of quota bumps to accept and apply within the - * {@link #QUOTA_BUMP_WINDOW_SIZE_MS window}. - */ - public int QUOTA_BUMP_LIMIT = DEFAULT_QUOTA_BUMP_LIMIT; - public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { switch (key) { @@ -3650,14 +3506,6 @@ public final class QuotaController extends StateController { updateEJLimitConstantsLocked(); break; - case KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS: - case KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT: - case KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT: - case KEY_QUOTA_BUMP_WINDOW_SIZE_MS: - case KEY_QUOTA_BUMP_LIMIT: - updateQuotaBumpConstantsLocked(); - break; - case KEY_MAX_JOB_COUNT_EXEMPTED: MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED); int newExemptedMaxJobCount = @@ -4156,65 +4004,6 @@ public final class QuotaController extends StateController { } } - private void updateQuotaBumpConstantsLocked() { - if (mQuotaBumpConstantsUpdated) { - return; - } - mQuotaBumpConstantsUpdated = true; - - // Query the values as an atomic set. - final DeviceConfig.Properties properties = DeviceConfig.getProperties( - DeviceConfig.NAMESPACE_JOB_SCHEDULER, - KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - KEY_QUOTA_BUMP_WINDOW_SIZE_MS, KEY_QUOTA_BUMP_LIMIT); - QUOTA_BUMP_ADDITIONAL_DURATION_MS = properties.getLong( - KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS); - QUOTA_BUMP_ADDITIONAL_JOB_COUNT = properties.getInt( - KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT); - QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = properties.getInt( - KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT); - QUOTA_BUMP_WINDOW_SIZE_MS = properties.getLong( - KEY_QUOTA_BUMP_WINDOW_SIZE_MS, DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS); - QUOTA_BUMP_LIMIT = properties.getInt( - KEY_QUOTA_BUMP_LIMIT, DEFAULT_QUOTA_BUMP_LIMIT); - - // The window must be in the range [1 hour, 24 hours]. - long newWindowSizeMs = Math.max(HOUR_IN_MILLIS, - Math.min(MAX_PERIOD_MS, QUOTA_BUMP_WINDOW_SIZE_MS)); - if (mQuotaBumpWindowSizeMs != newWindowSizeMs) { - mQuotaBumpWindowSizeMs = newWindowSizeMs; - mShouldReevaluateConstraints = true; - } - // The limit must be nonnegative. - int newLimit = Math.max(0, QUOTA_BUMP_LIMIT); - if (mQuotaBumpLimit != newLimit) { - mQuotaBumpLimit = newLimit; - mShouldReevaluateConstraints = true; - } - // The job count must be nonnegative. - int newJobAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_JOB_COUNT); - if (mQuotaBumpAdditionalJobCount != newJobAddition) { - mQuotaBumpAdditionalJobCount = newJobAddition; - mShouldReevaluateConstraints = true; - } - // The session count must be nonnegative. - int newSessionAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_SESSION_COUNT); - if (mQuotaBumpAdditionalSessionCount != newSessionAddition) { - mQuotaBumpAdditionalSessionCount = newSessionAddition; - mShouldReevaluateConstraints = true; - } - // The additional duration must be in the range [0, 10 minutes]. - long newAdditionalDuration = Math.max(0, - Math.min(10 * MINUTE_IN_MILLIS, QUOTA_BUMP_ADDITIONAL_DURATION_MS)); - if (mQuotaBumpAdditionalDurationMs != newAdditionalDuration) { - mQuotaBumpAdditionalDurationMs = newAdditionalDuration; - mShouldReevaluateConstraints = true; - } - } - private void dump(IndentingPrintWriter pw) { pw.println(); pw.println("QuotaController:"); @@ -4277,15 +4066,6 @@ public final class QuotaController extends StateController { EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println(); pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - QUOTA_BUMP_ADDITIONAL_DURATION_MS).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, - QUOTA_BUMP_ADDITIONAL_JOB_COUNT).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - QUOTA_BUMP_ADDITIONAL_SESSION_COUNT).println(); - pw.print(KEY_QUOTA_BUMP_WINDOW_SIZE_MS, QUOTA_BUMP_WINDOW_SIZE_MS).println(); - pw.print(KEY_QUOTA_BUMP_LIMIT, QUOTA_BUMP_LIMIT).println(); - pw.decreaseIndent(); } @@ -4503,31 +4283,6 @@ public final class QuotaController extends StateController { return mQcConstants; } - @VisibleForTesting - long getQuotaBumpAdditionDurationMs() { - return mQuotaBumpAdditionalDurationMs; - } - - @VisibleForTesting - int getQuotaBumpAdditionJobCount() { - return mQuotaBumpAdditionalJobCount; - } - - @VisibleForTesting - int getQuotaBumpAdditionSessionCount() { - return mQuotaBumpAdditionalSessionCount; - } - - @VisibleForTesting - int getQuotaBumpLimit() { - return mQuotaBumpLimit; - } - - @VisibleForTesting - long getQuotaBumpWindowSizeMs() { - return mQuotaBumpWindowSizeMs; - } - //////////////////////////// DATA DUMP ////////////////////////////// @NeverCompile // Avoid size overhead of debugging code. diff --git a/core/api/current.txt b/core/api/current.txt index 6f60378d73e3..f59b57528ef6 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -56623,7 +56623,7 @@ package android.view.inputmethod { method public java.util.Map<android.view.inputmethod.InputMethodInfo,java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes(); method @Deprecated public void hideSoftInputFromInputMethod(android.os.IBinder, int); method public boolean hideSoftInputFromWindow(android.os.IBinder, int); - method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver); + method @Deprecated public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver); method @Deprecated public void hideStatusIcon(android.os.IBinder); method public void invalidateInput(@NonNull android.view.View); method public boolean isAcceptingText(); @@ -56647,7 +56647,7 @@ package android.view.inputmethod { method public void showInputMethodAndSubtypeEnabler(@Nullable String); method public void showInputMethodPicker(); method public boolean showSoftInput(android.view.View, int); - method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver); + method @Deprecated public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver); method @Deprecated public void showSoftInputFromInputMethod(android.os.IBinder, int); method @Deprecated public void showStatusIcon(android.os.IBinder, String, @DrawableRes int); method @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public void startConnectionlessStylusHandwriting(@NonNull android.view.View, @Nullable android.view.inputmethod.CursorAnchorInfo, @NonNull java.util.concurrent.Executor, @NonNull android.view.inputmethod.ConnectionlessHandwritingCallback); diff --git a/core/api/removed.txt b/core/api/removed.txt index 3c7c0d6e6ea1..a3cab291a1de 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -82,12 +82,12 @@ package android.database { package android.graphics { @Deprecated public class AvoidXfermode extends android.graphics.Xfermode { - ctor public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode); + ctor @Deprecated public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode); } - public enum AvoidXfermode.Mode { - enum_constant public static final android.graphics.AvoidXfermode.Mode AVOID; - enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET; + @Deprecated public enum AvoidXfermode.Mode { + enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode AVOID; + enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode TARGET; } public class Canvas { @@ -102,9 +102,9 @@ package android.graphics { } @Deprecated public class LayerRasterizer extends android.graphics.Rasterizer { - ctor public LayerRasterizer(); - method public void addLayer(android.graphics.Paint, float, float); - method public void addLayer(android.graphics.Paint); + ctor @Deprecated public LayerRasterizer(); + method @Deprecated public void addLayer(android.graphics.Paint, float, float); + method @Deprecated public void addLayer(android.graphics.Paint); } public class Paint { @@ -118,7 +118,7 @@ package android.graphics { } @Deprecated public class PixelXorXfermode extends android.graphics.Xfermode { - ctor public PixelXorXfermode(int); + ctor @Deprecated public PixelXorXfermode(int); } public class Rasterizer { @@ -170,14 +170,14 @@ package android.media.tv { package android.net { @Deprecated public class NetworkBadging { - method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); - field public static final int BADGING_4K = 30; // 0x1e - field public static final int BADGING_HD = 20; // 0x14 - field public static final int BADGING_NONE = 0; // 0x0 - field public static final int BADGING_SD = 10; // 0xa + method @Deprecated @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); + field @Deprecated public static final int BADGING_4K = 30; // 0x1e + field @Deprecated public static final int BADGING_HD = 20; // 0x14 + field @Deprecated public static final int BADGING_NONE = 0; // 0x0 + field @Deprecated public static final int BADGING_SD = 10; // 0xa } - @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging { + @Deprecated @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging { } public final class Proxy { @@ -304,14 +304,14 @@ package android.provider { @Deprecated public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns { field @Deprecated public static final String CONTENT_DIRECTORY = "stream_items"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns { field @Deprecated public static final String PHOTO = "photo"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated protected static interface ContactsContract.StreamItemPhotosColumns { @@ -332,16 +332,16 @@ package android.provider { field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item"; field @Deprecated public static final android.net.Uri CONTENT_URI; field @Deprecated public static final String MAX_ITEMS = "max_items"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns { field @Deprecated public static final String CONTENT_DIRECTORY = "photo"; field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo"; field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated protected static interface ContactsContract.StreamItemsColumns { @@ -447,14 +447,14 @@ package android.text.style { package android.util { @Deprecated public class FloatMath { - method public static float ceil(float); - method public static float cos(float); - method public static float exp(float); - method public static float floor(float); - method public static float hypot(float, float); - method public static float pow(float, float); - method public static float sin(float); - method public static float sqrt(float); + method @Deprecated public static float ceil(float); + method @Deprecated public static float cos(float); + method @Deprecated public static float exp(float); + method @Deprecated public static float floor(float); + method @Deprecated public static float hypot(float, float); + method @Deprecated public static float pow(float, float); + method @Deprecated public static float sin(float); + method @Deprecated public static float sqrt(float); } } diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index bbfa0ec3f3c2..78b9994e8fa1 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -7,8 +7,8 @@ package android.app { } @Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback { - ctor public AppOpsManager.AppOpsCollector(); - method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); + ctor @Deprecated public AppOpsManager.AppOpsCollector(); + method @Deprecated @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); } public class Notification implements android.os.Parcelable { @@ -207,7 +207,7 @@ package android.service.translation { @Deprecated public static interface TranslationService.OnTranslationResultCallback { method @Deprecated public void onError(); - method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); + method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); } } @@ -261,64 +261,64 @@ package android.telephony.ims { } @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable { - method public boolean containsKey(@NonNull String); - method @NonNull public android.os.PersistableBundle copyBundle(); - method public int describeContents(); - method public boolean getBoolean(@NonNull String, boolean); - method public int getInt(@NonNull String, int); - method @Nullable public String getString(@NonNull String); - method public long getVersion(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; - field public static final String IPTYPE_IPV4 = "IPV4"; - field public static final String IPTYPE_IPV6 = "IPV6"; - field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; - field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; - field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; - field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; - field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; - field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; - field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; - field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; - field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; - field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; - field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; - field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; - field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; - field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; - field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; - field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; - field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; - field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; - field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; - field public static final String SIP_TRANSPORT_TCP = "TCP"; - field public static final String SIP_TRANSPORT_UDP = "UDP"; - } - - public static final class SipDelegateImsConfiguration.Builder { - ctor public SipDelegateImsConfiguration.Builder(int); - ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); + method @Deprecated public boolean containsKey(@NonNull String); + method @Deprecated @NonNull public android.os.PersistableBundle copyBundle(); + method @Deprecated public int describeContents(); + method @Deprecated public boolean getBoolean(@NonNull String, boolean); + method @Deprecated public int getInt(@NonNull String, int); + method @Deprecated @Nullable public String getString(@NonNull String); + method @Deprecated public long getVersion(); + method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; + field @Deprecated public static final String IPTYPE_IPV4 = "IPV4"; + field @Deprecated public static final String IPTYPE_IPV6 = "IPV6"; + field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; + field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP"; + field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP"; + } + + @Deprecated public static final class SipDelegateImsConfiguration.Builder { + ctor @Deprecated public SipDelegateImsConfiguration.Builder(int); + ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); } } diff --git a/core/java/Android.bp b/core/java/Android.bp index 1265de1ebb15..65cd984ac1ba 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -214,6 +214,9 @@ filegroup { aidl_interface { name: "android.os.hintmanager_aidl", + defaults: [ + "android.hardware.power-aidl", + ], srcs: [ "android/os/IHintManager.aidl", "android/os/IHintSession.aidl", @@ -231,9 +234,6 @@ aidl_interface { enabled: true, }, }, - imports: [ - "android.hardware.power-V5", - ], } aidl_library { diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 11e885055162..37fa9a26b91c 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -6,6 +6,14 @@ container: "system" # flag relates to. flag { + name: "notifications_redesign_app_icons" + namespace: "systemui" + description: "Notifications Redesign: Use app icons in notification rows (not to be confused with" + " notifications_use_app_icons, notifications_use_app_icon_in_row which are just experiments)." + bug: "371174789" +} + +flag { name: "modes_api" is_exported: true namespace: "systemui" diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index c7d7dc1eb0de..52d733314eb6 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -326,3 +326,11 @@ flag { bug: "360129103" is_fixed_read_only: true } + +flag { + name: "include_feature_flags_in_package_cacher" + namespace: "package_manager_service" + description: "Include feature flag status when determining hits or misses in PackageCacher." + bug: "364771256" + is_fixed_read_only: true +} diff --git a/core/java/android/content/pm/xr.aconfig b/core/java/android/content/pm/xr.aconfig new file mode 100644 index 000000000000..61835c162c49 --- /dev/null +++ b/core/java/android/content/pm/xr.aconfig @@ -0,0 +1,9 @@ +package: "android.xr" +container: "system" + +flag { + namespace: "xr" + name: "xr_manifest_entries" + description: "Adds manifest entries used by Android XR" + bug: "364416355" +}
\ No newline at end of file diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig index 99eeca98bce0..f03a4a954842 100644 --- a/core/java/android/hardware/input/input_framework.aconfig +++ b/core/java/android/hardware/input/input_framework.aconfig @@ -142,3 +142,10 @@ flag { description: "Controls whether the connected mice's primary buttons, left and right, can be swapped." bug: "352598211" } + +flag { + name: "keyboard_a11y_shortcut_control" + namespace: "input" + description: "Adds shortcuts to toggle and control a11y features" + bug: "373458181" +} diff --git a/core/java/android/hardware/radio/RadioAlert.aidl b/core/java/android/hardware/radio/RadioAlert.aidl new file mode 100644 index 000000000000..17f4fc7e9a13 --- /dev/null +++ b/core/java/android/hardware/radio/RadioAlert.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +/** @hide */ +parcelable RadioAlert;
\ No newline at end of file diff --git a/core/java/android/hardware/radio/RadioAlert.java b/core/java/android/hardware/radio/RadioAlert.java new file mode 100644 index 000000000000..b55dcd82ef7b --- /dev/null +++ b/core/java/android/hardware/radio/RadioAlert.java @@ -0,0 +1,505 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +import android.annotation.FlaggedApi; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Emergency Alert Message + * + * <p>Alert message can be sent from a radio station of technologies such as HD radio to + * the radio users for some emergency events (see ITU-T X.1303 bis for more info). + * @hide + */ +@FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) +public final class RadioAlert implements Parcelable { + + public static final class Geocode implements Parcelable { + + private final String mValueName; + private final String mValue; + + /** + * Constructor of geocode. + * + * @param valueName Name of geocode value + * @param value Value of geocode + * @hide + */ + public Geocode(@NonNull String valueName, @NonNull String value) { + mValueName = Objects.requireNonNull(valueName, "Geocode value name can not be null"); + mValue = Objects.requireNonNull(value, "Geocode value can not be null"); + } + + private Geocode(Parcel in) { + mValueName = in.readString8(); + mValue = in.readString8(); + } + + public static final @NonNull Creator<Geocode> CREATOR = new Creator<Geocode>() { + @Override + public Geocode createFromParcel(Parcel in) { + return new Geocode(in); + } + + @Override + public Geocode[] newArray(int size) { + return new Geocode[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mValueName); + dest.writeString8(mValue); + } + + @NonNull + @Override + public String toString() { + return "Gecode [valueName=" + mValueName + ", value=" + mValue + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mValueName, mValue); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Geocode other)) { + return false; + } + + return Objects.equals(mValueName, other.mValueName) + && Objects.equals(mValue, other.mValue); + } + } + + public static final class Coordinate implements Parcelable { + private final double mLatitude; + private final double mLongitude; + + /** + * Constructor of coordinate. + * + * @param latitude Latitude of the coordinate + * @param longitude Longitude of the coordinate + * @hide + */ + public Coordinate(double latitude, double longitude) { + if (latitude < -90.0 || latitude > 90.0) { + throw new IllegalArgumentException("Latitude value should be between -90 and 90"); + } + if (longitude < -180.0 || longitude > 180.0) { + throw new IllegalArgumentException( + "Longitude value should be between -180 and 180"); + } + mLatitude = latitude; + mLongitude = longitude; + } + + private Coordinate(Parcel in) { + mLatitude = in.readDouble(); + mLongitude = in.readDouble(); + } + + public static final @NonNull Creator<Coordinate> CREATOR = new Creator<Coordinate>() { + @Override + public Coordinate createFromParcel(Parcel in) { + return new Coordinate(in); + } + + @Override + public Coordinate[] newArray(int size) { + return new Coordinate[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeDouble(mLatitude); + dest.writeDouble(mLongitude); + } + + @NonNull + @Override + public String toString() { + return "Coordinate [latitude=" + mLatitude + ", longitude=" + mLongitude + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mLatitude, mLongitude); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Coordinate other)) { + return false; + } + return mLatitude == other.mLatitude && mLongitude == other.mLongitude; + } + } + + public static final class Polygon implements Parcelable { + + private final List<Coordinate> mCoordinates; + + /** + * Constructor of polygon. + * + * @param coordinates Coordinates the polygon is composed of + * @hide + */ + public Polygon(@NonNull List<Coordinate> coordinates) { + Objects.requireNonNull(coordinates, "Coordinates can not be null"); + if (coordinates.size() < 4) { + throw new IllegalArgumentException("Number of coordinates must be at least 4"); + } + if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) { + throw new IllegalArgumentException( + "The last and first coordinates must be the same"); + } + mCoordinates = coordinates; + } + + private Polygon(Parcel in) { + mCoordinates = new ArrayList<>(); + in.readTypedList(mCoordinates, Coordinate.CREATOR); + } + + public static final @NonNull Creator<Polygon> CREATOR = new Creator<Polygon>() { + @Override + public Polygon createFromParcel(Parcel in) { + return new Polygon(in); + } + + @Override + public Polygon[] newArray(int size) { + return new Polygon[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mCoordinates); + } + + @NonNull + @Override + public String toString() { + return "Polygon [coordinates=" + mCoordinates + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mCoordinates); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Polygon other)) { + return false; + } + return mCoordinates.equals(other.mCoordinates); + } + } + + public static final class AlertArea implements Parcelable { + + private final List<Polygon> mPolygons; + private final List<Geocode> mGeocodes; + + /** + * Constructor of alert area. + * + * @param polygons Polygons used in alert area + * @param geocodes Geocodes used in alert area + * @hide + */ + public AlertArea(@NonNull List<Polygon> polygons, @NonNull List<Geocode> geocodes) { + mPolygons = Objects.requireNonNull(polygons, "Polygons can not be null"); + mGeocodes = Objects.requireNonNull(geocodes, "Geocodes can not be null"); + } + + private AlertArea(Parcel in) { + mPolygons = new ArrayList<>(); + mGeocodes = new ArrayList<>(); + in.readTypedList(mPolygons, Polygon.CREATOR); + in.readTypedList(mGeocodes, Geocode.CREATOR); + } + + public static final @NonNull Creator<AlertArea> CREATOR = new Creator<AlertArea>() { + @Override + public AlertArea createFromParcel(Parcel in) { + return new AlertArea(in); + } + + @Override + public AlertArea[] newArray(int size) { + return new AlertArea[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mPolygons); + dest.writeTypedList(mGeocodes); + } + + @NonNull + @Override + public String toString() { + return "AlertArea [polygons=" + mPolygons + ", geocodes=" + mGeocodes + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mPolygons, mGeocodes); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AlertArea other)) { + return false; + } + + return mPolygons.equals(other.mPolygons) && mGeocodes.equals(other.mGeocodes); + } + } + + public static final class AlertInfo implements Parcelable { + + private final List<Integer> mCategoryList; + private final int mUrgency; + private final int mSeverity; + private final int mCertainty; + private final String mTextualMessage; + private final List<AlertArea> mAreaList; + + /** + * Constructor for alert info. + * + * @param categoryList Array of categories of the subject event of the alert message + * @param urgency The urgency of the subject event of the alert message + * @param severity The severity of the subject event of the alert message + * @param certainty The certainty of the subject event of the alert message + * @param textualMessage Textual descriptions of the subject event + * @param areaList The array of geographic areas to which the alert info segment in which + * it appears applies + * @hide + */ + public AlertInfo(@NonNull List<Integer> categoryList, int urgency, + int severity, int certainty, + String textualMessage, @NonNull List<AlertArea> areaList) { + mCategoryList = Objects.requireNonNull(categoryList, "Category list can not be null"); + mUrgency = urgency; + mSeverity = severity; + mCertainty = certainty; + mTextualMessage = textualMessage; + mAreaList = Objects.requireNonNull(areaList, "Area list can not be null"); + } + + private AlertInfo(Parcel in) { + mCategoryList = in.readArrayList(Integer.class.getClassLoader(), Integer.class); + mUrgency = in.readInt(); + mSeverity = in.readInt(); + mCertainty = in.readInt(); + mTextualMessage = in.readString8(); + mAreaList = new ArrayList<>(); + in.readTypedList(mAreaList, AlertArea.CREATOR); + } + + public static final @NonNull Creator<AlertInfo> CREATOR = new Creator<AlertInfo>() { + @Override + public AlertInfo createFromParcel(Parcel in) { + return new AlertInfo(in); + } + + @Override + public AlertInfo[] newArray(int size) { + return new AlertInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(mCategoryList); + dest.writeInt(mUrgency); + dest.writeInt(mSeverity); + dest.writeInt(mCertainty); + dest.writeString8(mTextualMessage); + dest.writeTypedList(mAreaList); + } + + @NonNull + @Override + public String toString() { + return "AlertInfo [categoryList=" + mCategoryList + ", urgency=" + mUrgency + + ", severity=" + mSeverity + ", certainty=" + mCertainty + + ", textualMessage=" + mTextualMessage + ", areaList=" + mAreaList + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mCategoryList, mUrgency, mSeverity, mCertainty, mTextualMessage, + mAreaList); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AlertInfo other)) { + return false; + } + + return mCategoryList.equals(other.mCategoryList) && mUrgency == other.mUrgency + && mSeverity == other.mSeverity && mCertainty == other.mCertainty + && mTextualMessage.equals(other.mTextualMessage) + && mAreaList.equals(other.mAreaList); + } + } + + private final int mStatus; + private final int mMessageType; + private final List<AlertInfo> mInfoList; + private final int mScope; + + /** + * Constructor of radio alert message. + * + * @param status Status of alert message + * @param messageType Message type of alert message + * @param infoList List of alert info + * @param scope Scope of alert message + * @hide + */ + public RadioAlert(int status, int messageType, + @NonNull List<AlertInfo> infoList, int scope) { + mStatus = status; + mMessageType = messageType; + mInfoList = Objects.requireNonNull(infoList, "Alert info list can not be null"); + mScope = scope; + } + + private RadioAlert(Parcel in) { + mStatus = in.readInt(); + mMessageType = in.readInt(); + mInfoList = in.readParcelableList(new ArrayList<>(), AlertInfo.class.getClassLoader(), + AlertInfo.class); + mScope = in.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStatus); + dest.writeInt(mMessageType); + dest.writeParcelableList(mInfoList, /* flags= */ 0); + dest.writeInt(mScope); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + return "RadioAlert [status=" + mStatus + ", messageType=" + mMessageType + + ", infoList= " + mInfoList + ", scope=" + mScope + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mStatus, mMessageType, mInfoList, mScope); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof RadioAlert other)) { + return false; + } + + return mStatus == other.mStatus && mMessageType == other.mMessageType + && mInfoList.equals(other.mInfoList) && mScope == other.mScope; + } + + public static final @NonNull Creator<RadioAlert> CREATOR = new Creator<RadioAlert>() { + @Override + public RadioAlert createFromParcel(Parcel in) { + return new RadioAlert(in); + } + + @Override + public RadioAlert[] newArray(int size) { + return new RadioAlert[size]; + } + }; +} diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java index 77d6cb762e06..f1d3957cc919 100644 --- a/core/java/android/os/CombinedVibration.java +++ b/core/java/android/os/CombinedVibration.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.vibrator.Flags; import android.util.SparseArray; @@ -28,6 +29,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.StringJoiner; +import java.util.function.Function; /** * A CombinedVibration describes a combination of haptic effects to be performed by one or more @@ -114,6 +116,17 @@ public abstract class CombinedVibration implements Parcelable { public abstract long getDuration(); /** + * Gets the estimated duration of the combined vibration in milliseconds. + * + * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns + * the estimated duration based on the {@link VibratorInfo}. For all other effects this will + * return the same as {@link #getDuration()}. + * + * @hide + */ + public abstract long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos); + + /** * Returns true if this effect could represent a touch haptic feedback. * * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified @@ -383,6 +396,23 @@ public abstract class CombinedVibration implements Parcelable { /** @hide */ @Override + public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) { + if (vibratorInfos == null) { + return getDuration(); + } + long maxDuration = 0; + for (int i = 0; i < vibratorInfos.size(); i++) { + long duration = mEffect.getDuration(vibratorInfos.valueAt(i)); + if ((duration == Long.MAX_VALUE) || (duration < 0)) { + return duration; + } + maxDuration = Math.max(maxDuration, duration); + } + return maxDuration; + } + + /** @hide */ + @Override public boolean isHapticFeedbackCandidate() { return mEffect.isHapticFeedbackCandidate(); } @@ -531,10 +561,27 @@ public abstract class CombinedVibration implements Parcelable { @Override public long getDuration() { + return getDuration(idx -> mEffects.valueAt(idx).getDuration()); + } + + /** @hide */ + @Override + public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) { + if (vibratorInfos == null) { + return getDuration(); + } + return getDuration(idx -> { + VibrationEffect effect = mEffects.valueAt(idx); + VibratorInfo info = vibratorInfos.get(mEffects.keyAt(idx)); + return effect.getDuration(info); + }); + } + + private long getDuration(Function<Integer, Long> durationFn) { long maxDuration = Long.MIN_VALUE; boolean hasUnknownStep = false; for (int i = 0; i < mEffects.size(); i++) { - long duration = mEffects.valueAt(i).getDuration(); + long duration = durationFn.apply(i); if (duration == Long.MAX_VALUE) { // If any duration is repeating, this combination duration is also repeating. return duration; @@ -750,12 +797,21 @@ public abstract class CombinedVibration implements Parcelable { @Override public long getDuration() { + return getDuration(CombinedVibration::getDuration); + } + + /** @hide */ + @Override + public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) { + return getDuration(effect -> effect.getDuration(vibratorInfos)); + } + + private long getDuration(Function<CombinedVibration, Long> durationFn) { boolean hasUnknownStep = false; long durations = 0; final int effectCount = mEffects.size(); for (int i = 0; i < effectCount; i++) { - CombinedVibration effect = mEffects.get(i); - long duration = effect.getDuration(); + long duration = durationFn.apply(mEffects.get(i)); if (duration == Long.MAX_VALUE) { // If any duration is repeating, this combination duration is also repeating. return duration; diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index ffc58c537f2a..61dd11fd4122 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -55,6 +55,7 @@ import java.util.Locale; import java.util.Objects; import java.util.StringJoiner; import java.util.function.BiFunction; +import java.util.function.Function; /** * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}. @@ -565,6 +566,19 @@ public abstract class VibrationEffect implements Parcelable { public abstract long getDuration(); /** + * Gets the estimated duration of the segment for given vibrator, in milliseconds. + * + * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns + * the estimated duration based on the given {@link VibratorInfo}. For all other effects this + * will return the same as {@link #getDuration()}. + * + * @hide + */ + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + return getDuration(); + } + + /** * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended. * * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more @@ -904,13 +918,23 @@ public abstract class VibrationEffect implements Parcelable { @Override public long getDuration() { + return getDuration(VibrationEffectSegment::getDuration); + } + + /** @hide */ + @Override + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + return getDuration(segment -> segment.getDuration(vibratorInfo)); + } + + private long getDuration(Function<VibrationEffectSegment, Long> durationFn) { if (mRepeatIndex >= 0) { return Long.MAX_VALUE; } int segmentCount = mSegments.size(); long totalDuration = 0; for (int i = 0; i < segmentCount; i++) { - long segmentDuration = mSegments.get(i).getDuration(); + long segmentDuration = durationFn.apply(mSegments.get(i)); if (segmentDuration < 0) { return segmentDuration; } diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java index 39f841226e4e..b17e82a704bf 100644 --- a/core/java/android/os/vibrator/PrebakedSegment.java +++ b/core/java/android/os/vibrator/PrebakedSegment.java @@ -16,6 +16,17 @@ package android.os.vibrator; +import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; +import static android.os.VibrationEffect.Composition.PRIMITIVE_THUD; +import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK; +import static android.os.VibrationEffect.EFFECT_CLICK; +import static android.os.VibrationEffect.EFFECT_DOUBLE_CLICK; +import static android.os.VibrationEffect.EFFECT_HEAVY_CLICK; +import static android.os.VibrationEffect.EFFECT_POP; +import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK; +import static android.os.VibrationEffect.EFFECT_THUD; +import static android.os.VibrationEffect.EFFECT_TICK; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -78,6 +89,32 @@ public final class PrebakedSegment extends VibrationEffectSegment { /** @hide */ @Override + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + if (vibratorInfo == null) { + return getDuration(); + } + return switch (mEffectId) { + case EFFECT_TICK, + EFFECT_CLICK, + EFFECT_HEAVY_CLICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_CLICK); + case EFFECT_TEXTURE_TICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_TICK); + case EFFECT_THUD -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_THUD); + case EFFECT_DOUBLE_CLICK -> { + long clickDuration = vibratorInfo.getPrimitiveDuration(PRIMITIVE_CLICK); + yield clickDuration > 0 ? 2 * clickDuration : getDuration(); + } + default -> getDuration(); + }; + } + + private long estimateFromPrimitiveDuration(VibratorInfo vibratorInfo, int primitiveId) { + int duration = vibratorInfo.getPrimitiveDuration(primitiveId); + // Unsupported primitives should be ignored here. + return duration > 0 ? duration : getDuration(); + } + + /** @hide */ + @Override public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) { return true; @@ -89,34 +126,30 @@ public final class PrebakedSegment extends VibrationEffectSegment { } // The vibrator does not have hardware support for the effect, but the effect has fallback // support. Check if a fallback will be available for the effect ID. - switch (mEffectId) { - case VibrationEffect.EFFECT_CLICK: - case VibrationEffect.EFFECT_DOUBLE_CLICK: - case VibrationEffect.EFFECT_HEAVY_CLICK: - case VibrationEffect.EFFECT_TICK: - // Any of these effects are always supported via some form of fallback. - return true; - default: - return false; - } + return switch (mEffectId) { + // Any of these effects are always supported via some form of fallback. + case EFFECT_CLICK, + EFFECT_DOUBLE_CLICK, + EFFECT_HEAVY_CLICK, + EFFECT_TICK -> true; + default -> false; + }; } /** @hide */ @Override public boolean isHapticFeedbackCandidate() { - switch (mEffectId) { - case VibrationEffect.EFFECT_CLICK: - case VibrationEffect.EFFECT_DOUBLE_CLICK: - case VibrationEffect.EFFECT_HEAVY_CLICK: - case VibrationEffect.EFFECT_POP: - case VibrationEffect.EFFECT_TEXTURE_TICK: - case VibrationEffect.EFFECT_THUD: - case VibrationEffect.EFFECT_TICK: - return true; - default: - // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback - return false; - } + return switch (mEffectId) { + case EFFECT_CLICK, + EFFECT_DOUBLE_CLICK, + EFFECT_HEAVY_CLICK, + EFFECT_POP, + EFFECT_TEXTURE_TICK, + EFFECT_THUD, + EFFECT_TICK -> true; + // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback + default -> false; + }; } /** @hide */ @@ -153,27 +186,25 @@ public final class PrebakedSegment extends VibrationEffectSegment { } private static boolean isValidEffectStrength(int strength) { - switch (strength) { - case VibrationEffect.EFFECT_STRENGTH_LIGHT: - case VibrationEffect.EFFECT_STRENGTH_MEDIUM: - case VibrationEffect.EFFECT_STRENGTH_STRONG: - return true; - default: - return false; - } + return switch (strength) { + case VibrationEffect.EFFECT_STRENGTH_LIGHT, + VibrationEffect.EFFECT_STRENGTH_MEDIUM, + VibrationEffect.EFFECT_STRENGTH_STRONG -> true; + default -> false; + }; } /** @hide */ @Override public void validate() { switch (mEffectId) { - case VibrationEffect.EFFECT_CLICK: - case VibrationEffect.EFFECT_DOUBLE_CLICK: - case VibrationEffect.EFFECT_HEAVY_CLICK: - case VibrationEffect.EFFECT_POP: - case VibrationEffect.EFFECT_TEXTURE_TICK: - case VibrationEffect.EFFECT_THUD: - case VibrationEffect.EFFECT_TICK: + case EFFECT_CLICK: + case EFFECT_DOUBLE_CLICK: + case EFFECT_HEAVY_CLICK: + case EFFECT_POP: + case EFFECT_TEXTURE_TICK: + case EFFECT_THUD: + case EFFECT_TICK: break; default: int[] ringtones = VibrationEffect.RINGTONES; diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java index 3c84bcda639b..91653edd1ba5 100644 --- a/core/java/android/os/vibrator/PrimitiveSegment.java +++ b/core/java/android/os/vibrator/PrimitiveSegment.java @@ -77,6 +77,16 @@ public final class PrimitiveSegment extends VibrationEffectSegment { /** @hide */ @Override + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + if (vibratorInfo == null) { + return getDuration(); + } + int duration = vibratorInfo.getPrimitiveDuration(mPrimitiveId); + return duration > 0 ? duration + mDelay : getDuration(); + } + + /** @hide */ + @Override public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { return vibratorInfo.isPrimitiveSupported(mPrimitiveId); } diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java index e6e5a27bd731..88be96a4aef3 100644 --- a/core/java/android/os/vibrator/VibrationConfig.java +++ b/core/java/android/os/vibrator/VibrationConfig.java @@ -86,6 +86,7 @@ public class VibrationConfig { private final int mDefaultKeyboardVibrationIntensity; private final boolean mKeyboardVibrationSettingsSupported; + private final int mVibrationPipelineMaxDurationMs; /** @hide */ public VibrationConfig(@Nullable Resources resources) { @@ -106,6 +107,8 @@ public class VibrationConfig { com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger); mKeyboardVibrationSettingsSupported = loadBoolean(resources, com.android.internal.R.bool.config_keyboardVibrationSettingsSupported); + mVibrationPipelineMaxDurationMs = loadInteger(resources, + com.android.internal.R.integer.config_vibrationPipelineMaxDuration, 0); mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources, com.android.internal.R.integer.config_defaultAlarmVibrationIntensity); @@ -221,6 +224,23 @@ public class VibrationConfig { } /** + * The max duration, in milliseconds, allowed for pipelining vibration requests. + * + * <p>If the ongoing vibration duration is shorter than this threshold then it should be allowed + * to finish before the next vibration can start. If the ongoing vibration is longer than this + * then it should be cancelled when it's superseded for the new one. + * + * @return the max duration allowed for vibration effect to finish before the next request, or + * zero to disable effect pipelining. + */ + public int getVibrationPipelineMaxDurationMs() { + if (mVibrationPipelineMaxDurationMs < 0) { + return 0; + } + return mVibrationPipelineMaxDurationMs; + } + + /** * Whether or not vibrations are ignored if the device is on a wireless charger. * * <p>This may be the case if vibration during wireless charging causes unwanted results, like diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java index e1fb4e361008..dadc849dae0a 100644 --- a/core/java/android/os/vibrator/VibrationEffectSegment.java +++ b/core/java/android/os/vibrator/VibrationEffectSegment.java @@ -17,6 +17,7 @@ package android.os.vibrator; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -58,10 +59,23 @@ public abstract class VibrationEffectSegment implements Parcelable { */ public abstract long getDuration(); - /** - * Checks if a given {@link Vibrator} can play this segment as intended. See - * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about - * what counts as supported by a vibrator, and what counts as not. + /** + * Gets the estimated duration of the segment for given vibrator, in milliseconds. + * + * <p>For segments with hardware-dependent constants (e.g. primitives), this returns the + * estimated duration based on the given {@link VibratorInfo}. For all other effects this will + * return the same as {@link #getDuration()}. + * + * @hide + */ + public long getDuration(@Nullable VibratorInfo vibratorInfo) { + return getDuration(); + } + + /** + * Checks if a given {@link android.os.Vibrator} can play this segment as intended. See + * {@link android.os.Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more + * information about what counts as supported by a vibrator, and what counts as not. * * @hide */ diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index e3b1221b3004..7ceb948945fd 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -123,4 +123,25 @@ flag { metadata { purpose: PURPOSE_FEATURE } -}
\ No newline at end of file +} + +flag { + namespace: "haptics" + name: "primitive_composition_absolute_delay" + is_exported: true + description: "Enables functionality to create primitive compositions with absolute delays" + bug: "373357740" + metadata { + purpose: PURPOSE_FEATURE + } +} + +flag { + namespace: "haptics" + name: "vibration_pipeline_enabled" + description: "Enables functionality to pipeline vibration effects to avoid cancelling short vibrations" + bug: "344494220" + metadata { + purpose: PURPOSE_FEATURE + } +} diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 0dec13ff0c02..e254bf3e016f 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -73,8 +73,11 @@ import java.util.Locale; public abstract class Layout { // These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h - private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 4f; - private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0.2f; + private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 0f; + private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0f; + private static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP = 5f; + // since we're not using soft light yet, this needs to be much lower than the spec'd 0.8 + private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.5f; /** @hide */ @IntDef(prefix = { "BREAK_STRATEGY_" }, value = { @@ -1025,11 +1028,18 @@ public abstract class Layout { var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX, mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR); + var cornerRadius = mPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP; + + // We set the alpha on the color itself instead of Paint.setAlpha(), because that function + // actually mutates the color in... *ehem* very strange ways. Also the color might get reset + // for various reasons, which also resets the alpha. + var white = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 1f, 1f, 1f); + var black = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 0f, 0f, 0f); var originalTextColor = mPaint.getColor(); var bgPaint = mWorkPlainPaint; bgPaint.reset(); - bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? Color.WHITE : Color.BLACK); + bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? white : black); bgPaint.setStyle(Paint.Style.FILL); int start = getLineStart(firstLine); @@ -1082,7 +1092,12 @@ public abstract class Layout { private void drawRect() { if (!mLineBackground.isEmpty()) { mLineBackground.inset(-padding, -padding); - canvas.drawRect(mLineBackground, bgPaint); + canvas.drawRoundRect( + mLineBackground, + cornerRadius, + cornerRadius, + bgPaint + ); } } @@ -1104,7 +1119,7 @@ public abstract class Layout { if (hasColorChanged) { mLastColor = textColor; - return isHighContrastTextDark(textColor) ? Color.WHITE : Color.BLACK; + return isHighContrastTextDark(textColor) ? white : black; } return bgPaint.getColor(); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index d08873c56e6a..59c66532fe0b 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -22,6 +22,7 @@ import static android.view.InsetsControllerProto.CONTROL; import static android.view.InsetsControllerProto.STATE; import static android.view.InsetsSource.ID_IME; import static android.view.InsetsSource.ID_IME_CAPTION_BAR; +import static android.view.ViewProtoLogGroups.IME_INSETS_CONTROLLER; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.LAST; import static android.view.WindowInsets.Type.all; @@ -69,6 +70,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.ImeTracing; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.TriFunction; import java.io.PrintWriter; @@ -1920,6 +1922,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final @InsetsType int requestedVisibleTypes = (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask); if (mRequestedVisibleTypes != requestedVisibleTypes) { + ProtoLog.d(IME_INSETS_CONTROLLER, "Setting requestedVisibleTypes to %d (was %d)", + requestedVisibleTypes, mRequestedVisibleTypes); mRequestedVisibleTypes = requestedVisibleTypes; } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 83b4971c8621..eb59e21cd7e2 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1734,9 +1734,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mRTLastReportedPosition.top /*positionTop*/, postScaleX, postScaleY); - mRTLastSetCrop.set((int) (clipLeft / postScaleX), (int) (clipTop / postScaleY), - (int) Math.ceil(clipRight / postScaleX), - (int) Math.ceil(clipBottom / postScaleY)); + mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom); if (DEBUG_POSITION) { Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] " + "from scale %f, %f", mRTLastSetCrop.left, diff --git a/core/java/android/view/ViewProtoLogGroups.java b/core/java/android/view/ViewProtoLogGroups.java new file mode 100644 index 000000000000..099f76189a50 --- /dev/null +++ b/core/java/android/view/ViewProtoLogGroups.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.view.inputmethod.Flags; + +import com.android.internal.protolog.ProtoLogGroup; + +import java.util.UUID; + +/** + * Defines logging groups for ProtoLog. + * + * This file is used by the ProtoLogTool to generate optimized logging code. All of its dependencies + * must be included in services.core.wm.protologgroups build target. + * + * @hide + */ +final class ViewProtoLogGroups { + final static ProtoLogGroup IME_INSETS_CONTROLLER = new ProtoLogGroup( + "IME_INSETS_CONTROLLER", "InsetsController", Flags.refactorInsetsController()); + + final static ProtoLogGroup[] ALL_GROUPS = { + IME_INSETS_CONTROLLER + }; +} + diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3be9a821a463..182ed1ebad59 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -279,6 +279,7 @@ import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneFallbackEventHandler; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FastPrintWriter; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; @@ -1282,6 +1283,8 @@ public final class ViewRootImpl implements ViewParent, mIsStylusPointerIconEnabled = InputSettings.isStylusPointerIconEnabled(mContext); + initializeProtoLogInProcess(); + String processorOverrideName = context.getResources().getString( R.string.config_inputEventCompatProcessorOverrideClassName); if (processorOverrideName.isEmpty()) { @@ -13403,4 +13406,13 @@ public final class ViewRootImpl implements ViewParent, mCurrentColorMode = colorMode; } + + private static boolean sProtoLogInitialized = false; + + private void initializeProtoLogInProcess() { + if (!sProtoLogInitialized) { + ProtoLog.init(ViewProtoLogGroups.ALL_GROUPS); + sProtoLogInitialized = true; + } + } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 1e5c6d8177e1..47fc43735c4d 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2352,6 +2352,13 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note: * this does not return result of the request. For result use {@param resultReceiver} instead. + * + * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the + * Input Method is actually shown or hidden. If result is needed, use + * {@link android.view.WindowInsetsController#show} instead and set a + * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for + * the visibility of IME. If result is not needed, use {@link #showSoftInput(View, int)} + * instead. */ public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) { return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT); @@ -2399,6 +2406,14 @@ public final class InputMethodManager { & WindowInsets.Type.ime()) == 0) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION); + if (resultReceiver != null) { + final boolean imeReqVisible = + (viewRootImpl.getInsetsController().getRequestedVisibleTypes() + & WindowInsets.Type.ime()) != 0; + resultReceiver.send( + imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN + : InputMethodManager.RESULT_SHOWN, null); + } // TODO(b/322992891) handle case of SHOW_IMPLICIT viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(), false /* fromIme */, statsToken); @@ -2531,6 +2546,13 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note: * this does not return result of the request. For result use {@param resultReceiver} instead. + * + * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the + * Input Method is actually shown or hidden. If result is needed, use + * {@link android.view.WindowInsetsController#hide} instead and set a + * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for + * the visibility of IME. If result is not needed, use + * {@link #hideSoftInputFromView(View, int)} instead. */ public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, ResultReceiver resultReceiver) { @@ -2569,6 +2591,14 @@ public final class InputMethodManager { // TODO(b/322992891) handle case of HIDE_IMPLICIT_ONLY final var viewRootImpl = servedView.getViewRootImpl(); if (viewRootImpl != null) { + if (resultReceiver != null) { + final boolean imeReqVisible = + (viewRootImpl.getInsetsController().getRequestedVisibleTypes() + & WindowInsets.Type.ime()) != 0; + resultReceiver.send( + !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN + : InputMethodManager.RESULT_HIDDEN, null); + } viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime()); } return true; diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig index bae8affcec47..aa4927ee9b9c 100644 --- a/core/java/android/view/inputmethod/flags.aconfig +++ b/core/java/android/view/inputmethod/flags.aconfig @@ -73,6 +73,17 @@ flag { } flag { + name: "consistent_get_current_input_method_info" + namespace: "input_method" + description: "Use BindingController as the source of truth in getCurrentInputMethodInfo" + bug: "355034523" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "ime_switcher_revamp" is_exported: true namespace: "input_method" diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index 8e35843e2193..05dc910b89de 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -64,7 +64,9 @@ public enum DesktopModeFlags { ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true), ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS( Flags::enableDesktopWindowingTaskbarRunningApps, true), + // TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false), + ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false), ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false), ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS( Flags::enableWindowingTransitionHandlersObservers, false); diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 18c8eb4ec46b..de7ad346a7cd 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -1195,7 +1195,12 @@ public class ResolverListAdapter extends BaseAdapter { @Nullable protected Drawable loadIconFromResource(Resources res, int resId) { - return res.getDrawableForDensity(resId, mIconDpi); + try { + return res.getDrawableForDensity(resId, mIconDpi); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Resource not found", e); + return null; + } } } diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java index dd6c879f1135..3c201fc42df3 100644 --- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java +++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java @@ -179,6 +179,6 @@ public class ProtoLogViewerConfigReader { } } - throw new RuntimeException("Group " + group + "not found in viewer config"); + throw new RuntimeException("Group " + group + " not found in viewer config"); } } diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java index 3e597d73f217..d3b1f972a955 100644 --- a/core/java/com/android/internal/widget/NotificationProgressBar.java +++ b/core/java/com/android/internal/widget/NotificationProgressBar.java @@ -20,16 +20,20 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification.ProgressStyle; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.graphics.drawable.LayerDrawable; import android.os.Bundle; import android.util.AttributeSet; +import android.util.Log; import android.view.RemotableViewMethod; import android.widget.ProgressBar; import android.widget.RemoteViews; import androidx.annotation.ColorInt; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.internal.widget.NotificationProgressDrawable.Part; @@ -49,7 +53,13 @@ import java.util.TreeSet; */ @RemoteViews.RemoteView public class NotificationProgressBar extends ProgressBar { + private static final String TAG = "NotificationProgressBar"; + private NotificationProgressModel mProgressModel; + + @Nullable + private List<Part> mProgressDrawableParts = null; + @Nullable private Drawable mProgressTrackerDrawable = null; @@ -58,7 +68,7 @@ public class NotificationProgressBar extends ProgressBar { } public NotificationProgressBar(Context context, AttributeSet attrs) { - this(context, attrs, com.android.internal.R.attr.progressBarStyle); + this(context, attrs, R.attr.progressBarStyle); } public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { @@ -82,10 +92,42 @@ public class NotificationProgressBar extends ProgressBar { "Bundle shouldn't be null"); mProgressModel = NotificationProgressModel.fromBundle(bundle); + + if (mProgressModel.isIndeterminate()) { + final int indeterminateColor = mProgressModel.getIndeterminateColor(); + setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor)); + } else { + mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(), + mProgressModel.getPoints(), + mProgressModel.getProgress(), mProgressModel.isStyledByProgress()); + + try { + final NotificationProgressDrawable drawable = getNotificationProgressDrawable(); + drawable.setParts(mProgressDrawableParts); + } catch (IllegalStateException ex) { + Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex); + } + } } - private void setProgressModel(@NonNull NotificationProgressModel model) { - mProgressModel = model; + @NonNull + private NotificationProgressDrawable getNotificationProgressDrawable() { + final Drawable d = getProgressDrawable(); + if (d == null) { + throw new IllegalStateException("getProgressDrawable() returns null"); + } + if (!(d instanceof LayerDrawable)) { + throw new IllegalStateException("getProgressDrawable() doesn't return a LayerDrawable"); + } + + final Drawable layer = ((LayerDrawable) d).findDrawableByLayerId(R.id.background); + if (!(layer instanceof NotificationProgressDrawable)) { + throw new IllegalStateException( + "Couldn't get NotificationProgressDrawable, retrieved drawable is: " + ( + layer != null ? layer.toString() : null)); + } + + return (NotificationProgressDrawable) layer; } /** @@ -97,7 +139,6 @@ public class NotificationProgressBar extends ProgressBar { public void setProgressTrackerIcon(@Nullable Icon icon) { } - /** * Async version of {@link #setProgressTrackerIcon} */ diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java index 89ef8759a169..2be7273e5d10 100644 --- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java +++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java @@ -45,6 +45,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -156,11 +157,18 @@ public final class NotificationProgressDrawable extends Drawable { } /** - * + * Set the segments and points that constitute the drawable. */ - public void setParts(@NonNull Part... parts) { + public void setParts(List<Part> parts) { mParts.clear(); - mParts.addAll(Arrays.asList(parts)); + mParts.addAll(parts); + } + + /** + * Set the segments and points that constitute the drawable. + */ + public void setParts(@NonNull Part... parts) { + setParts(Arrays.asList(parts)); } @Override @@ -379,7 +387,7 @@ public final class NotificationProgressDrawable extends Drawable { if (state.mThemeAttrsPoints != null) { final TypedArray a = t.resolveAttributes( state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints); - updateSegmentsFromTypedArray(a); + updatePointsFromTypedArray(a); a.recycle(); } } @@ -651,9 +659,11 @@ public final class NotificationProgressDrawable extends Drawable { State(@NonNull State orig, @Nullable Resources res) { mChangingConfigurations = orig.mChangingConfigurations; + mSegSegGap = orig.mSegSegGap; + mSegPointGap = orig.mSegPointGap; + mStrokeWidth = orig.mStrokeWidth; mStrokeColor = orig.mStrokeColor; mFadedStrokeColor = orig.mFadedStrokeColor; - mStrokeWidth = orig.mStrokeWidth; mStrokeDashWidth = orig.mStrokeDashWidth; mStrokeDashGap = orig.mStrokeDashGap; mPointRadius = orig.mPointRadius; @@ -791,6 +801,7 @@ public final class NotificationProgressDrawable extends Drawable { final State state = mState; mStrokePaint.setStrokeWidth(state.mStrokeWidth); + mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth); if (state.mStrokeDashWidth != 0.0f) { final DashPathEffect e = new DashPathEffect( diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java index 98e6e8505534..adcc0f64b598 100644 --- a/core/java/com/android/internal/widget/NotificationRowIconView.java +++ b/core/java/com/android/internal/widget/NotificationRowIconView.java @@ -39,17 +39,24 @@ import com.android.internal.R; /** * An image view that holds the icon displayed at the start of a notification row. + * This can generally either display the "small icon" of a notification set via + * {@link this#setImageIcon(Icon)}, or an app icon controlled and fetched by the provider set + * through {@link this#setIconProvider(NotificationIconProvider)}. */ @RemoteViews.RemoteView public class NotificationRowIconView extends CachingIconView { + private NotificationIconProvider mIconProvider; + private boolean mApplyCircularCrop = false; private boolean mShouldShowAppIcon = false; + private Drawable mAppIcon = null; - // Padding and background set on the view prior to being changed by setShouldShowAppIcon(true), - // to be restored if shouldShowAppIcon becomes false again. + // Padding, background and colors set on the view prior to being overridden when showing the app + // icon, to be restored if we're showing the small icon again. private Rect mOriginalPadding = null; private Drawable mOriginalBackground = null; - + private int mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID; + private int mOriginalIconColor = ColoredIconHelper.COLOR_INVALID; public NotificationRowIconView(Context context) { super(context); @@ -81,6 +88,71 @@ public class NotificationRowIconView extends CachingIconView { super.onFinishInflate(); } + /** + * Sets the icon provider for this view. This is used to determine whether we should show the + * app icon instead of the small icon, and to fetch the app icon if needed. + */ + public void setIconProvider(NotificationIconProvider iconProvider) { + mIconProvider = iconProvider; + } + + private Drawable loadAppIcon() { + if (mIconProvider != null && mIconProvider.shouldShowAppIcon()) { + return mIconProvider.getAppIcon(); + } + return null; + } + + @RemotableViewMethod(asyncImpl = "setImageIconAsync") + @Override + public void setImageIcon(Icon icon) { + if (Flags.notificationsRedesignAppIcons()) { + if (mAppIcon != null) { + // We already know that we should be using the app icon, and we already loaded it. + // We assume that cannot change throughout the lifetime of a notification, so + // there's nothing to do here. + return; + } + mAppIcon = loadAppIcon(); + if (mAppIcon != null) { + setImageDrawable(mAppIcon); + adjustViewForAppIcon(); + } else { + super.setImageIcon(icon); + restoreViewForSmallIcon(); + } + return; + } + super.setImageIcon(icon); + } + + @RemotableViewMethod + @Override + public Runnable setImageIconAsync(Icon icon) { + if (Flags.notificationsRedesignAppIcons()) { + if (mAppIcon != null) { + // We already know that we should be using the app icon, and we already loaded it. + // We assume that cannot change throughout the lifetime of a notification, so + // there's nothing to do here. + return () -> { + }; + } + mAppIcon = loadAppIcon(); + if (mAppIcon != null) { + return () -> { + setImageDrawable(mAppIcon); + adjustViewForAppIcon(); + }; + } else { + return () -> { + super.setImageIcon(icon); + restoreViewForSmallIcon(); + }; + } + } + return super.setImageIconAsync(icon); + } + /** Whether the icon represents the app icon (instead of the small icon). */ @RemotableViewMethod public void setShouldShowAppIcon(boolean shouldShowAppIcon) { @@ -91,35 +163,122 @@ public class NotificationRowIconView extends CachingIconView { mShouldShowAppIcon = shouldShowAppIcon; if (mShouldShowAppIcon) { - if (mOriginalPadding == null && mOriginalBackground == null) { - mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(), - getPaddingRight(), getPaddingBottom()); - mOriginalBackground = getBackground(); - } - - setPadding(0, 0, 0, 0); - - // Make the background white in case the icon itself doesn't have one. - ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE, - PorterDuff.Mode.SRC_ATOP); - - if (mOriginalBackground == null) { - setBackground(getContext().getDrawable(R.drawable.notification_icon_circle)); - } - getBackground().mutate().setColorFilter(colorFilter); + adjustViewForAppIcon(); } else { // Restore original padding and background if needed - if (mOriginalPadding != null) { - setPadding(mOriginalPadding.left, mOriginalPadding.top, mOriginalPadding.right, - mOriginalPadding.bottom); - mOriginalPadding = null; - } - setBackground(mOriginalBackground); - mOriginalBackground = null; + restoreViewForSmallIcon(); } } } + /** + * Override padding and background from the view to display the app icon. + */ + private void adjustViewForAppIcon() { + removePadding(); + + if (Flags.notificationsUseAppIconInRow()) { + addWhiteBackground(); + } else { + // No need to set the background for notification redesign, since the icon + // factory already does that for us. + removeBackground(); + } + } + + /** + * Restore padding and background overridden by {@link this#adjustViewForAppIcon}. + * Does nothing if they were not overridden. + */ + private void restoreViewForSmallIcon() { + restorePadding(); + restoreBackground(); + restoreColors(); + } + + private void removePadding() { + if (mOriginalPadding == null) { + mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(), + getPaddingRight(), getPaddingBottom()); + } + setPadding(0, 0, 0, 0); + } + + private void restorePadding() { + if (mOriginalPadding != null) { + setPadding(mOriginalPadding.left, mOriginalPadding.top, + mOriginalPadding.right, + mOriginalPadding.bottom); + mOriginalPadding = null; + } + } + + private void removeBackground() { + if (mOriginalBackground == null) { + mOriginalBackground = getBackground(); + } + + setBackground(null); + } + + private void addWhiteBackground() { + if (mOriginalBackground == null) { + mOriginalBackground = getBackground(); + } + + // Make the background white in case the icon itself doesn't have one. + ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE, + PorterDuff.Mode.SRC_ATOP); + + if (mOriginalBackground == null) { + setBackground(getContext().getDrawable(R.drawable.notification_icon_circle)); + } + getBackground().mutate().setColorFilter(colorFilter); + } + + private void restoreBackground() { + // NOTE: This will not work if the original background was null, but that's better than + // accidentally clearing the background. We expect that there's generally going to be one + // anyway unless we manually clear it. + if (mOriginalBackground != null) { + setBackground(mOriginalBackground); + mOriginalBackground = null; + } + } + + private void restoreColors() { + if (mOriginalBackgroundColor != ColoredIconHelper.COLOR_INVALID) { + super.setBackgroundColor(mOriginalBackgroundColor); + mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID; + } + if (mOriginalIconColor != ColoredIconHelper.COLOR_INVALID) { + super.setOriginalIconColor(mOriginalIconColor); + mOriginalIconColor = ColoredIconHelper.COLOR_INVALID; + } + } + + @RemotableViewMethod + @Override + public void setBackgroundColor(int color) { + // Ignore color overrides if we're showing the app icon. + if (mAppIcon == null) { + super.setBackgroundColor(color); + } else { + mOriginalBackgroundColor = color; + } + } + + @RemotableViewMethod + @Override + public void setOriginalIconColor(int color) { + // Ignore color overrides if we're showing the app icon. + if (mAppIcon == null) { + super.setOriginalIconColor(color); + } else { + mOriginalIconColor = color; + } + } + @Nullable @Override Drawable loadSizeRestrictedIcon(@Nullable Icon icon) { @@ -197,4 +356,17 @@ public class NotificationRowIconView extends CachingIconView { return bitmap; } + + /** + * A provider that allows this view to verify whether it should use the app icon instead of the + * icon provided to it via setImageIcon, as well as actually fetching the app icon. It should + * primarily be called on the background thread. + */ + public interface NotificationIconProvider { + /** Whether this notification should use the app icon instead of the small icon. */ + boolean shouldShowAppIcon(); + + /** Get the app icon for this notification. */ + Drawable getAppIcon(); + } } diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml new file mode 100644 index 000000000000..8b2afa86986c --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/disabledAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorPrimary" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml new file mode 100644 index 000000000000..cefc9121b7a4 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/primaryContentAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorOnPrimary" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml index 530d752732c1..eaf9e7d50bbd 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml @@ -1,6 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ Copyright (C) 2024 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -14,12 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view_legacy" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. --> - -</com.android.systemui.biometrics.UdfpsKeyguardViewLegacy> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/disabledAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorSurfaceContainer" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml new file mode 100644 index 000000000000..94e50fbe2533 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/primaryContentAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorOnSurface" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml new file mode 100644 index 000000000000..0029de14e34a --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/btn_material_filled_background_color"/> + <corners android:radius="?android:attr/buttonCornerRadius"/> + <size + android:width="@dimen/btn_material_width" + android:height="@dimen/btn_material_height" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml new file mode 100644 index 000000000000..105f077cd841 --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/btn_material_filled_tonal_background_color"/> + <corners android:radius="?android:attr/buttonCornerRadius"/> + <size + android:width="@dimen/btn_material_width" + android:height="@dimen/btn_material_height" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_zen_mode_icon_piano.xml b/core/res/res/drawable/ic_zen_mode_icon_piano.xml new file mode 100644 index 000000000000..012b9398d687 --- /dev/null +++ b/core/res/res/drawable/ic_zen_mode_icon_piano.xml @@ -0,0 +1,25 @@ +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?android:attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L330,760L330,580L320,580Q303,580 291.5,568.5Q280,557 280,540L280,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM630,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L680,200L680,540Q680,557 668.5,568.5Q657,580 640,580L630,580L630,760ZM390,760L570,760L570,580L560,580Q543,580 531.5,568.5Q520,557 520,540L520,200L440,200L440,540Q440,557 428.5,568.5Q417,580 400,580L390,580L390,760Z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml new file mode 100644 index 000000000000..3a6b60077045 --- /dev/null +++ b/core/res/res/drawable/notification_progress.xml @@ -0,0 +1,37 @@ +<?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. +--> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@id/background" + android:gravity="center_vertical|fill_horizontal"> + <com.android.internal.widget.NotificationProgressDrawable + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:segSegGap="@dimen/notification_progress_segSeg_gap" + android:segPointGap="@dimen/notification_progress_segPoint_gap"> + <segments + android:color="?attr/colorProgressBackgroundNormal" + android:dashGap="@dimen/notification_progress_segments_dash_gap" + android:dashWidth="@dimen/notification_progress_segments_dash_width" + android:width="@dimen/notification_progress_segments_height" /> + <points + android:color="?attr/colorProgressBackgroundNormal" + android:radius="@dimen/notification_progress_points_radius" + android:cornerRadius="@dimen/notification_progress_points_corner_radius" + android:inset="@dimen/notification_progress_points_inset" /> + </com.android.internal.widget.NotificationProgressDrawable> + </item> +</layer-list> diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml index fdcefccdbf16..75827a279ff7 100644 --- a/core/res/res/layout/notification_template_material_progress.xml +++ b/core/res/res/layout/notification_template_material_progress.xml @@ -75,12 +75,11 @@ /> - <com.android.internal.widget.NotificationProgressBar - android:id="@+id/progress" + <include android:layout_width="0dp" - android:layout_height="@dimen/notification_progress_bar_height" - style="@style/Widget.Material.Light.ProgressBar.Horizontal" android:layout_weight="1" + android:layout_height="@dimen/notification_progress_tracker_height" + layout="@layout/notification_template_notification_progress_bar" /> <com.android.internal.widget.CachingIconView diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/layout/notification_template_notification_progress_bar.xml index f1c55ef16cdc..35748962cfb2 100644 --- a/packages/SystemUI/res/layout/udfps_bp_view.xml +++ b/core/res/res/layout/notification_template_notification_progress_bar.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ Copyright (C) 2014 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -12,11 +12,12 @@ ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and - ~ limitations under the License. + ~ limitations under the License --> -<com.android.systemui.biometrics.UdfpsBpView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view" + +<com.android.internal.widget.NotificationProgressBar xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/progress" android:layout_width="match_parent" - android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsBpView> + android:layout_height="@dimen/notification_progress_tracker_height" + style="@style/Widget.Material.Notification.NotificationProgressBar" + /> diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml new file mode 100644 index 000000000000..4bc2a66fa206 --- /dev/null +++ b/core/res/res/values-watch-v36/colors.xml @@ -0,0 +1,18 @@ +<!-- + ~ 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. + --> +<!-- TODO(b/372524566): update color token's value to match material3 design. --> +<resources> +</resources>
\ No newline at end of file diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml new file mode 100644 index 000000000000..c8f347afb318 --- /dev/null +++ b/core/res/res/values-watch-v36/config.xml @@ -0,0 +1,20 @@ +<!-- + ~ 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. + --> + +<resources> + <!-- Overrides system value --> + <dimen name="config_buttonCornerRadius">26dp</dimen> +</resources> diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml new file mode 100644 index 000000000000..ad3c1a3ef3a1 --- /dev/null +++ b/core/res/res/values-watch-v36/dimens_material.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <!-- values for material3 button --> + <dimen name="btn_material_width">172dp</dimen> + <dimen name="btn_material_height">52dp</dimen> + <dimen name="btn_horizontal_edge_padding">14dp</dimen> + <dimen name="btn_drawable_padding">6dp</dimen> + <dimen name="btn_lineHeight">18sp</dimen> + <dimen name="btn_textSize">15sp</dimen> + + <!-- Opacity factor for disabled material3 widget --> + <dimen name="disabled_alpha_device_default">0.12</dimen> + <dimen name="primary_content_alpha_device_default">0.38</dimen> +</resources> diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml new file mode 100644 index 000000000000..32a22bb755cb --- /dev/null +++ b/core/res/res/values-watch-v36/styles_material.xml @@ -0,0 +1,58 @@ +<?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. + --> + +<resources> + <!-- Button Styles --> + <!-- Material Button - Filled --> + <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button"> + <item name="android:background">@drawable/btn_background_material_filled</item> + <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item> + </style> + + <!-- Material Button - Filled Tonal(Override system default button styles) --> + <style name="Widget.DeviceDefault.Button"> + <item name="background">@drawable/btn_background_material_filled_tonal</item> + <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item> + <item name="minHeight">@dimen/btn_material_height</item> + <item name="maxWidth">@dimen/btn_material_width</item> + <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item> + <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item> + <item name="android:drawablePadding">@dimen/btn_drawable_padding</item> + <item name="android:maxLines">2</item> + <item name="android:ellipsize">end</item> + <item name="android:breakStrategy">simple</item> + <item name="stateListAnimator">@anim/button_state_list_anim_material</item> + <item name="focusable">true</item> + <item name="clickable">true</item> + <item name="gravity">center_vertical</item> + </style> + + <!-- Text Styles --> + <!-- TextAppearance for Material Button - Filled --> + <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material"> + <item name="textColor">@color/btn_material_filled_text_color</item> + </style> + + <!-- TextAppearance for Material Button - Filled Tonal --> + <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault"> + <item name="android:fontFamily">font-family-flex-device-default</item> + <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item> + <item name="textSize">@dimen/btn_textSize</item> + <item name="textColor">@color/btn_material_filled_tonal_text_color</item> + <item name="lineHeight">@dimen/btn_lineHeight</item> + </style> +</resources>
\ No newline at end of file diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8a2d767e9ad1..91b482051065 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -287,6 +287,11 @@ vibration params. --> <integer name="config_requestVibrationParamsTimeout">50</integer> + <!-- The max duration (in milliseconds) that the vibrator service will allow effects to be + pipelined (i.e. service will wait for ongoing vibration to finish instead of cancelling it + to start the new one). Value should be positive. Zero will disable effect pipelining. --> + <integer name="config_vibrationPipelineMaxDuration">25</integer> + <!-- Array containing the usages that should request vibration params before they are played. These usages don't have strong latency requirements, e.g. ringtone and notification, and can be slightly delayed. --> @@ -4447,17 +4452,25 @@ <string-array translatable="false" name="config_defaultPinnerServiceFiles"> </string-array> - <!-- True if camera app should be pinned via Pinner Service --> + <!-- Note: This config is deprecated, use config_pinnerCameraPinBytes instead. + True if camera app should be pinned via Pinner Service --> <bool name="config_pinnerCameraApp">false</bool> - <!-- Bytes that the PinnerService will pin for Home app --> - <integer name="config_pinnerHomePinBytes">0</integer> + <!-- Default: 60 MB. Bytes that the PinnerService will pin for Home app --> + <integer name="config_pinnerHomePinBytes">62914560</integer> + + <!-- Default: 80 MB. Bytes that the PinnerService will pin for Camera app --> + <integer name="config_pinnerCameraPinBytes">83886080</integer> - <!-- True if assistant app should be pinned via Pinner Service --> + <!-- Note: This config is deprecated, use config_pinnerAssistantPinBytes instead. + True if assistant app should be pinned via Pinner Service --> <bool name="config_pinnerAssistantApp">false</bool> - <!-- Bytes that the PinnerService will pin for WebView --> - <integer name="config_pinnerWebviewPinBytes">0</integer> + <!-- Default: 60 MB. Bytes that the PinnerService will pin for Assistant --> + <integer name="config_pinnerAssistantPinBytes">62914560</integer> + + <!-- Default: 20 MB. Bytes that the PinnerService will pin for WebView --> + <integer name="config_pinnerWebviewPinBytes">20971520</integer> <!-- Maximum memory that PinnerService will pin for apps expressed as a percentage of total device memory [0,100]. diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index b92aa2f355ed..7184d9a8c890 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -817,6 +817,22 @@ <dimen name="notification_progress_tracker_width">40dp</dimen> <!-- The size of the progress tracker height --> <dimen name="notification_progress_tracker_height">20dp</dimen> + <!-- The gap between segments in the notification progress bar --> + <dimen name="notification_progress_segSeg_gap">2dp</dimen> + <!-- The gap between a segment and a point in the notification progress bar --> + <dimen name="notification_progress_segPoint_gap">4dp</dimen> + <!-- The dash gap of the notification progress bar segments --> + <dimen name="notification_progress_segments_dash_gap">9dp</dimen> + <!-- The dash width of the notification progress bar segments --> + <dimen name="notification_progress_segments_dash_width">3dp</dimen> + <!-- The height of the notification progress bar segments --> + <dimen name="notification_progress_segments_height">6dp</dimen> + <!-- The radius of the notification progress bar points --> + <dimen name="notification_progress_points_radius">10dp</dimen> + <!-- The corner radius of the notification progress bar points drawn as rects --> + <dimen name="notification_progress_points_corner_radius">4dp</dimen> + <!-- The inset of the notification progress bar points drawn as rects --> + <dimen name="notification_progress_points_inset">0dp</dimen> <!-- The maximum size of the small notification icon on low memory devices. --> <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index eec6ae3fb521..cb8e4aa9b2a9 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -502,6 +502,10 @@ please see styles_device_defaults.xml. <style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" /> + <style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal"> + <item name="progressDrawable">@drawable/notification_progress</item> + </style> + <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView"> <item name="lineHeight">20sp</item> <item name="textAppearance">@style/TextAppearance.Material.Notification</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4f63fac96a8d..0348b4685a66 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2141,6 +2141,7 @@ <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" /> <java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" /> <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" /> + <java-symbol type="integer" name="config_vibrationPipelineMaxDuration" /> <java-symbol type="integer" name="config_radioScanningTimeout" /> <java-symbol type="integer" name="config_requestVibrationParamsTimeout" /> <java-symbol type="array" name="config_requestVibrationParamsForUsages" /> @@ -3471,6 +3472,8 @@ <java-symbol type="array" name="config_defaultPinnerServiceFiles" /> <java-symbol type="bool" name="config_pinnerCameraApp" /> <java-symbol type="integer" name="config_pinnerHomePinBytes" /> + <java-symbol type="integer" name="config_pinnerCameraPinBytes" /> + <java-symbol type="integer" name="config_pinnerAssistantPinBytes" /> <java-symbol type="bool" name="config_pinnerAssistantApp" /> <java-symbol type="integer" name="config_pinnerWebviewPinBytes" /> <java-symbol type="integer" name="config_pinnerMaxPinnedMemoryPercentage" /> @@ -3864,6 +3867,14 @@ <java-symbol type="dimen" name="notification_progress_icon_size" /> <java-symbol type="dimen" name="notification_progress_tracker_width" /> <java-symbol type="dimen" name="notification_progress_tracker_height" /> + <java-symbol type="dimen" name="notification_progress_segSeg_gap" /> + <java-symbol type="dimen" name="notification_progress_segPoint_gap" /> + <java-symbol type="dimen" name="notification_progress_segments_dash_gap" /> + <java-symbol type="dimen" name="notification_progress_segments_dash_width" /> + <java-symbol type="dimen" name="notification_progress_segments_height" /> + <java-symbol type="dimen" name="notification_progress_points_radius" /> + <java-symbol type="dimen" name="notification_progress_points_corner_radius" /> + <java-symbol type="dimen" name="notification_progress_points_inset" /> <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/> <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/> diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java index 25f9cb7c1088..6311298ec8cf 100644 --- a/core/tests/coretests/src/android/text/LayoutTest.java +++ b/core/tests/coretests/src/android/text/LayoutTest.java @@ -42,6 +42,7 @@ import android.text.Layout.Alignment; import android.text.style.ForegroundColorSpan; import android.text.style.StrikethroughSpan; +import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -697,7 +698,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW); expect.that(drawCommand.paint.getBlendMode()).isNotNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -750,7 +751,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW); expect.that(drawCommand.paint.getBlendMode()).isNotNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -802,7 +803,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN); expect.that(drawCommand.paint.getBlendMode()).isNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -855,7 +856,7 @@ public class LayoutTest { if (drawCommand.path != null) { expect.that(drawCommand.path).isEqualTo(selectionPath); - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN); expect.that(drawCommand.paint.getBlendMode()).isNull(); highlightsFound++; } else if (drawCommand.text != null) { @@ -914,7 +915,7 @@ public class LayoutTest { if (drawCommand.rect != null) { numBackgroundsFound++; - expect.that(drawCommand.paint.getColor()).isEqualTo(Color.BLACK); + expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.BLACK); expect.that(drawCommand.rect.height()).isAtLeast(LINE_HEIGHT); expect.that(drawCommand.rect.width()).isGreaterThan(0); float expectedY = (numBackgroundsFound) * (LINE_HEIGHT + LINE_DESCENT); @@ -997,20 +998,38 @@ public class LayoutTest { .filter(it -> it.rect != null) .toList(); - expect.that(backgroundCommands.get(0).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(1).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(2).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(3).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(4).paint.getColor()).isEqualTo(Color.WHITE); - expect.that(backgroundCommands.get(5).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(6).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(7).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(8).paint.getColor()).isEqualTo(Color.BLACK); - expect.that(backgroundCommands.get(9).paint.getColor()).isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(0).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(1).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(2).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(3).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(4).paint.getColor())) + .isEqualTo(Color.WHITE); + expect.that(removeAlpha(backgroundCommands.get(5).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(6).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(7).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(8).paint.getColor())) + .isEqualTo(Color.BLACK); + expect.that(removeAlpha(backgroundCommands.get(9).paint.getColor())) + .isEqualTo(Color.BLACK); expect.that(backgroundCommands.size()).isEqualTo(backgroundRectsDrawn); } + private int removeAlpha(int color) { + return Color.rgb( + Color.red(color), + Color.green(color), + Color.blue(color) + ); + } + private static final class MockCanvas extends Canvas { static class DrawCommand { @@ -1122,6 +1141,11 @@ public class LayoutTest { mDrawCommands.add(new DrawCommand(rect, p)); } + @Override + public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { + mDrawCommands.add(new DrawCommand(rect, paint)); + } + List<DrawCommand> getDrawCommands() { return mDrawCommands; } diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java index 6419c1e07f2e..79a478a7676b 100644 --- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java @@ -254,7 +254,6 @@ public class NotificationProgressBarTest { // Colors with 50% opacity int fadedGreen = 0x7F00FF00; - int fadedBlue = 0x7F0000FF; int fadedYellow = 0x7FFFFF00; List<Part> expected = new ArrayList<>(List.of( diff --git a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java index 244fcff7d27d..37ddfd21c98d 100644 --- a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java +++ b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java @@ -22,6 +22,9 @@ import static junit.framework.Assert.assertTrue; import static org.testng.Assert.assertThrows; +import android.hardware.vibrator.IVibrator; +import android.util.SparseArray; + import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -134,6 +137,54 @@ public class CombinedVibrationTest { } @Test + public void testDurationMono_withVibratorSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Use max duration from all vibrators. + assertEquals(10, CombinedVibration.createParallel( + VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos)); + assertEquals(111, CombinedVibration.createParallel( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .getDuration(infos)); + } + + @Test + public void testDurationMono_withVibratorNotSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Use max duration from all vibrators. + assertEquals(-1, CombinedVibration.createParallel( + VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos)); + assertEquals(-1, CombinedVibration.createParallel( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .getDuration(infos)); + } + + @Test public void testDurationStereo() { assertEquals(6, CombinedVibration.startParallel() .addVibrator(1, VibrationEffect.createOneShot(1, 1)) @@ -156,6 +207,75 @@ public class CombinedVibrationTest { } @Test + public void testDurationStereo_withVibratorSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Use specific vibrator durations, then max effect duration + assertEquals(111, CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .addVibrator(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + assertEquals(110, CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test + public void testDurationStereo_withVibratorNotSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // One vibrator does not support primitives + assertEquals(-1, CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .addVibrator(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + // Invalid vibrator ID + assertEquals(-1, CombinedVibration.startParallel() + .addVibrator(3, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test public void testDurationSequential() { assertEquals(26, CombinedVibration.startSequential() .addNext(1, VibrationEffect.createOneShot(10, 10), 10) @@ -178,6 +298,59 @@ public class CombinedVibrationTest { } @Test + public void testDurationSequential_withVibratorSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + // Add each duration and delay + assertEquals(321, CombinedVibration.startSequential() + .addNext(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose(), 100) + .addNext(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test + public void testDurationSequential_withVibratorNotSupportingPrimitives() { + SparseArray<VibratorInfo> infos = new SparseArray<>(2); + infos.put(1, new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + infos.put(2, new VibratorInfo.Builder(/* id= */ 2) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1) + .build()); + + assertEquals(-1, CombinedVibration.startSequential() + .addNext(1, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose(), 100) + .addNext(2, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose()) + .combine() + .getDuration(infos)); + } + + @Test public void testIsHapticFeedbackCandidateMono() { assertTrue(CombinedVibration.createParallel( VibrationEffect.createOneShot(1, 1)).isHapticFeedbackCandidate()); diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java index f5b04ee759a5..8acf2ed87e95 100644 --- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java +++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java @@ -1078,6 +1078,52 @@ public class VibrationEffectTest { } @Test + public void testDuration_withVibratorSupportingPrimitives() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5) + .build(); + + VibrationEffect composition = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100) + .compose(); + + assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration()); + assertEquals(10, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info)); + assertEquals(115, composition.getDuration(info)); + assertEquals(Long.MAX_VALUE, + VibrationEffect.startComposition() + .repeatEffectIndefinitely(composition) + .compose() + .getDuration(info)); + if (Flags.vendorVibrationEffects()) { + assertEquals(-1, + VibrationEffect.createVendorEffect(createNonEmptyBundle()).getDuration(info)); + } + } + + @Test + public void testDuration_withVibratorNotSupportingPrimitives() { + VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build(); + + VibrationEffect composition = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + + assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info)); + assertEquals(-1, composition.getDuration(info)); + assertEquals(Long.MAX_VALUE, + VibrationEffect.startComposition() + .repeatEffectIndefinitely(composition) + .compose() + .getDuration(info)); + } + + @Test public void testAreVibrationFeaturesSupported_allSegmentsSupported() { VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java index 7dd9e55f8f3e..f9ec5f0b2305 100644 --- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java @@ -24,6 +24,7 @@ import static junit.framework.Assert.assertTrue; import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertThrows; +import android.hardware.vibrator.IVibrator; import android.os.Parcel; import android.os.VibrationEffect; import android.os.VibratorInfo; @@ -114,39 +115,82 @@ public class PrebakedSegmentTest { @Test public void testDuration() { - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) - .getDuration()); - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) - .getDuration()); - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) - .getDuration()); - assertEquals(-1, new PrebakedSegment( - VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK).getDuration()); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK).getDuration()); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD).getDuration()); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) .getDuration()); } @Test + public void testDuration_withVibratorSupportingPrimitives_returnsPrimitiveDuration() { + int tickDuration = 5; + int clickDuration = 10; + int thudDuration = 15; + + VibratorInfo vibratorInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, tickDuration) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, clickDuration) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, thudDuration) + .build(); + + assertEquals(5, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .getDuration(vibratorInfo)); + assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_TICK) + .getDuration(vibratorInfo)); + assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) + .getDuration(vibratorInfo)); + assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .getDuration(vibratorInfo)); + assertEquals(20, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .getDuration(vibratorInfo)); + assertEquals(15, createSegmentWithFallback(VibrationEffect.EFFECT_THUD) + .getDuration(vibratorInfo)); + + // Unknown effects + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0]) + .getDuration(vibratorInfo)); + } + + @Test + public void testDuration_withVibratorNotSupportingPrimitives_returnsUnknown() { + VibratorInfo vibratorInfo = createVibratorInfoWithSupportedEffects( + VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_POP); + + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP) + .getDuration(vibratorInfo)); + assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0]) + .getDuration(vibratorInfo)); + } + + @Test public void testIsHapticFeedbackCandidate_prebakedConstants_areCandidates() { - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_HEAVY_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD) .isHapticFeedbackCandidate()); - assertTrue(new PrebakedSegment( - VibrationEffect.EFFECT_TEXTURE_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) .isHapticFeedbackCandidate()); } @@ -271,8 +315,7 @@ public class PrebakedSegmentTest { @Test public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() { - assertFalse(new PrebakedSegment( - VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) + assertFalse(createSegmentWithFallback(VibrationEffect.RINGTONES[1]) .isHapticFeedbackCandidate()); } diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java index 97f1d5e77ddb..a6d9dc51d7bb 100644 --- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java @@ -201,6 +201,22 @@ public class PrimitiveSegmentTest { } @Test + public void testDuration_withVibratorSupportingPrimitives_returnsVibratorDurationWithDelay() { + VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, /* durationMs= */ 10); + assertEquals(15, new PrimitiveSegment( + VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 5).getDuration(vibratorInfo)); + } + + @Test + public void testDuration_withVibratorNotSupportingPrimitive_returnsUnknown() { + VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK); + assertEquals(-1, new PrimitiveSegment( + VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 5).getDuration(vibratorInfo)); + } + + @Test public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() { assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK) .areVibrationFeaturesSupported( @@ -252,9 +268,14 @@ public class PrimitiveSegmentTest { } private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) { + return createVibratorInfoWithSupportedPrimitive(primitiveId, /* durationMs= */ 10); + } + + private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId, + int durationMs) { return new VibratorInfo.Builder(/* id= */ 1) .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) - .setSupportedPrimitive(primitiveId, 10) + .setSupportedPrimitive(primitiveId, durationMs) .build(); } } diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java index bea82931dda7..df874bcb73ca 100644 --- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java @@ -195,7 +195,14 @@ public class RampSegmentTest { @Test public void testDuration() { + VibratorInfo infoWithSupport = + createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true); + VibratorInfo infoWithoutSupport = + createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ false); + assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration()); + assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithSupport)); + assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithoutSupport)); } @Test diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java index 411074a75e2e..914117c10c87 100644 --- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java @@ -213,7 +213,13 @@ public class StepSegmentTest { @Test public void testDuration() { + VibratorInfo infoWithSupport = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true); + VibratorInfo infoWithoutSupport = + createVibInfoForAmplitude(/* hasAmplitudeControl= */ false); + assertEquals(5, new StepSegment(0, 0, 5).getDuration()); + assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithSupport)); + assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithoutSupport)); } @Test diff --git a/errorprone/Android.bp b/errorprone/Android.bp index c1d2235e3324..b559a15c3a60 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -22,6 +22,7 @@ java_library_host { static_libs: [ "annotations", + "jsr305", "framework-annotations-lib", "//external/error_prone:error_prone_core", ], diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java index 8dc9579e6b52..6d5e44844a83 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java @@ -29,6 +29,7 @@ import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.fixes.SuggestedFix; import com.google.errorprone.matchers.Description; import com.google.errorprone.util.ASTHelpers; +import com.google.errorprone.util.ErrorProneComment; import com.google.errorprone.util.ErrorProneToken; import com.google.errorprone.util.ErrorProneTokens; import com.sun.source.tree.ClassTree; @@ -37,7 +38,6 @@ import com.sun.source.tree.MethodTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; -import com.sun.tools.javac.parser.Tokens; import java.util.HashMap; import java.util.Map; @@ -66,7 +66,7 @@ public class HideInCommentsChecker extends BugChecker implements final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state); final String sourceCode = state.getSourceCode().toString(); for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) { - for (Tokens.Comment comment : token.comments()) { + for (ErrorProneComment comment : token.comments()) { if (!javadocableTrees.containsKey(token.pos())) { continue; } @@ -81,7 +81,7 @@ public class HideInCommentsChecker extends BugChecker implements return NO_MATCH; } - private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) { + private static Optional<SuggestedFix> generateFix(ErrorProneComment comment) { final String text = comment.getText(); if (text.startsWith("/**")) { return Optional.empty(); diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt index 96ffa03a1f65..52ce8cb5c0dc 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt @@ -67,6 +67,7 @@ class BubbleStackViewTest { private val context = ApplicationProvider.getApplicationContext<Context>() private lateinit var positioner: BubblePositioner + private lateinit var bubbleLogger: BubbleLogger private lateinit var iconFactory: BubbleIconFactory private lateinit var expandedViewManager: FakeBubbleExpandedViewManager private lateinit var bubbleStackView: BubbleStackView @@ -96,10 +97,11 @@ class BubbleStackViewTest { ) ) positioner = BubblePositioner(context, windowManager) + bubbleLogger = BubbleLogger(UiEventLoggerFake()) bubbleData = BubbleData( context, - BubbleLogger(UiEventLoggerFake()), + bubbleLogger, positioner, BubbleEducationController(context), shellExecutor, @@ -394,6 +396,7 @@ class BubbleStackViewTest { expandedViewManager, bubbleTaskViewFactory, positioner, + bubbleLogger, bubbleStackView, null, iconFactory, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt index 9fdde128ce41..712cc7c475bc 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt @@ -29,6 +29,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R +import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.protolog.ProtoLog import com.android.internal.statusbar.IStatusBarService import com.android.launcher3.icons.BubbleIconFactory @@ -70,6 +71,7 @@ class BubbleViewInfoTaskTest { private lateinit var bgExecutor: TestExecutor private lateinit var bubbleStackView: BubbleStackView private lateinit var bubblePositioner: BubblePositioner + private lateinit var bubbleLogger: BubbleLogger private lateinit var expandedViewManager: BubbleExpandedViewManager private val bubbleTaskViewFactory = BubbleTaskViewFactory { @@ -103,10 +105,11 @@ class BubbleViewInfoTaskTest { mainExecutor ) bubblePositioner = BubblePositioner(context, windowManager) + bubbleLogger = BubbleLogger(UiEventLoggerFake()) val bubbleData = BubbleData( context, - mock<BubbleLogger>(), + bubbleLogger, bubblePositioner, BubbleEducationController(context), mainExecutor, @@ -138,7 +141,7 @@ class BubbleViewInfoTaskTest { WindowManagerShellWrapper(mainExecutor), mock<UserManager>(), mock<LauncherApps>(), - mock<BubbleLogger>(), + bubbleLogger, mock<TaskStackListenerImpl>(), mock<ShellTaskOrganizer>(), bubblePositioner, @@ -314,6 +317,7 @@ class BubbleViewInfoTaskTest { expandedViewManager, bubbleTaskViewFactory, bubblePositioner, + bubbleLogger, bubbleStackView, null /* layerView */, iconFactory, diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt index 35d459f27534..f181ce004478 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -27,11 +27,13 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.BubbleData import com.android.wm.shell.bubbles.BubbleExpandedViewManager +import com.android.wm.shell.bubbles.BubbleLogger import com.android.wm.shell.bubbles.BubblePositioner import com.android.wm.shell.bubbles.BubbleTaskView import com.android.wm.shell.bubbles.BubbleTaskViewFactory @@ -106,11 +108,12 @@ class BubbleBarExpandedViewTest { bubbleExpandedView.initialize( expandedViewManager, positioner, + BubbleLogger(UiEventLoggerFake()), false /* isOverflow */, bubbleTaskView, mainExecutor, bgExecutor, - regionSamplingProvider + regionSamplingProvider, ) getInstrumentation().runOnMainSync(Runnable { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index e3fc5c2273e2..e8e25e20d8d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -572,6 +572,7 @@ public class Bubble implements BubbleViewProvider { * @param expandedViewManager the bubble expanded view manager. * @param taskViewFactory the task view factory used to create the task view for the bubble. * @param positioner the bubble positioner. + * @param bubbleLogger log bubble metrics. * @param stackView the view the bubble is added to, iff showing as floating. * @param layerView the layer the bubble is added to, iff showing in the bubble bar. * @param iconFactory the icon factory used to create images for the bubble. @@ -581,6 +582,7 @@ public class Bubble implements BubbleViewProvider { BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory iconFactory, @@ -595,6 +597,7 @@ public class Bubble implements BubbleViewProvider { expandedViewManager, taskViewFactory, positioner, + bubbleLogger, stackView, layerView, iconFactory, @@ -616,6 +619,7 @@ public class Bubble implements BubbleViewProvider { expandedViewManager, taskViewFactory, positioner, + bubbleLogger, stackView, layerView, iconFactory, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index c81ffdabf9b8..37e8ead4fc78 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -892,7 +892,7 @@ public class BubbleController implements ConfigurationChangeListener, registerBroadcastReceiver(); if (isShowingAsBubbleBar()) { mBubbleData.getOverflow().initializeForBubbleBar( - mExpandedViewManager, mBubblePositioner); + mExpandedViewManager, mBubblePositioner, mLogger); } else { mBubbleData.getOverflow().initialize( mExpandedViewManager, mStackView, mBubblePositioner); @@ -1100,6 +1100,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1111,6 +1112,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1246,9 +1248,12 @@ public class BubbleController implements ConfigurationChangeListener, */ public void dragBubbleToDismiss(String bubbleKey, long timestamp) { String selectedBubbleKey = mBubbleData.getSelectedBubbleKey(); - if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) { + Bubble bubbleToDismiss = mBubbleData.getAnyBubbleWithkey(bubbleKey); + if (bubbleToDismiss != null) { mBubbleData.dismissBubbleWithKey( bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp); + mLogger.log(bubbleToDismiss, + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE); } if (mBubbleData.hasBubbles()) { // We still have bubbles, if we dragged an individual bubble to dismiss we were expanded @@ -1582,6 +1587,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1644,6 +1650,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, @@ -1724,6 +1731,7 @@ public class BubbleController implements ConfigurationChangeListener, mExpandedViewManager, mBubbleTaskViewFactory, mBubblePositioner, + mLogger, mStackView, mLayerView, mBubbleIconFactory, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 709a7bdc61f2..4de9dfa54c5d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -495,7 +495,7 @@ public class BubbleData { /** * When this method is called it is expected that all info in the bubble has completed loading. * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager, - * BubbleTaskViewFactory, BubblePositioner, BubbleStackView, + * BubbleTaskViewFactory, BubblePositioner, BubbleLogger, BubbleStackView, * com.android.wm.shell.bubbles.bar.BubbleBarLayerView, * com.android.launcher3.icons.BubbleIconFactory, boolean) */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index 68c4657f2b68..c74412b825d9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -73,17 +73,19 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl fun initializeForBubbleBar( expandedViewManager: BubbleExpandedViewManager, - positioner: BubblePositioner + positioner: BubblePositioner, + bubbleLogger: BubbleLogger, ) { createBubbleBarExpandedView() .initialize( expandedViewManager, positioner, + bubbleLogger, /* isOverflow= */ true, /* bubbleTaskView= */ null, /* mainExecutor= */ null, /* backgroundExecutor= */ null, - /* regionSamplingProvider= */ null + /* regionSamplingProvider= */ null, ) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 39fb2f49c1ee..96b6043059d2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -73,6 +73,7 @@ public class BubbleViewInfoTask { private final WeakReference<BubbleExpandedViewManager> mExpandedViewManager; private final WeakReference<BubbleTaskViewFactory> mTaskViewFactory; private final WeakReference<BubblePositioner> mPositioner; + private final WeakReference<BubbleLogger> mBubbleLogger; private final WeakReference<BubbleStackView> mStackView; private final WeakReference<BubbleBarLayerView> mLayerView; private final BubbleIconFactory mIconFactory; @@ -94,6 +95,7 @@ public class BubbleViewInfoTask { BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory factory, @@ -106,6 +108,7 @@ public class BubbleViewInfoTask { mExpandedViewManager = new WeakReference<>(expandedViewManager); mTaskViewFactory = new WeakReference<>(taskViewFactory); mPositioner = new WeakReference<>(positioner); + mBubbleLogger = new WeakReference<>(bubbleLogger); mStackView = new WeakReference<>(stackView); mLayerView = new WeakReference<>(layerView); mIconFactory = factory; @@ -221,8 +224,9 @@ public class BubbleViewInfoTask { ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s", mBubble.getKey()); viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(), - mPositioner.get(), false /* isOverflow */, viewInfo.taskView, - mMainExecutor, mBgExecutor, new RegionSamplingProvider() { + mPositioner.get(), mBubbleLogger.get(), false /* isOverflow */, + viewInfo.taskView, mMainExecutor, mBgExecutor, + new RegionSamplingProvider() { @Override public RegionSamplingHelper createHelper(View sampledView, RegionSamplingHelper.SamplingCallback callback, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java index e9a593392dc2..c1da94cc470f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java @@ -78,6 +78,7 @@ public class BubbleViewInfoTaskLegacy extends private WeakReference<BubbleExpandedViewManager> mExpandedViewManager; private WeakReference<BubbleTaskViewFactory> mTaskViewFactory; private WeakReference<BubblePositioner> mPositioner; + private WeakReference<BubbleLogger> mBubbleLogger; private WeakReference<BubbleStackView> mStackView; private WeakReference<BubbleBarLayerView> mLayerView; private BubbleIconFactory mIconFactory; @@ -95,6 +96,7 @@ public class BubbleViewInfoTaskLegacy extends BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, @Nullable BubbleStackView stackView, @Nullable BubbleBarLayerView layerView, BubbleIconFactory factory, @@ -107,6 +109,7 @@ public class BubbleViewInfoTaskLegacy extends mExpandedViewManager = new WeakReference<>(expandedViewManager); mTaskViewFactory = new WeakReference<>(taskViewFactory); mPositioner = new WeakReference<>(positioner); + mBubbleLogger = new WeakReference<>(bubbleLogger); mStackView = new WeakReference<>(stackView); mLayerView = new WeakReference<>(layerView); mIconFactory = factory; @@ -124,8 +127,9 @@ public class BubbleViewInfoTaskLegacy extends } if (mLayerView.get() != null) { return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(), - mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory, - mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor); + mTaskViewFactory.get(), mPositioner.get(), mBubbleLogger.get(), + mLayerView.get(), mIconFactory, mBubble, mSkipInflation, mMainExecutor, + mBackgroundExecutor); } else { return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(), mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory, @@ -187,6 +191,7 @@ public class BubbleViewInfoTaskLegacy extends BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, + BubbleLogger bubbleLogger, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, Bubble b, @@ -200,9 +205,9 @@ public class BubbleViewInfoTaskLegacy extends LayoutInflater inflater = LayoutInflater.from(c); info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); - info.bubbleBarExpandedView.initialize( - expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView, - mainExecutor, backgroundExecutor, new RegionSamplingProvider() { + info.bubbleBarExpandedView.initialize(expandedViewManager, positioner, bubbleLogger, + false /* isOverflow */, bubbleTaskView, mainExecutor, backgroundExecutor, + new RegionSamplingProvider() { @Override public RegionSamplingHelper createHelper(View sampledView, RegionSamplingHelper.SamplingCallback callback, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 2a9001728cc2..84405bbe5823 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleExpandedViewManager; +import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubbleOverflowContainerView; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleTaskView; @@ -90,6 +91,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private Bubble mBubble; private BubbleExpandedViewManager mManager; private BubblePositioner mPositioner; + private BubbleLogger mBubbleLogger; private boolean mIsOverflow; private BubbleTaskViewHelper mBubbleTaskViewHelper; private BubbleBarMenuViewController mMenuViewController; @@ -178,6 +180,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView /** Initializes the view, must be called before doing anything else. */ public void initialize(BubbleExpandedViewManager expandedViewManager, BubblePositioner positioner, + BubbleLogger bubbleLogger, boolean isOverflow, @Nullable BubbleTaskView bubbleTaskView, @Nullable Executor mainExecutor, @@ -185,6 +188,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Nullable RegionSamplingProvider regionSamplingProvider) { mManager = expandedViewManager; mPositioner = positioner; + mBubbleLogger = bubbleLogger; mIsOverflow = isOverflow; mMainExecutor = mainExecutor; mBackgroundExecutor = backgroundExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java index 0b2b3e7c41c8..85921703d559 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java @@ -87,7 +87,7 @@ public class DividerSnapAlgorithm { private final boolean mCalculateRatiosBasedOnAvailableSpace; /** Allows split ratios that go offscreen (a.k.a. "flexible split") */ private final boolean mAllowOffscreenRatios; - private final boolean mIsHorizontalDivision; + private final boolean mIsLeftRightSplit; /** The first target which is still splitting the screen */ private final SnapTarget mFirstSplitTarget; @@ -101,13 +101,13 @@ public class DividerSnapAlgorithm { public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, - boolean isHorizontalDivision, Rect insets, int dockSide) { - this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets, - dockSide, false /* minimized */, true /* resizable */); + boolean isLeftRightSplit, Rect insets, int dockSide) { + this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets, + dockSide, false /* minimized */, true /* resizable */); } public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, - boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode, + boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode, boolean isHomeResizable) { mMinFlingVelocityPxPerSecond = MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density; @@ -116,7 +116,7 @@ public class DividerSnapAlgorithm { mDividerSize = dividerSize; mDisplayWidth = displayWidth; mDisplayHeight = displayHeight; - mIsHorizontalDivision = isHorizontalDivision; + mIsLeftRightSplit = isLeftRightSplit; mInsets.set(insets); mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED : res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode); @@ -133,7 +133,7 @@ public class DividerSnapAlgorithm { Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res); mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize( com.android.internal.R.dimen.task_height_of_minimized_mode) : 0; - calculateTargets(isHorizontalDivision, dockSide); + calculateTargets(isLeftRightSplit, dockSide); mFirstSplitTarget = mTargets.get(1); mLastSplitTarget = mTargets.get(mTargets.size() - 2); mDismissStartTarget = mTargets.get(0); @@ -218,18 +218,18 @@ public class DividerSnapAlgorithm { } private int getStartInset() { - if (mIsHorizontalDivision) { - return mInsets.top; - } else { + if (mIsLeftRightSplit) { return mInsets.left; + } else { + return mInsets.top; } } private int getEndInset() { - if (mIsHorizontalDivision) { - return mInsets.bottom; - } else { + if (mIsLeftRightSplit) { return mInsets.right; + } else { + return mInsets.bottom; } } @@ -269,11 +269,11 @@ public class DividerSnapAlgorithm { return mTargets.get(minIndex); } - private void calculateTargets(boolean isHorizontalDivision, int dockedSide) { + private void calculateTargets(boolean isLeftRightSplit, int dockedSide) { mTargets.clear(); - int dividerMax = isHorizontalDivision - ? mDisplayHeight - : mDisplayWidth; + int dividerMax = isLeftRightSplit + ? mDisplayWidth + : mDisplayHeight; int startPos = -mDividerSize; if (dockedSide == DOCKED_RIGHT) { startPos += mInsets.left; @@ -281,38 +281,38 @@ public class DividerSnapAlgorithm { mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f)); switch (mSnapMode) { case SNAP_MODE_16_9: - addRatio16_9Targets(isHorizontalDivision, dividerMax); + addRatio16_9Targets(isLeftRightSplit, dividerMax); break; case SNAP_FIXED_RATIO: - addFixedDivisionTargets(isHorizontalDivision, dividerMax); + addFixedDivisionTargets(isLeftRightSplit, dividerMax); break; case SNAP_ONLY_1_1: - addMiddleTarget(isHorizontalDivision); + addMiddleTarget(isLeftRightSplit); break; case SNAP_MODE_MINIMIZED: - addMinimizedTarget(isHorizontalDivision, dockedSide); + addMinimizedTarget(isLeftRightSplit, dockedSide); break; } mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f)); } - private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, + private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition, int bottomPosition, int dividerMax) { @PersistentSnapPosition int firstTarget = mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66; @PersistentSnapPosition int lastTarget = mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33; maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget); - addMiddleTarget(isHorizontalDivision); + addMiddleTarget(isLeftRightSplit); maybeAddTarget(bottomPosition, dividerMax - getEndInset() - (bottomPosition + mDividerSize), lastTarget); } - private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) { - int start = isHorizontalDivision ? mInsets.top : mInsets.left; - int end = isHorizontalDivision - ? mDisplayHeight - mInsets.bottom - : mDisplayWidth - mInsets.right; + private void addFixedDivisionTargets(boolean isLeftRightSplit, int dividerMax) { + int start = isLeftRightSplit ? mInsets.left : mInsets.top; + int end = isLeftRightSplit + ? mDisplayWidth - mInsets.right + : mDisplayHeight - mInsets.bottom; int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2; if (mAllowOffscreenRatios) { @@ -323,23 +323,23 @@ public class DividerSnapAlgorithm { } int topPosition = start + size; int bottomPosition = end - size - mDividerSize; - addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); + addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax); } - private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) { - int start = isHorizontalDivision ? mInsets.top : mInsets.left; - int end = isHorizontalDivision - ? mDisplayHeight - mInsets.bottom - : mDisplayWidth - mInsets.right; - int startOther = isHorizontalDivision ? mInsets.left : mInsets.top; - int endOther = isHorizontalDivision + private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) { + int start = isLeftRightSplit ? mInsets.left : mInsets.top; + int end = isLeftRightSplit ? mDisplayWidth - mInsets.right : mDisplayHeight - mInsets.bottom; + int startOther = isLeftRightSplit ? mInsets.top : mInsets.left; + int endOther = isLeftRightSplit + ? mDisplayHeight - mInsets.bottom + : mDisplayWidth - mInsets.right; float size = 9.0f / 16.0f * (endOther - startOther); int sizeInt = (int) Math.floor(size); int topPosition = start + sizeInt; int bottomPosition = end - sizeInt - mDividerSize; - addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); + addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax); } /** @@ -352,17 +352,17 @@ public class DividerSnapAlgorithm { } } - private void addMiddleTarget(boolean isHorizontalDivision) { - int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, + private void addMiddleTarget(boolean isLeftRightSplit) { + int position = DockedDividerUtils.calculateMiddlePosition(isLeftRightSplit, mInsets, mDisplayWidth, mDisplayHeight, mDividerSize); mTargets.add(new SnapTarget(position, SNAP_TO_2_50_50)); } - private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) { + private void addMinimizedTarget(boolean isLeftRightSplit, int dockedSide) { // In portrait offset the position by the statusbar height, in landscape add the statusbar // height as well to match portrait offset int position = mTaskHeightInMinimizedMode + mInsets.top; - if (!isHorizontalDivision) { + if (isLeftRightSplit) { if (dockedSide == DOCKED_LEFT) { position += mInsets.left; } else if (dockedSide == DOCKED_RIGHT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java index f25dfeafb32c..25157c05d0de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java @@ -97,12 +97,12 @@ public class DockedDividerUtils { } } - public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets, + public static int calculateMiddlePosition(boolean isLeftRightSplit, Rect insets, int displayWidth, int displayHeight, int dividerSize) { - int start = isHorizontalDivision ? insets.top : insets.left; - int end = isHorizontalDivision - ? displayHeight - insets.bottom - : displayWidth - insets.right; + int start = isLeftRightSplit ? insets.left : insets.top; + int end = isLeftRightSplit + ? displayWidth - insets.right + : displayHeight - insets.bottom; return start + (end - start) / 2 - dividerSize / 2; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index c9c3aa0dd537..f73065ea8eb8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -665,7 +665,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange rootBounds.width(), rootBounds.height(), mDividerSize, - !mIsLeftRightSplit, + mIsLeftRightSplit, insets, mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */); } 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 52262e68c401..97397cea4a38 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 @@ -16,6 +16,7 @@ package com.android.wm.shell.dagger; +import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT; @@ -727,7 +728,8 @@ public abstract class WMShellModule { Transitions transitions, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor) { - return Flags.enableDesktopWindowingTransitions() + return (Flags.enableDesktopWindowingTransitions() || + ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue()) ? new SpringDragToDesktopTransitionHandler(context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor) : new DefaultDragToDesktopTransitionHandler(context, transitions, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index 379e052e7b38..8ebe503a3816 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -21,17 +21,39 @@ import com.android.internal.protolog.ProtoLog import com.android.internal.util.FrameworkStatsLog import com.android.window.flags.Flags import com.android.wm.shell.EventLogTags -import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import java.security.SecureRandom +import java.util.Random +import java.util.concurrent.atomic.AtomicInteger + /** Event logger for logging desktop mode session events */ class DesktopModeEventLogger { + private val random: Random = SecureRandom() + + /** The session id for the current desktop mode session */ + @VisibleForTesting + val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID) + + private fun generateSessionId() = 1 + random.nextInt(1 shl 20) + /** - * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for - * entering desktop mode + * Logs enter into desktop mode with [enterReason] */ - fun logSessionEnter(sessionId: Int, enterReason: EnterReason) { + fun logSessionEnter(enterReason: EnterReason) { + val sessionId = generateSessionId() + val previousSessionId = currentSessionId.getAndSet(sessionId) + if (previousSessionId != NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Existing desktop mode session id: %s found on desktop " + + "mode enter", + previousSessionId + ) + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session enter, session: %s reason: %s", sessionId, enterReason.name @@ -47,12 +69,20 @@ class DesktopModeEventLogger { } /** - * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for - * exiting desktop mode + * Logs exit from desktop mode session with [exitReason] */ - fun logSessionExit(sessionId: Int, exitReason: ExitReason) { + fun logSessionExit(exitReason: ExitReason) { + val sessionId = currentSessionId.getAndSet(NO_SESSION_ID) + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging exit from desktop mode" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session exit, session: %s reason: %s", sessionId, exitReason.name @@ -68,12 +98,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] was added in the desktop mode session having - * session id [sessionId] + * Logs that a task with [taskUpdate] was added in a desktop mode session */ - fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskAdded(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task added" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task added, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -85,12 +123,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] was removed in the desktop mode session having - * session id [sessionId] + * Logs that a task with [taskUpdate] was removed from a desktop mode session */ - fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskRemoved(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task removed" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task remove, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -102,12 +148,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session - * having session id [sessionId] + * Logs that a task with [taskUpdate] had it's info changed in a desktop mode session */ - fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskInfoChanged(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task info changed" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task info changed, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -119,14 +173,23 @@ class DesktopModeEventLogger { } /** - * Logs that a task resize event is starting with [taskSizeUpdate] within a - * Desktop mode [sessionId]. + * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode + * session. */ - fun logTaskResizingStarted(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) { if (!Flags.enableResizingMetrics()) return + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging start of task resizing" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s", sessionId, taskSizeUpdate.instanceId @@ -138,14 +201,22 @@ class DesktopModeEventLogger { } /** - * Logs that a task resize event is ending with [taskSizeUpdate] within a - * Desktop mode [sessionId]. + * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session. */ - fun logTaskResizingEnded(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) { if (!Flags.enableResizingMetrics()) return + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging end of task resizing" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s", sessionId, taskSizeUpdate.instanceId @@ -248,6 +319,7 @@ class DesktopModeEventLogger { } companion object { + /** * Describes a task position and dimensions. * @@ -465,5 +537,6 @@ class DesktopModeEventLogger { FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE private const val DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED + @VisibleForTesting const val NO_SESSION_ID = 0 } -} +}
\ No newline at end of file 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 f847aa8918c2..ed03982d191d 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 @@ -35,8 +35,6 @@ import androidx.core.util.isEmpty import androidx.core.util.isNotEmpty import androidx.core.util.plus import androidx.core.util.putAll -import com.android.internal.logging.InstanceId -import com.android.internal.logging.InstanceIdSequence import com.android.internal.protolog.ProtoLog import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason @@ -48,8 +46,8 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE -import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions @@ -65,8 +63,6 @@ class DesktopModeLoggerTransitionObserver( private val desktopModeEventLogger: DesktopModeEventLogger ) : Transitions.TransitionObserver { - private val idSequence: InstanceIdSequence by lazy { InstanceIdSequence(Int.MAX_VALUE) } - init { if (DesktopModeStatus.canEnterDesktopMode(context)) { shellInit.addInitCallback(this::onInit, this) @@ -87,15 +83,7 @@ class DesktopModeLoggerTransitionObserver( // following enter reason could be Screen On private var wasPreviousTransitionExitByScreenOff: Boolean = false - // The instanceId for the current logging session - private var loggerInstanceId: InstanceId? = null - - private val isSessionActive: Boolean - get() = loggerInstanceId != null - - private fun setSessionInactive() { - loggerInstanceId = null - } + @VisibleForTesting var isSessionActive: Boolean = false fun onInit() { transitions.registerObserver(this) @@ -247,38 +235,32 @@ class DesktopModeLoggerTransitionObserver( ) { // Sessions is finishing, log task updates followed by an exit event identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) desktopModeEventLogger.logSessionExit( - loggerInstanceId!!.id, getExitReason(transitionInfo) ) - - setSessionInactive() + isSessionActive = false } else if ( postTransitionVisibleFreeformTasks.isNotEmpty() && preTransitionVisibleFreeformTasks.isEmpty() && !isSessionActive ) { // Session is starting, log enter event followed by task updates - loggerInstanceId = idSequence.newInstanceId() + isSessionActive = true desktopModeEventLogger.logSessionEnter( - loggerInstanceId!!.id, getEnterReason(transitionInfo) ) identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) } else if (isSessionActive) { // Session is neither starting, nor finishing, log task updates if there are any identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) @@ -291,7 +273,6 @@ class DesktopModeLoggerTransitionObserver( /** Compare the old and new state of taskInfos and identify and log the changes */ private fun identifyAndLogTaskUpdates( - sessionId: Int, preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>, postTransitionVisibleFreeformTasks: SparseArray<TaskInfo> ) { @@ -302,7 +283,7 @@ class DesktopModeLoggerTransitionObserver( when { // new tasks added previousTaskInfo == null -> { - desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate) + desktopModeEventLogger.logTaskAdded(currentTaskUpdate) Trace.setCounter( Trace.TRACE_TAG_WINDOW_MANAGER, VISIBLE_TASKS_COUNTER_NAME, @@ -315,14 +296,14 @@ class DesktopModeLoggerTransitionObserver( // TODO(b/347935387): Log changes only once they are stable. buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size()) != currentTaskUpdate -> - desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate) + desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate) } } // find old tasks that were removed preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo -> if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) { - desktopModeEventLogger.logTaskRemoved(sessionId, + desktopModeEventLogger.logTaskRemoved( buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())) Trace.setCounter( Trace.TRACE_TAG_WINDOW_MANAGER, @@ -417,13 +398,6 @@ class DesktopModeLoggerTransitionObserver( visibleFreeformTaskInfos.set(taskInfo.taskId, taskInfo) } - @VisibleForTesting fun getLoggerSessionId(): Int? = loggerInstanceId?.id - - @VisibleForTesting - fun setLoggerSessionId(id: Int) { - loggerInstanceId = InstanceId.fakeInstanceId(id) - } - private fun TransitionInfo.Change.requireTaskInfo(): RunningTaskInfo { return this.taskInfo ?: throw IllegalStateException("Expected TaskInfo in the Change") } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index 52b92a89abdf..61de0777ed05 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -378,11 +378,12 @@ public class DesktopModeVisualIndicator { private static VisualIndicatorAnimator fadeBoundsIn( @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect startBounds = getIndicatorBounds(displayLayout, type); + final Rect endBounds = getIndicatorBounds(displayLayout, type); + final Rect startBounds = getMinBounds(endBounds); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( - view, startBounds, getMaxBounds(startBounds)); + view, startBounds, endBounds); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM); return animator; @@ -390,8 +391,8 @@ public class DesktopModeVisualIndicator { private static VisualIndicatorAnimator fadeBoundsOut( @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect endBounds = getIndicatorBounds(displayLayout, type); - final Rect startBounds = getMaxBounds(endBounds); + final Rect startBounds = getIndicatorBounds(displayLayout, type); + final Rect endBounds = getMinBounds(startBounds); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( @@ -422,28 +423,35 @@ public class DesktopModeVisualIndicator { return animator; } + /** Calculates the bounds the indicator should have when fully faded in. */ private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type) { - final int padding = layout.stableInsets().top; + final Rect desktopStableBounds = new Rect(); + layout.getStableBounds(desktopStableBounds); + final int padding = desktopStableBounds.top; switch (type) { case TO_FULLSCREEN_INDICATOR: - return new Rect(padding, padding, - layout.width() - padding, - layout.height() - padding); + desktopStableBounds.top += padding; + desktopStableBounds.bottom -= padding; + desktopStableBounds.left += padding; + desktopStableBounds.right -= padding; + return desktopStableBounds; case TO_DESKTOP_INDICATOR: final float adjustmentPercentage = 1f - DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE; - return new Rect((int) (adjustmentPercentage * layout.width() / 2), - (int) (adjustmentPercentage * layout.height() / 2), - (int) (layout.width() - (adjustmentPercentage * layout.width() / 2)), - (int) (layout.height() - (adjustmentPercentage * layout.height() / 2))); + return new Rect((int) (adjustmentPercentage * desktopStableBounds.width() / 2), + (int) (adjustmentPercentage * desktopStableBounds.height() / 2), + (int) (desktopStableBounds.width() + - (adjustmentPercentage * desktopStableBounds.width() / 2)), + (int) (desktopStableBounds.height() + - (adjustmentPercentage * desktopStableBounds.height() / 2))); case TO_SPLIT_LEFT_INDICATOR: return new Rect(padding, padding, - layout.width() / 2 - padding, - layout.height() - padding); + desktopStableBounds.width() / 2 - padding, + desktopStableBounds.height()); case TO_SPLIT_RIGHT_INDICATOR: - return new Rect(layout.width() / 2 + padding, padding, - layout.width() - padding, - layout.height() - padding); + return new Rect(desktopStableBounds.width() / 2 + padding, padding, + desktopStableBounds.width() - padding, + desktopStableBounds.height()); default: throw new IllegalArgumentException("Invalid indicator type provided."); } @@ -505,17 +513,18 @@ public class DesktopModeVisualIndicator { } /** - * Return the max bounds of a visual indicator + * Return the minimum bounds of a visual indicator, to be used at the end of fading out + * and the start of fading in. */ - private static Rect getMaxBounds(Rect startBounds) { - return new Rect((int) (startBounds.left - - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), - (int) (startBounds.top - - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())), - (int) (startBounds.right - + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), - (int) (startBounds.bottom - + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height()))); + private static Rect getMinBounds(Rect maxBounds) { + return new Rect((int) (maxBounds.left + + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())), + (int) (maxBounds.top + + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())), + (int) (maxBounds.right + - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())), + (int) (maxBounds.bottom + - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height()))); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index f783b45589b3..5a2abe1e7e25 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -16,9 +16,9 @@ package com.android.wm.shell.unfold; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; @@ -30,6 +30,7 @@ import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -45,6 +46,8 @@ import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator; import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; @@ -56,6 +59,18 @@ import java.util.concurrent.Executor; */ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener { + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE, + DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD, + DefaultDisplayChange.DEFAULT_DISPLAY_FOLD, + }) + private @interface DefaultDisplayChange { + int DEFAULT_DISPLAY_NO_CHANGE = 0; + int DEFAULT_DISPLAY_UNFOLD = 1; + int DEFAULT_DISPLAY_FOLD = 2; + } + private final ShellUnfoldProgressProvider mUnfoldProgressProvider; private final Transitions mTransitions; private final Executor mExecutor; @@ -66,7 +81,10 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Nullable private IBinder mTransition; + // TODO: b/318803244 - remove when we could guarantee finishing the animation + // after startAnimation callback private boolean mAnimationFinished = false; + private float mLastAnimationProgress = 0.0f; private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>(); public UnfoldTransitionHandler(ShellInit shellInit, @@ -107,16 +125,6 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { - if (shouldPlayUnfoldAnimation(info) && transition != mTransition) { - // Take over transition that has unfold, we might receive it if no other handler - // accepted request in handleRequest, e.g. for rotation + unfold or - // TRANSIT_NONE + unfold transitions - mTransition = transition; - - ProtoLog.v(WM_SHELL_TRANSITIONS, "UnfoldTransitionHandler: " - + "take over startAnimation"); - } - if (transition != mTransition) return false; for (int i = 0; i < mAnimators.size(); i++) { @@ -158,6 +166,8 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Override public void onStateChangeProgress(float progress) { + mLastAnimationProgress = progress; + if (mTransition == null) return; SurfaceControl.Transaction transaction = null; @@ -182,8 +192,14 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Override public void onStateChangeFinished() { - mAnimationFinished = true; finishTransitionIfNeeded(); + + // mLastAnimationProgress is guaranteed to be 0f when folding finishes, see + // {@link PhysicsBasedUnfoldTransitionProgressProvider#cancelTransition}. + // We can use it as an indication that the next animation progress events will be related + // to unfolding, so let's reset mAnimationFinished to 'false' in this case. + final boolean isFoldingFinished = mLastAnimationProgress == 0f; + mAnimationFinished = !isFoldingFinished; } @Override @@ -211,6 +227,12 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene // Apply changes happening during the unfold animation immediately t.apply(); finishCallback.onTransitionFinished(null); + + if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) { + // Force-finish current unfold animation as we are processing folding now which doesn't + // have any animations on the Shell side + finishTransitionIfNeeded(); + } } /** Whether `request` contains an unfold action. */ @@ -219,18 +241,25 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene if (!ValueAnimator.areAnimatorsEnabled()) return false; return (request.getType() == TRANSIT_CHANGE - && request.getDisplayChange() != null - && isUnfoldDisplayChange(request.getDisplayChange())); + && getDefaultDisplayChange(request.getDisplayChange()) + == DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD); } - private boolean isUnfoldDisplayChange( - @NonNull TransitionRequestInfo.DisplayChange displayChange) { + @DefaultDisplayChange + private int getDefaultDisplayChange( + @Nullable TransitionRequestInfo.DisplayChange displayChange) { + if (displayChange == null) return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE; + + if (displayChange.getDisplayId() != DEFAULT_DISPLAY) { + return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE; + } + if (!displayChange.isPhysicalDisplayChanged()) { - return false; + return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE; } if (displayChange.getStartAbsBounds() == null || displayChange.getEndAbsBounds() == null) { - return false; + return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE; } // Handle only unfolding, currently we don't have an animation when folding @@ -239,17 +268,11 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene final int startArea = displayChange.getStartAbsBounds().width() * displayChange.getStartAbsBounds().height(); - return endArea > startArea; + return endArea > startArea ? DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD + : DefaultDisplayChange.DEFAULT_DISPLAY_FOLD; } - /** Whether `transitionInfo` contains an unfold action. */ - public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) { - // Unfold animation won't play when animations are disabled - if (!ValueAnimator.areAnimatorsEnabled()) return false; - // Only handle transitions that are marked as physical display switch - // See PhysicalDisplaySwitchTransitionLauncher for the conditions - if ((transitionInfo.getFlags() & TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH) == 0) return false; - + private int getDefaultDisplayChange(@NonNull TransitionInfo transitionInfo) { for (int i = 0; i < transitionInfo.getChanges().size(); i++) { final TransitionInfo.Change change = transitionInfo.getChanges().get(i); // We are interested only in display container changes @@ -268,11 +291,13 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene * change.getStartAbsBounds().height(); if (afterArea > beforeArea) { - return true; + return DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD; + } else { + return DefaultDisplayChange.DEFAULT_DISPLAY_FOLD; } } - return false; + return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE; } @Nullable @@ -293,10 +318,6 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Override public void onFoldStateChanged(boolean isFolded) { if (isFolded) { - // Reset unfold animation finished flag on folding, so it could be used next time - // when we unfold the device as an indication that animation hasn't finished yet - mAnimationFinished = false; - // If we are currently animating unfold animation we should finish it because // the animation might not start and finish as the device was folded finishTransitionIfNeeded(); 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 2c621b1f1a52..d3b7ca15856f 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 @@ -514,8 +514,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin )); } else { mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData( - mTaskInfo, TaskInfoKt.getRequestingImmersive(mTaskInfo), inFullImmersive, - hasGlobalFocus + mTaskInfo, + TaskInfoKt.getRequestingImmersive(mTaskInfo), + inFullImmersive, + hasGlobalFocus, + /* maximizeHoverEnabled= */ canOpenMaximizeMenu( + /* animatingTaskResizeOrReposition= */ false) )); } Trace.endSection(); @@ -1616,8 +1620,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) { if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return; - asAppHeader(mWindowDecorViewHolder) - .setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition); + final boolean inFullImmersive = + mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId); + asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData( + mTaskInfo, + TaskInfoKt.getRequestingImmersive(mTaskInfo), + inFullImmersive, + isFocused(), + /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition))); } /** @@ -1634,6 +1644,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin asAppHeader(mWindowDecorViewHolder).onMaximizeWindowHoverEnter(); } + private boolean canOpenMaximizeMenu(boolean animatingTaskResizeOrReposition) { + if (!Flags.enableFullyImmersiveInDesktop()) { + return !animatingTaskResizeOrReposition; + } + final boolean inImmersiveAndRequesting = + mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId) + && TaskInfoKt.getRequestingImmersive(mTaskInfo); + return !animatingTaskResizeOrReposition && !inImmersiveAndRequesting; + } + @Override public String toString() { return "{" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index cf03b3f74dc7..d94391820d05 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -70,7 +70,7 @@ class AppHeaderViewHolder( rootView: View, onCaptionTouchListener: View.OnTouchListener, onCaptionButtonClickListener: View.OnClickListener, - onLongClickListener: OnLongClickListener, + private val onLongClickListener: OnLongClickListener, onCaptionGenericMotionListener: View.OnGenericMotionListener, appName: CharSequence, appIconBitmap: Bitmap, @@ -81,7 +81,8 @@ class AppHeaderViewHolder( val taskInfo: RunningTaskInfo, val isRequestingImmersive: Boolean, val inFullImmersiveState: Boolean, - val hasGlobalFocus: Boolean + val hasGlobalFocus: Boolean, + val enableMaximizeLongClick: Boolean, ) : Data() private val decorThemeUtil = DecorThemeUtil(context) @@ -160,19 +161,30 @@ class AppHeaderViewHolder( } override fun bindData(data: HeaderData) { - bindData(data.taskInfo, data.isRequestingImmersive, data.inFullImmersiveState, - data.hasGlobalFocus) + bindData( + data.taskInfo, + data.isRequestingImmersive, + data.inFullImmersiveState, + data.hasGlobalFocus, + data.enableMaximizeLongClick + ) } private fun bindData( taskInfo: RunningTaskInfo, isRequestingImmersive: Boolean, inFullImmersiveState: Boolean, - hasGlobalFocus: Boolean + hasGlobalFocus: Boolean, + enableMaximizeLongClick: Boolean, ) { if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) { - bindDataWithThemedHeaders(taskInfo, isRequestingImmersive, inFullImmersiveState, - hasGlobalFocus) + bindDataWithThemedHeaders( + taskInfo, + isRequestingImmersive, + inFullImmersiveState, + hasGlobalFocus, + enableMaximizeLongClick, + ) } else { bindDataLegacy(taskInfo, hasGlobalFocus) } @@ -215,7 +227,8 @@ class AppHeaderViewHolder( taskInfo: RunningTaskInfo, requestingImmersive: Boolean, inFullImmersiveState: Boolean, - hasGlobalFocus: Boolean + hasGlobalFocus: Boolean, + enableMaximizeLongClick: Boolean, ) { val header = fillHeaderInfo(taskInfo, hasGlobalFocus) val headerStyle = getHeaderStyle(header) @@ -281,6 +294,16 @@ class AppHeaderViewHolder( drawableInsets = closeDrawableInsets ) } + if (!enableMaximizeLongClick) { + maximizeButtonView.cancelHoverAnimation() + } + maximizeButtonView.hoverDisabled = !enableMaximizeLongClick + maximizeWindowButton.onLongClickListener = if (enableMaximizeLongClick) { + onLongClickListener + } else { + // Disable long-click to open maximize menu when in immersive. + null + } } override fun onHandleMenuOpened() {} @@ -291,14 +314,6 @@ class AppHeaderViewHolder( } } - fun setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition: Boolean) { - // If animating a task resize or reposition, cancel any running hover animations - if (animatingTaskResizeOrReposition) { - maximizeButtonView.cancelHoverAnimation() - } - maximizeButtonView.hoverDisabled = animatingTaskResizeOrReposition - } - fun onMaximizeWindowHoverExit() { maximizeButtonView.cancelHoverAnimation() } @@ -364,6 +379,11 @@ class AppHeaderViewHolder( private fun shouldShowExitFullImmersiveIcon( requestingImmersive: Boolean, inFullImmersiveState: Boolean + ): Boolean = isInFullImmersiveStateAndRequesting(requestingImmersive, inFullImmersiveState) + + private fun isInFullImmersiveStateAndRequesting( + requestingImmersive: Boolean, + inFullImmersiveState: Boolean ): Boolean = Flags.enableFullyImmersiveInDesktop() && requestingImmersive && inFullImmersiveState diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java index 094af9652ea3..a1d4a1a301bd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java @@ -26,6 +26,7 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; +import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.wm.shell.ShellTestCase; import org.junit.Before; @@ -45,6 +46,7 @@ public class BubbleOverflowTest extends ShellTestCase { private TestableBubblePositioner mPositioner; private BubbleOverflow mOverflow; private BubbleExpandedViewManager mExpandedViewManager; + private BubbleLogger mBubbleLogger; @Mock private BubbleController mBubbleController; @@ -58,6 +60,7 @@ public class BubbleOverflowTest extends ShellTestCase { mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(mBubbleController); mPositioner = new TestableBubblePositioner(mContext, mContext.getSystemService(WindowManager.class)); + mBubbleLogger = new BubbleLogger(new UiEventLoggerFake()); when(mBubbleController.getPositioner()).thenReturn(mPositioner); when(mBubbleController.getStackView()).thenReturn(mBubbleStackView); @@ -77,7 +80,7 @@ public class BubbleOverflowTest extends ShellTestCase { @Test public void test_initialize_forBubbleBar() { - mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner); + mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner, mBubbleLogger); assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull(); assertThat(mOverflow.getExpandedView()).isNull(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt index dde9fda13ea9..0825b6b0d7be 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -18,7 +18,10 @@ package com.android.wm.shell.desktopmode import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule +import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations +import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker import com.android.dx.mockito.inline.extended.ExtendedMockito.verify +import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.window.flags.Flags @@ -26,15 +29,16 @@ import com.android.wm.shell.EventLogTags import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.NO_SESSION_ID import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskSizeUpdate -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON -import kotlinx.coroutines.runBlocking +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason +import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.mockito.kotlin.eq @@ -49,40 +53,87 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @JvmField @Rule(order = 0) val extendedMockitoRule = ExtendedMockitoRule.Builder(this) - .mockStatic(FrameworkStatsLog::class.java) - .mockStatic(EventLogTags::class.java).build()!! + .mockStatic(FrameworkStatsLog::class.java) + .mockStatic(EventLogTags::class.java).build()!! @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule() @Test - fun logSessionEnter_enterReason() = runBlocking { - desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER) + fun logSessionEnter_logsEnterReasonWithNewSessionId() { + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + + val sessionId = desktopModeEventLogger.currentSessionId.get() + assertThat(sessionId).isNotEqualTo(NO_SESSION_ID) + verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), + /* enter_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER), + /* exit_reason */ + eq(0), + /* sessionId */ + eq(sessionId) + ) + } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verify { + EventLogTags.writeWmShellEnterDesktopMode( + eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason), + eq(sessionId) + ) + } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logSessionEnter_ongoingSession_logsEnterReasonWithNewSessionId() { + val previousSessionId = startDesktopModeSession() + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + + val sessionId = desktopModeEventLogger.currentSessionId.get() + assertThat(sessionId).isNotEqualTo(NO_SESSION_ID) + assertThat(sessionId).isNotEqualTo(previousSessionId) verify { FrameworkStatsLog.write( eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), /* event */ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), /* enter_reason */ - eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER), + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER), /* exit_reason */ eq(0), /* sessionId */ - eq(SESSION_ID) + eq(sessionId) ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellEnterDesktopMode( - eq(EnterReason.UNKNOWN_ENTER.reason), - eq(SESSION_ID)) + eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason), + eq(sessionId) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logSessionExit_exitReason() = runBlocking { - desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT) + fun logSessionExit_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logSessionExit_logsExitReasonAndClearsSessionId() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT) verify { FrameworkStatsLog.write( @@ -92,24 +143,39 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* enter_reason */ eq(0), /* exit_reason */ - eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT), + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT), /* sessionId */ - eq(SESSION_ID) + eq(sessionId) ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellExitDesktopMode( - eq(ExitReason.UNKNOWN_EXIT.reason), - eq(SESSION_ID)) + eq(ExitReason.DRAG_TO_EXIT.reason), + eq(sessionId) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID) + } + + @Test + fun logTaskAdded_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskAdded(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskAdded_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskAdded_logsTaskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskAdded(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), /* instance_id */ @@ -125,36 +191,52 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskRemoved_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskRemoved(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskRemoved_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskRemoved_taskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskRemoved(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), /* instance_id */ @@ -170,39 +252,57 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskInfoChanged_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskInfoChanged_taskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -216,40 +316,51 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, - createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT)) + fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged( + createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT) + ) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -263,42 +374,53 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), /* minimize_reason */ eq(MinimizeReason.TASK_LIMIT.reason), /* unminimize_reason */ eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(MinimizeReason.TASK_LIMIT.reason), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, - createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP)) + fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged( + createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP) + ) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -312,39 +434,55 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), /* minimize_reason */ eq(UNSET_MINIMIZE_REASON), /* unminimize_reason */ eq(UnminimizeReason.TASKBAR_TAP.reason), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UnminimizeReason.TASKBAR_TAP.reason), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskResizingStarted_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS) - fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() = runBlocking { - desktopModeEventLogger.logTaskResizingStarted(sessionId = SESSION_ID, createTaskSizeUpdate()) + fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), /* resizing_stage */ @@ -352,7 +490,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* input_method */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD), /* desktop_mode_session_id */ - eq(SESSION_ID), + eq(sessionId), /* instance_id */ eq(TASK_SIZE_UPDATE.instanceId), /* uid */ @@ -365,15 +503,27 @@ class DesktopModeEventLoggerTest : ShellTestCase() { eq(TASK_SIZE_UPDATE.displayArea), ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + } + + @Test + fun logTaskResizingEnded_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS) - fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() = runBlocking { - desktopModeEventLogger.logTaskResizingEnded(sessionId = SESSION_ID, createTaskSizeUpdate()) + fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), /* resizing_stage */ @@ -381,7 +531,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* input_method */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD), /* desktop_mode_session_id */ - eq(SESSION_ID), + eq(sessionId), /* instance_id */ eq(TASK_SIZE_UPDATE.instanceId), /* uid */ @@ -394,6 +544,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { eq(TASK_SIZE_UPDATE.displayArea), ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + } + + private fun startDesktopModeSession(): Int { + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + clearInvocations(staticMockMarker(FrameworkStatsLog::class.java)) + clearInvocations(staticMockMarker(EventLogTags::class.java)) + return desktopModeEventLogger.currentSessionId.get() } @Test @@ -428,7 +586,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { } private companion object { - private const val SESSION_ID = 1 + private const val sessionId = 1 private const val TASK_ID = 1 private const val TASK_UID = 1 private const val TASK_X = 0 @@ -453,23 +611,12 @@ class DesktopModeEventLoggerTest : ShellTestCase() { DISPLAY_AREA, ) - private fun createTaskSizeUpdate( - resizeTrigger: ResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, - inputMethod: InputMethod = InputMethod.UNKNOWN_INPUT_METHOD, - ) = TaskSizeUpdate( - resizeTrigger, - inputMethod, - TASK_ID, - TASK_UID, - TASK_HEIGHT, - TASK_WIDTH, - DISPLAY_AREA, - ) - private fun createTaskUpdate( minimizeReason: MinimizeReason? = null, unminimizeReason: UnminimizeReason? = null, - ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason, - unminimizeReason, TASK_COUNT) + ) = TaskUpdate( + TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason, + unminimizeReason, TASK_COUNT + ) } } 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 e7593b5b9324..f25faa53f356 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 @@ -59,8 +59,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder import com.android.wm.shell.transition.Transitions -import junit.framework.Assert.assertNotNull -import junit.framework.Assert.assertNull +import kotlin.test.assertFalse +import kotlin.test.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -142,8 +142,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) + verify(desktopModeEventLogger, never()).logSessionEnter(any()) + verify(desktopModeEventLogger, never()).logTaskAdded(any()) } @Test @@ -228,11 +228,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -241,7 +240,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -259,11 +259,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -272,7 +271,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -290,11 +290,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -303,7 +302,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -324,11 +324,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { // Tests for AppFromOverview precedence in compared to cancelled Overview // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -337,7 +336,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM)) @@ -379,11 +379,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() { val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) // Previous Exit reason recorded as Screen Off - val sessionId = 1 transitionObserver.addTaskInfosToCachedMap(freeformTask) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) // Enter desktop through back transition, this happens when user enters after dismissing // keyguard val change = createChange(TRANSIT_TO_FRONT, freeformTask) @@ -399,11 +398,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() { val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) // Previous Exit reason recorded as Screen Off - val sessionId = 1 transitionObserver.addTaskInfosToCachedMap(freeformTask) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) // Enter desktop through app handle drag. This represents cases where instead of moving to // desktop right after turning the screen on, we move to fullscreen then move another task @@ -419,24 +417,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } @Test - fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() { - val sessionId = 1 + fun transitSleep_logTaskRemovedAndExitReasonScreenOff() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -444,15 +440,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -462,16 +457,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { .build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -479,16 +472,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -496,15 +487,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { - val sessionId = 1 + fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // recents transition val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM)) @@ -513,31 +503,30 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) } @Test - fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { - val sessionId = 1 + fun transitClose_logTaskRemovedAndExitReasonTaskFinished() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task closing val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE) } @Test fun sessionExitByRecents_cancelledAnimation_sessionRestored() { - val sessionId = 1 // add a freeform task to an existing session val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // recents transition sent freeform window to back val change = createChange(TRANSIT_TO_BACK, taskInfo) @@ -546,7 +535,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo1) verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() callOnTransitionReady(transitionInfo2) @@ -557,10 +547,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { - val sessionId = 1 // add an existing freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // new freeform task added val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) @@ -568,18 +557,16 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskAdded(eq(sessionId), - eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2))) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2))) + verify(desktopModeEventLogger, never()).logSessionEnter(any()) } @Test fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() { - val sessionId = 1 // add an existing freeform task val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task position changed val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100) @@ -591,18 +578,17 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), - eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))) + eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1)) + ) verifyZeroInteractions(desktopModeEventLogger) } @Test fun sessionAlreadyStarted_taskResized_logsTaskUpdate() { - val sessionId = 1 // add an existing freeform task val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task resized val newTaskInfo = @@ -618,23 +604,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq( DEFAULT_TASK_UPDATE.copy( taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100, - visibleTaskCount = 1))) + visibleTaskCount = 1)) + ) verifyZeroInteractions(desktopModeEventLogger) } @Test fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() { - val sessionId = 1 // add 2 existing freeform task val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM) val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2) transitionObserver.addTaskInfosToCachedMap(taskInfo1) transitionObserver.addTaskInfosToCachedMap(taskInfo2) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task 1 position update val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100) @@ -646,8 +631,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( - taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))) + eq(DEFAULT_TASK_UPDATE.copy( + taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2)) + ) verifyZeroInteractions(desktopModeEventLogger) // task 2 resize @@ -666,7 +652,6 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq( DEFAULT_TASK_UPDATE.copy( instanceId = 2, @@ -679,11 +664,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { - val sessionId = 1 // add two existing freeform tasks transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // new freeform task closed val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) @@ -691,9 +675,11 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( - instanceId = 2, visibleTaskCount = 1))) - verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) + .logTaskRemoved( + eq(DEFAULT_TASK_UPDATE.copy( + instanceId = 2, visibleTaskCount = 1)) + ) + verify(desktopModeEventLogger, never()).logSessionExit(any()) } /** Simulate calling the onTransitionReady() method */ @@ -706,10 +692,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) { - val sessionId = transitionObserver.getLoggerSessionId() - assertNotNull(sessionId) - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate)) + assertTrue(transitionObserver.isSessionActive) + verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate)) ExtendedMockito.verify { Trace.setCounter( eq(Trace.TRACE_TAG_WINDOW_MANAGER), @@ -725,14 +710,13 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } private fun verifyTaskRemovedAndExitLogging( - sessionId: Int, exitReason: ExitReason, taskUpdate: TaskUpdate ) { - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate)) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason)) + assertFalse(transitionObserver.isSessionActive) + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate)) + verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason)) verifyZeroInteractions(desktopModeEventLogger) - assertNull(transitionObserver.getLoggerSessionId()) } private companion object { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java index cf2de91bad88..22da66dd5e67 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java @@ -18,13 +18,13 @@ package com.android.wm.shell.unfold; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH; import static android.view.WindowManager.TRANSIT_NONE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -50,6 +50,7 @@ import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator; import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; import java.util.ArrayList; import java.util.List; @@ -140,11 +141,12 @@ public class UnfoldTransitionHandlerTest { } @Test - public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() { + public void handleFoldMergeRequest_finishesTheTransition() { TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); - + // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to + // notify about the end of the animation mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), @@ -153,16 +155,24 @@ public class UnfoldTransitionHandlerTest { finishCallback ); - verify(finishCallback, never()).onTransitionFinished(any()); + // Send fold transition request + TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class); + mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(), + mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback); + + // Verify that fold transition is merged into unfold and that unfold is finished + final InOrder inOrder = inOrder(mergeFinishCallback, finishCallback); + inOrder.verify(mergeFinishCallback).onTransitionFinished(any()); + inOrder.verify(finishCallback).onTransitionFinished(any()); } @Test - public void startAnimation_sameTransitionAsHandleRequest_startsAnimation() { + public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() { TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); - boolean animationStarted = mUnfoldTransitionHandler.startAnimation( + mUnfoldTransitionHandler.startAnimation( mTransition, mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), @@ -170,17 +180,18 @@ public class UnfoldTransitionHandlerTest { finishCallback ); - assertThat(animationStarted).isTrue(); + verify(finishCallback, never()).onTransitionFinished(any()); } @Test - public void startAnimation_differentTransitionFromRequestWithUnfold_startsAnimation() { - mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo()); + public void startAnimation_sameTransitionAsHandleRequest_startsAnimation() { + TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo(); + mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo); TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); boolean animationStarted = mUnfoldTransitionHandler.startAnimation( mTransition, - createUnfoldTransitionInfo(), + mock(TransitionInfo.class), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback @@ -196,7 +207,7 @@ public class UnfoldTransitionHandlerTest { boolean animationStarted = mUnfoldTransitionHandler.startAnimation( mTransition, - createDisplayResizeTransitionInfo(), + createUnfoldTransitionInfo(), mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), finishCallback @@ -247,6 +258,7 @@ public class UnfoldTransitionHandlerTest { TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class); mShellUnfoldProgressProvider.onStateChangeStarted(); + mShellUnfoldProgressProvider.onStateChangeProgress(0.5f); mShellUnfoldProgressProvider.onStateChangeFinished(); mUnfoldTransitionHandler.startAnimation( mTransition, @@ -279,6 +291,8 @@ public class UnfoldTransitionHandlerTest { clearInvocations(finishCallback); // Fold + mShellUnfoldProgressProvider.onStateChangeProgress(/* progress= */ 0.0f); + mShellUnfoldProgressProvider.onStateChangeFinished(); mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ true); // Second unfold @@ -370,6 +384,19 @@ public class UnfoldTransitionHandlerTest { triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */); } + private TransitionInfo createFoldTransitionInfo() { + final TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0); + + final TransitionInfo.Change change = new TransitionInfo.Change(/* container= */ null, + /* leash= */ null); + change.setFlags(TransitionInfo.FLAG_IS_DISPLAY); + change.setStartAbsBounds(new Rect(0, 0, 200, 200)); + change.setEndAbsBounds(new Rect(0, 0, 100, 100)); + transitionInfo.addChange(change); + + return transitionInfo; + } + private TransitionRequestInfo createNoneTransitionInfo() { return new TransitionRequestInfo(TRANSIT_NONE, /* triggerTask= */ null, /* remoteTransition= */ null, @@ -446,17 +473,6 @@ public class UnfoldTransitionHandlerTest { change.setEndAbsBounds(new Rect(0, 0, 100, 100)); change.setFlags(TransitionInfo.FLAG_IS_DISPLAY); transitionInfo.addChange(change); - transitionInfo.setFlags(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH); - return transitionInfo; - } - - private TransitionInfo createDisplayResizeTransitionInfo() { - TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0); - TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class)); - change.setStartAbsBounds(new Rect(0, 0, 10, 10)); - change.setEndAbsBounds(new Rect(0, 0, 100, 100)); - change.setFlags(TransitionInfo.FLAG_IS_DISPLAY); - transitionInfo.addChange(change); return transitionInfo; } diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 28d85bd860df..a3737131702d 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -242,45 +242,59 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } } -SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const { - const DirtyStack* frame = mHead; - Matrix4 transform; - SkRect pretransformResult = bounds; - while (true) { - SkRect currentBounds = pretransformResult; - pretransformResult.setEmpty(); - switch (frame->type) { - case TransformRenderNode: { - const RenderProperties& props = frame->renderNode->properties(); - // Perform clipping - if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) { - if (!currentBounds.intersect( - SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { - currentBounds.setEmpty(); - } +static void computeClipAndTransformImpl(const DirtyStack* currentFrame, SkRect* crop, + Matrix4* outMatrix) { + SkRect currentCrop = *crop; + switch (currentFrame->type) { + case TransformRenderNode: { + const RenderProperties& props = currentFrame->renderNode->properties(); + // Perform clipping + if (props.getClipDamageToBounds() && !currentCrop.isEmpty()) { + if (!currentCrop.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { + currentCrop.setEmpty(); } + } - // apply all transforms - mapRect(props, currentBounds, &pretransformResult); - frame->renderNode->applyViewPropertyTransforms(transform); - } break; - case TransformMatrix4: - mapRect(frame->matrix4, currentBounds, &pretransformResult); - transform.multiply(*frame->matrix4); - break; - default: - pretransformResult = currentBounds; - break; - } - if (frame->prev == frame) break; - frame = frame->prev; + // apply all transforms + crop->setEmpty(); + mapRect(props, currentCrop, crop); + } break; + case TransformMatrix4: + crop->setEmpty(); + mapRect(currentFrame->matrix4, currentCrop, crop); + break; + default: + break; + } + + if (currentFrame->prev != currentFrame) { + computeClipAndTransformImpl(currentFrame->prev, crop, outMatrix); + } + switch (currentFrame->type) { + case TransformRenderNode: + currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix); + break; + case TransformMatrix4: + outMatrix->multiply(*currentFrame->matrix4); + break; + case TransformNone: + // nothing to be done + break; + default: + LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", + currentFrame->type); } - SkRect result; +} + +SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const { + SkRect cropInGlobal = bounds; + outMatrix->loadIdentity(); + computeClipAndTransformImpl(mHead, &cropInGlobal, outMatrix); + SkRect cropInLocal; Matrix4 globalToLocal; - globalToLocal.loadInverse(transform); - mapRect(&globalToLocal, pretransformResult, &result); - *outMatrix = transform; - return result; + globalToLocal.loadInverse(*outMatrix); + mapRect(&globalToLocal, cropInGlobal, &cropInLocal); + return cropInLocal; } void DamageAccumulator::dirty(float left, float top, float right, float bottom) { diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 84bd45dfc012..b73380e38fd1 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -582,20 +582,22 @@ size_t Bitmap::mTotalBitmapBytes = 0; size_t Bitmap::mTotalBitmapCount = 0; void Bitmap::traceBitmapCreate() { + size_t bytes = getAllocationByteCount(); + std::lock_guard lock{mLock}; + mTotalBitmapBytes += bytes; + mTotalBitmapCount++; if (ATRACE_ENABLED()) { - std::lock_guard lock{mLock}; - mTotalBitmapBytes += getAllocationByteCount(); - mTotalBitmapCount++; ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes); ATRACE_INT64("Bitmap Count", mTotalBitmapCount); } } void Bitmap::traceBitmapDelete() { + size_t bytes = getAllocationByteCount(); + std::lock_guard lock{mLock}; + mTotalBitmapBytes -= getAllocationByteCount(); + mTotalBitmapCount--; if (ATRACE_ENABLED()) { - std::lock_guard lock{mLock}; - mTotalBitmapBytes -= getAllocationByteCount(); - mTotalBitmapCount--; ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes); ATRACE_INT64("Bitmap Count", mTotalBitmapCount); } diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java index b2861826a103..601e001f48c2 100644 --- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java +++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java @@ -57,7 +57,7 @@ import java.util.List; * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false) * 2. User doesn't set any title or icon for button. */ -public class ActionButtonsPreference extends Preference { +public class ActionButtonsPreference extends Preference implements GroupSectionDividerMixin { private static final String TAG = "ActionButtonPreference"; private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 6cd777e878fe..10769ecfbe42 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -43,7 +43,7 @@ import com.android.settingslib.widget.preference.banner.R; * Banner message is a banner displaying important information (permission request, page error etc), * and provide actions for user to address. It requires a user action to be dismissed. */ -public class BannerMessagePreference extends Preference { +public class BannerMessagePreference extends Preference implements GroupSectionDividerMixin { public enum AttentionLevel { HIGH(0, R.color.banner_background_attention_high, R.color.banner_accent_attention_high), diff --git a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt index eb14746a0f22..84ff1bbef65d 100644 --- a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt +++ b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt @@ -30,7 +30,7 @@ class CardPreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { init { layoutResource = R.layout.settingslib_expressive_preference_card diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp index cd8f584953aa..12890feaf56e 100644 --- a/packages/SettingsLib/IllustrationPreference/Android.bp +++ b/packages/SettingsLib/IllustrationPreference/Android.bp @@ -21,6 +21,7 @@ android_library { "SettingsLibColor", "androidx.preference_preference", "lottie", + "SettingsLibSettingsTheme", "settingslib_illustrationpreference_flags_lib", ], diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java index a0599bb32dd1..adc4f316aca9 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java @@ -55,7 +55,7 @@ import java.io.InputStream; /** * IllustrationPreference is a preference that can play lottie format animation */ -public class IllustrationPreference extends Preference { +public class IllustrationPreference extends Preference implements GroupSectionDividerMixin { private static final String TAG = "IllustrationPreference"; diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index e3f8fbb88a65..2e3ee32e2efb 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml index 255b2c92e709..3e0e18488f36 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml index 4425ef08d6e3..f75d9b23c49b 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:paddingVertical="@dimen/settingslib_expressive_space_small1" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml index bf34db93298b..7c0eaeaca3de 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml @@ -18,11 +18,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" - android:layout_width="match_parent" - android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingRight="?android:attr/listPreferredItemPaddingRight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> + android:layout_width="match_parent"> <TextView android:id="@+id/switch_text" diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml index bef6e352d854..fa908a4ed6c8 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml @@ -18,6 +18,10 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" + android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingRight="?android:attr/listPreferredItemPaddingRight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:importantForAccessibility="no"> <com.android.settingslib.widget.MainSwitchBar diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index d895c874d95e..3394874797e3 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -36,7 +36,8 @@ import java.util.List; * This component is used as the main switch of the page * to enable or disable the prefereces on the page. */ -public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener { +public class MainSwitchPreference extends TwoStatePreference + implements OnCheckedChangeListener, GroupSectionDividerMixin { private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>(); diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt index e27838c4ef4d..1412c84c137b 100644 --- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt +++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt @@ -27,6 +27,7 @@ import androidx.preference.PreferenceScreen import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat +import java.util.concurrent.atomic.AtomicBoolean import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -53,7 +54,7 @@ abstract class CatalystScreenTestCase { enableCatalystScreen() assertThat(preferenceScreenCreator.isFlagEnabled(context)).isTrue() val catalystScreen = dumpPreferenceScreen() - Log.i("Catalyst", catalystScreen) + Log.i(TAG, catalystScreen) disableCatalystScreen() assertThat(preferenceScreenCreator.isFlagEnabled(context)).isFalse() @@ -83,11 +84,26 @@ abstract class CatalystScreenTestCase { } private fun dumpPreferenceScreen(): String { + // Dump threads for troubleshooting when the test thread is stuck. + // Latest junit Timeout rule supports similar feature but it is not yet available on AOSP. + val taskFinished = AtomicBoolean() + Thread { + Thread.sleep(20000) + if (!taskFinished.get()) dumpThreads() + } + .apply { + isDaemon = true + start() + } + @Suppress("UNCHECKED_CAST") val clazz = preferenceScreenCreator.fragmentClass() as Class<PreferenceFragmentCompat> val builder = StringBuilder() FragmentScenario.launch(clazz).use { - it.onFragment { fragment -> fragment.preferenceScreen.toString(builder) } + it.onFragment { fragment -> + taskFinished.set(true) + fragment.preferenceScreen.toString(builder) + } } return builder.toString() } @@ -120,4 +136,16 @@ abstract class CatalystScreenTestCase { } builder.append(indent).append("}\n") } + + companion object { + const val TAG = "CatalystScreenTestCase" + + fun dumpThreads() { + for ((thread, stack) in Thread.getAllStackTraces()) { + Log.i(TAG, "$thread") + for (frame in stack) Log.i(TAG, " $frame") + Log.i(TAG, "") + } + } + } } diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt new file mode 100644 index 000000000000..ba5f5cf855be --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.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.settingslib.widget + +/** + * A base interface to indicate that a class should not have rounded corners. + * + * Classes implementing this interface will be treated as already handle rounded corners. + */ +interface GroupSectionDividerMixin
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt new file mode 100644 index 000000000000..535d80f609fb --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceScreen +import androidx.recyclerview.widget.RecyclerView + +/** Base class for Settings to use PreferenceFragmentCompat */ +open abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() { + + override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> { + if (SettingsThemeHelper.isExpressiveTheme(requireContext())) + return SettingsPreferenceGroupAdapter(preferenceScreen) + return super.onCreateAdapter(preferenceScreen) + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt new file mode 100644 index 000000000000..98b7f7664b1b --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +import android.os.Handler +import android.os.Looper +import androidx.annotation.DrawableRes +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceGroup +import androidx.preference.PreferenceGroupAdapter +import androidx.preference.PreferenceViewHolder +import com.android.settingslib.widget.theme.R + +/** + * A custom adapter for displaying settings preferences in a list, handling rounded corners + * for preference items within a group. + */ +open class SettingsPreferenceGroupAdapter @JvmOverloads constructor( + preferenceGroup: PreferenceGroup +) : PreferenceGroupAdapter(preferenceGroup) { + + private val mPreferenceGroup = preferenceGroup + private var mRoundCornerMappingList: ArrayList<Int> = ArrayList() + + private var mNormalPaddingStart = 0 + private var mGroupPaddingStart = 0 + private var mNormalPaddingEnd = 0 + private var mGroupPaddingEnd = 0 + + private val mHandler = Handler(Looper.getMainLooper()) + + private val syncRunnable = Runnable { updatePreferences() } + + init { + val context = preferenceGroup.context + mNormalPaddingStart = + context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1) + mGroupPaddingStart = mNormalPaddingStart * 2 + mNormalPaddingEnd = + context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1) + mGroupPaddingEnd = mNormalPaddingEnd * 2 + updatePreferences() + } + + override fun onPreferenceHierarchyChange(preference: Preference) { + super.onPreferenceHierarchyChange(preference) + + // Post after super class has posted their sync runnable to update preferences. + mHandler.removeCallbacks(syncRunnable) + mHandler.post(syncRunnable) + } + + override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + updateBackground(holder, position) + } + + private fun updatePreferences() { + val oldList = ArrayList(mRoundCornerMappingList) + mRoundCornerMappingList = ArrayList() + mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup) + if (mRoundCornerMappingList != oldList) { + notifyDataSetChanged() + } + } + + private fun mappingPreferenceGroup(cornerStyles: MutableList<Int>, group: PreferenceGroup) { + cornerStyles.clear() + cornerStyles.addAll(MutableList(itemCount) { 0 }) + + // the first item in to group + var startIndex = -1 + // the last item in the group + var endIndex = -1 + var currentParent: PreferenceGroup? = group + for (i in 0 until itemCount) { + when (val pref = getItem(i)) { + // the preference has round corner background, so we don't need to handle it. + is GroupSectionDividerMixin -> { + cornerStyles[i] = 0 + startIndex = -1 + endIndex = -1 + } + + // PreferenceCategory should not have round corner background. + is PreferenceCategory -> { + cornerStyles[i] = 0 + startIndex = -1 + endIndex = -1 + currentParent = pref + } + + // ExpandablePreference is PreferenceGroup but it should handle round corner + is Expandable -> { + // When ExpandablePreference is expanded, we treat is as the first item. + if (pref.isExpanded()) { + currentParent = pref as? PreferenceGroup + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP or ROUND_CORNER_CENTER + endIndex = -1 + } + } + + else -> { + val parent = pref?.parent + + // item in the group should have round corner background. + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_CENTER + if (parent === currentParent) { + // find the first item in the group + if (startIndex == -1) { + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP + } + + // find the last item in the group, if we find the new last item, we should + // remove the old last item round corner. + if (endIndex == -1 || endIndex < i) { + if (endIndex != -1) { + cornerStyles[endIndex] = + cornerStyles[endIndex] and ROUND_CORNER_BOTTOM.inv() + } + endIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM + } + } else { + // this item is new group, we should reset the index. + currentParent = parent + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP + endIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM + } + } + } + } + } + + /** handle roundCorner background */ + private fun updateBackground(holder: PreferenceViewHolder, position: Int) { + @DrawableRes val backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/) + + val v = holder.itemView + val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart + val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd + + v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom) + v.setBackgroundResource(backgroundRes) + } + + @DrawableRes + protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int { + val cornerType = mRoundCornerMappingList[position] + + if ((cornerType and ROUND_CORNER_CENTER) == 0) { + return 0 + } + + return when { + (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) == 0 -> { + // the first + if (isSelected) R.drawable.settingslib_round_background_top_selected + else R.drawable.settingslib_round_background_top + } + + (cornerType and ROUND_CORNER_BOTTOM) != 0 && (cornerType and ROUND_CORNER_TOP) == 0 -> { + // the last + if (isSelected) R.drawable.settingslib_round_background_bottom_selected + else R.drawable.settingslib_round_background_bottom + } + + (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) != 0 -> { + // the only one preference + if (isSelected) R.drawable.settingslib_round_background_selected + else R.drawable.settingslib_round_background + } + + else -> { + // in the center + if (isSelected) R.drawable.settingslib_round_background_center_selected + else R.drawable.settingslib_round_background_center + } + } + } + + companion object { + private const val ROUND_CORNER_CENTER: Int = 1 + private const val ROUND_CORNER_TOP: Int = 1 shl 1 + private const val ROUND_CORNER_BOTTOM: Int = 1 shl 2 + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt index 9b1ccef9dadf..62573fe18357 100644 --- a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt +++ b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt @@ -32,7 +32,7 @@ class ZeroStatePreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { private val iconTint: Int = context.getColor( com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSecondaryContainer diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 79c3ff9ce989..5a8763f4db6e 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -149,3 +149,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "audio_sharing_developer_option" + namespace: "cross_device_experiences" + description: "Gates whether to enable audio sharing developer option" + bug: "368401233" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/StatementService/Parser/Android.bp b/packages/StatementService/Parser/Android.bp new file mode 100644 index 000000000000..c8af1344f4c7 --- /dev/null +++ b/packages/StatementService/Parser/Android.bp @@ -0,0 +1,26 @@ +// 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 { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_library { + name: "StatementServiceParser", + use_resource_processor: true, + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + target_sdk_version: "29", +} diff --git a/packages/StatementService/Parser/AndroidManifest.xml b/packages/StatementService/Parser/AndroidManifest.xml new file mode 100644 index 000000000000..a3a99ac0552d --- /dev/null +++ b/packages/StatementService/Parser/AndroidManifest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.statementservice.parser"> +</manifest>
\ No newline at end of file diff --git a/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt new file mode 100644 index 000000000000..4314f86fad37 --- /dev/null +++ b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("DalComponentParser") + +package com.android.statementservice.parser + +import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB +import android.os.PatternMatcher.PATTERN_LITERAL +import android.os.PatternMatcher.PATTERN_PREFIX +import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB + +/** + * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type + * and pattern. Matching expressions support the following wildcards: + * + * 1) An asterisk (*) matches zero to as many characters as possible + * 2) A question mark (?) matches any single character. + * + * Matching one to many characters can be done with a question mark followed by an asterisk (?+). + * + * @param expression A matching expression string from a DAL relation extension component used for + * matching a URI part. This must be a non-empty string and all characters in the + * string should be decoded. + * + * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern. + */ +fun parseMatchingExpression(expression: String): Pair<Int, String> { + if (expression.isNullOrEmpty()) { + throw IllegalArgumentException("Matching expressions cannot be an empty string") + } + var count = 0 + var isAdvanced = expression.contains("?*") + val pattern = buildString { + for (char in expression) { + when (char) { + '*' -> { + if (this.endsWith('.') && !this.endsWith("\\.")) { + append('+') + } else { + count += 1 + append(".*") + } + } + '?' -> { + count += 1 + append('.') + } + '.' -> { + append("\\.") + } + '[', ']', '{', '}' -> { + if (isAdvanced) { + append('\\') + } + append(char) + } + else -> append(char) + } + } + } + if (count == 0) { + return Pair(PATTERN_LITERAL, pattern) + } + if (count == 1 && pattern.endsWith(".*")) { + return Pair(PATTERN_PREFIX, pattern.dropLast(2)) + } + if (isAdvanced) { + return Pair(PATTERN_ADVANCED_GLOB, pattern) + } + return Pair(PATTERN_SIMPLE_GLOB, pattern) +}
\ No newline at end of file diff --git a/packages/StatementService/TEST_MAPPING b/packages/StatementService/TEST_MAPPING new file mode 100644 index 000000000000..0714c9366665 --- /dev/null +++ b/packages/StatementService/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit" : [ + { + "name": "StatementServiceTests" + } + ] +}
\ No newline at end of file diff --git a/packages/StatementService/tests/Android.bp b/packages/StatementService/tests/Android.bp new file mode 100644 index 000000000000..ec1bd96a0bc0 --- /dev/null +++ b/packages/StatementService/tests/Android.bp @@ -0,0 +1,30 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package { + default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_framework_android_packages", +} + +android_test { + name: "StatementServiceTests", + use_resource_processor: true, + test_suites: ["general-tests"], + srcs: ["src/**/*.kt"], + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.runner", + "StatementServiceParser", + "truth", + ], +} diff --git a/packages/StatementService/tests/AndroidManifest.xml b/packages/StatementService/tests/AndroidManifest.xml new file mode 100644 index 000000000000..bde0f953252c --- /dev/null +++ b/packages/StatementService/tests/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.statementservice.test"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.statementservice.test" /> +</manifest> diff --git a/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt new file mode 100644 index 000000000000..44a56ec91458 --- /dev/null +++ b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.statementservice.parser + +import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB +import android.os.PatternMatcher.PATTERN_LITERAL +import android.os.PatternMatcher.PATTERN_PREFIX +import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class DalComponentParserTest { + + @Test + fun parseExpressions() { + validateParsedExpression("foobar", PATTERN_LITERAL, "foobar") + validateParsedExpression("foo.bar", PATTERN_LITERAL, "foo\\.bar") + validateParsedExpression("foo*", PATTERN_PREFIX, "foo") + validateParsedExpression("*bar", PATTERN_SIMPLE_GLOB, ".*bar") + validateParsedExpression("foo*bar", PATTERN_SIMPLE_GLOB, "foo.*bar") + validateParsedExpression("foo.*bar", PATTERN_SIMPLE_GLOB, "foo\\..*bar") + validateParsedExpression("*foo*bar", PATTERN_SIMPLE_GLOB, ".*foo.*bar") + validateParsedExpression("foo?bar", PATTERN_SIMPLE_GLOB, "foo.bar") + validateParsedExpression("foo.?bar", PATTERN_SIMPLE_GLOB, "foo\\..bar") + validateParsedExpression("?bar", PATTERN_SIMPLE_GLOB, ".bar") + validateParsedExpression("foo?", PATTERN_SIMPLE_GLOB, "foo.") + validateParsedExpression("fo?b*r", PATTERN_SIMPLE_GLOB, "fo.b.*r") + validateParsedExpression("?*bar", PATTERN_ADVANCED_GLOB, ".+bar") + validateParsedExpression("foo?*bar", PATTERN_ADVANCED_GLOB, "foo.+bar") + validateParsedExpression("foo?*bar*", PATTERN_ADVANCED_GLOB, "foo.+bar.*") + validateParsedExpression("foo*?bar", PATTERN_SIMPLE_GLOB, "foo.*.bar") + + // set matches are not supported in DAL + validateParsedExpression("foo[a-z]", PATTERN_LITERAL, "foo[a-z]") + validateParsedExpression("foo[a-z]+", PATTERN_LITERAL, "foo[a-z]+") + validateParsedExpression("foo[a-z]*", PATTERN_PREFIX, "foo[a-z]") + validateParsedExpression("[a-z]*bar", PATTERN_SIMPLE_GLOB, "[a-z].*bar") + validateParsedExpression("foo[a-z]?bar", PATTERN_SIMPLE_GLOB, "foo[a-z].bar") + validateParsedExpression("foo[a-z]?*bar", PATTERN_ADVANCED_GLOB, "foo\\[a-z\\].+bar") + + // range matches are not supported in DAL + validateParsedExpression("fo{2}", PATTERN_LITERAL, "fo{2}") + validateParsedExpression("fo{2}+", PATTERN_LITERAL, "fo{2}+") + validateParsedExpression("fo{2}*", PATTERN_PREFIX, "fo{2}") + validateParsedExpression("fo{2}*bar", PATTERN_SIMPLE_GLOB, "fo{2}.*bar") + validateParsedExpression("fo{2}?*", PATTERN_ADVANCED_GLOB, "fo\\{2\\}.+") + validateParsedExpression("foo{2}?*bar", PATTERN_ADVANCED_GLOB, "foo\\{2\\}.+bar") + } + + @Test(expected = IllegalArgumentException::class) + fun parseEmptyExpression() { + parseMatchingExpression("") + } + + private fun validateParsedExpression(given: String, expectedType: Int, expectedFilter: String) { + val (type, filter) = parseMatchingExpression(given) + assertThat(filter).isEqualTo(expectedFilter) + assertThat(type).isEqualTo(expectedType) + assertThat(filter).isEqualTo(expectedFilter) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 28635318236d..3c560fdcadb6 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -87,7 +87,6 @@ filegroup { srcs: [ "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt", "tests/src/**/systemui/broadcast/ActionReceiverTest.kt", - "tests/src/**/systemui/doze/DozeMachineTest.java", "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java", "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java", "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt", @@ -175,7 +174,6 @@ filegroup { "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt", "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt", "tests/src/**/systemui/controls/ui/DetailDialogTest.kt", - "tests/src/**/systemui/doze/DozeMachineTest.kt", "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt", "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt", "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java", @@ -318,9 +316,6 @@ filegroup { "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt", "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt", "tests/src/**/keyguard/ClockEventControllerTest.kt", - "tests/src/**/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt", - "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.kt", - "tests/src/**/keyguard/LegacyLockIconViewControllerTest.java", "tests/src/**/systemui/animation/TransitionAnimatorTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt", @@ -417,7 +412,6 @@ filegroup { "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt", "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt", "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java", - "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.java", "tests/src/**/keyguard/CarrierTextManagerTest.java", "tests/src/**/systemui/ScreenDecorationsTest.java", "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt", @@ -899,9 +893,9 @@ android_robolectric_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", "truth", ], @@ -936,9 +930,9 @@ android_robolectric_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", "truth", ], @@ -974,9 +968,9 @@ android_ravenwood_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", ], auto_gen_config: true, plugins: [ diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 189294484085..540115de8830 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -395,9 +395,9 @@ flag { } flag { - name: "status_bar_ron_chips" + name: "status_bar_notification_chips" namespace: "systemui" - description: "Show rich ongoing notifications as chips in the status bar" + description: "Show promoted ongoing notifications as chips in the status bar" bug: "361346412" } @@ -1487,3 +1487,13 @@ flag { description: "Enables notes role qs tile which opens default notes role app in app bubbles" bug: "357863750" } + +flag { + name: "media_projection_request_attribution_fix" + namespace: "systemui" + description: "Ensure MediaProjection consent requests are properly attributed" + bug: "373581993" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 96c47cc5fb6f..4ab526188ffe 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -765,7 +765,7 @@ private fun BoxScope.CommunalHubLazyGrid( alpha = { outlineAlpha }, modifier = Modifier.requiredSize(dpSize).thenIf( - dragDropState.draggingItemIndex != index + dragDropState.draggingItemKey != item.key ) { Modifier.animateItem( placementSpec = spring(stiffness = Spring.StiffnessMediumLow) @@ -778,7 +778,7 @@ private fun BoxScope.CommunalHubLazyGrid( dragDropState = dragDropState, selected = selected, enabled = item.isWidgetContent(), - index = index, + key = item.key, ) { isDragging -> CommunalContent( modifier = Modifier.fillMaxSize(), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index 101385f88825..0718bc331d41 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -38,8 +38,9 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.round import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toSize import com.android.systemui.Flags.communalWidgetResizing @@ -93,7 +94,7 @@ internal constructor( private val scope: CoroutineScope, private val updateDragPositionForRemove: (offset: Offset) -> Boolean, ) { - var draggingItemIndex by mutableStateOf<Int?>(null) + var draggingItemKey by mutableStateOf<Any?>(null) private set var isDraggingToRemove by mutableStateOf(false) @@ -105,6 +106,8 @@ internal constructor( private var draggingItemInitialOffset by mutableStateOf(Offset.Zero) private var dragStartPointerOffset by mutableStateOf(Offset.Zero) + private var previousTargetItemKey: Any? = null + internal val draggingItemOffset: Offset get() = draggingItemLayoutInfo?.let { item -> @@ -112,7 +115,7 @@ internal constructor( } ?: Offset.Zero private val draggingItemLayoutInfo: LazyGridItemInfo? - get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex } + get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.key == draggingItemKey } /** * Called when dragging is initiated. @@ -137,7 +140,7 @@ internal constructor( .firstItemAtOffset(normalizedOffset - contentOffset) ?.apply { dragStartPointerOffset = normalizedOffset - this.offset.toOffset() - draggingItemIndex = index + draggingItemKey = key draggingItemInitialOffset = this.offset.toOffset() return true } @@ -146,16 +149,19 @@ internal constructor( } internal fun onDragInterrupted() { - draggingItemIndex?.let { + draggingItemKey?.let { if (isDraggingToRemove) { - contentListState.onRemove(it) + contentListState.onRemove( + contentListState.list.indexOfFirst { it.key == draggingItemKey } + ) isDraggingToRemove = false updateDragPositionForRemove(Offset.Zero) } // persist list editing changes on dragging ends contentListState.onSaveList() - draggingItemIndex = null + draggingItemKey = null } + previousTargetItemKey = null draggingItemDraggedDelta = Offset.Zero draggingItemInitialOffset = Offset.Zero dragStartPointerOffset = Offset.Zero @@ -170,15 +176,29 @@ internal constructor( val startOffset = draggingItem.offset.toOffset() + draggingItemOffset val endOffset = startOffset + draggingItem.size.toSize() val middleOffset = startOffset + (endOffset - startOffset) / 2f + val draggingBoundingBox = + IntRect(draggingItem.offset + draggingItemOffset.round(), draggingItem.size) val targetItem = - state.layoutInfo.visibleItemsInfo - .asSequence() - .filter { item -> contentListState.isItemEditable(item.index) } - .filter { item -> draggingItem.index != item.index } - .firstItemAtOffset(middleOffset) + if (communalWidgetResizing()) { + state.layoutInfo.visibleItemsInfo.findLast { item -> + val itemBoundingBox = IntRect(item.offset, item.size) + draggingItemKey != item.key && + contentListState.isItemEditable(item.index) && + draggingBoundingBox.contains(itemBoundingBox.center) + } + } else { + state.layoutInfo.visibleItemsInfo + .asSequence() + .filter { item -> contentListState.isItemEditable(item.index) } + .filter { item -> draggingItem.index != item.index } + .firstItemAtOffset(middleOffset) + } - if (targetItem != null) { + if ( + targetItem != null && + (!communalWidgetResizing() || targetItem.key != previousTargetItemKey) + ) { val scrollToIndex = if (targetItem.index == state.firstVisibleItemIndex) { draggingItem.index @@ -187,6 +207,14 @@ internal constructor( } else { null } + if (communalWidgetResizing()) { + // Keep track of the previous target item, to avoid rapidly oscillating between + // items if the target item doesn't visually move as a result of the index change. + // In this case, even after the index changes, we'd still be colliding with the + // element, so it would be selected as the target item the next time this function + // runs again, which would trigger us to revert the index change we recently made. + previousTargetItemKey = targetItem.key + } if (scrollToIndex != null) { scope.launch { // this is needed to neutralize automatic keeping the first item first. @@ -196,20 +224,17 @@ internal constructor( } else { contentListState.onMove(draggingItem.index, targetItem.index) } - draggingItemIndex = targetItem.index isDraggingToRemove = false - } else { + } else if (targetItem == null) { val overscroll = checkForOverscroll(startOffset, endOffset) if (overscroll != 0f) { scrollChannel.trySend(overscroll) } isDraggingToRemove = checkForRemove(startOffset) + previousTargetItemKey = null } } - private val LazyGridItemInfo.offsetEnd: IntOffset - get() = this.offset + this.size - /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */ private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float { return when { @@ -237,7 +262,7 @@ fun Modifier.dragContainer( viewModel: BaseCommunalViewModel, ): Modifier { return this.then( - pointerInput(dragDropState, contentOffset) { + Modifier.pointerInput(dragDropState, contentOffset) { detectDragGesturesAfterLongPress( onDrag = { change, offset -> change.consume() @@ -273,7 +298,7 @@ fun Modifier.dragContainer( @Composable fun LazyGridItemScope.DraggableItem( dragDropState: GridDragDropState, - index: Int, + key: Any, enabled: Boolean, selected: Boolean, modifier: Modifier = Modifier, @@ -283,7 +308,7 @@ fun LazyGridItemScope.DraggableItem( return content(false) } - val dragging = index == dragDropState.draggingItemIndex + val dragging = key == dragDropState.draggingItemKey val itemAlpha: Float by animateFloatAsState( targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt index 54497f621c73..2a91bd8b1d73 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt @@ -133,7 +133,14 @@ fun SceneScope.QuickSettingsLayout( Column( verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding), horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding), + modifier = + modifier + .fillMaxWidth() + .padding( + start = QuickSettingsShade.Dimensions.Padding, + end = QuickSettingsShade.Dimensions.Padding, + top = QuickSettingsShade.Dimensions.Padding, + ), ) { BrightnessSliderContainer( viewModel = viewModel.brightnessSliderViewModel, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 7e288ddd3a4c..9f99c372e2a8 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -631,7 +631,13 @@ internal class NestedScrollHandlerImpl( return@PriorityNestedScrollConnection false } - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo // If the current swipe transition is *not* closed to 0f or 1f, then we want the // scroll events to intercept the current transition to continue the scene @@ -650,7 +656,12 @@ internal class NestedScrollHandlerImpl( val isZeroOffset = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo val canStart = when (behavior) { @@ -685,7 +696,12 @@ internal class NestedScrollHandlerImpl( // We could start an overscroll animation canChangeScene = false - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable) if (canStart) { @@ -707,6 +723,12 @@ internal class NestedScrollHandlerImpl( onScroll = { offsetAvailable, _ -> val controller = dragController ?: error("Should be called after onStart") + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection 0f + } + // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is // initiated in a nested child. controller.onDrag(delta = offsetAvailable) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index aa70a0ce156b..8613f6da0f62 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.pointer.AwaitPointerEventScope import androidx.compose.ui.input.pointer.PointerEvent import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope @@ -184,6 +185,7 @@ internal class MultiPointerDraggableNode( private var startedPosition: Offset? = null private var pointersDown: Int = 0 + private var isMouseWheel: Boolean = false internal fun pointersInfo(): PointersInfo { return PointersInfo( @@ -191,6 +193,7 @@ internal class MultiPointerDraggableNode( startedPosition = startedPosition, // We could have 0 pointers during fling or for other reasons. pointersDown = pointersDown.coerceAtLeast(1), + isMouseWheel = isMouseWheel, ) } @@ -202,8 +205,15 @@ internal class MultiPointerDraggableNode( // [requireAncestorPointersInfoOwner], to our descendants. while (currentContext.isActive) { // During the Initial pass, we receive the event after our ancestors. - val changes = awaitPointerEvent(PointerEventPass.Initial).changes + val pointerEvent = awaitPointerEvent(PointerEventPass.Initial) + + // Ignore cursor has entered the input region. + // This will only be sent after the cursor is hovering when in the input region. + if (pointerEvent.type == PointerEventType.Enter) continue + + val changes = pointerEvent.changes pointersDown = changes.countDown() + isMouseWheel = pointerEvent.type == PointerEventType.Scroll when { // There are no more pointers down. @@ -223,7 +233,8 @@ internal class MultiPointerDraggableNode( // The first pointer down, startedPosition was not set. startedPosition == null -> { - val firstPointerDown = changes.single() + // Mouse wheel could start with multiple pointer down + val firstPointerDown = changes.first() velocityPointerId = firstPointerDown.id velocityTracker.resetTracking() velocityTracker.addPointerInputChange(firstPointerDown) @@ -647,4 +658,8 @@ internal fun interface PointersInfoOwner { fun pointersInfo(): PointersInfo } -internal data class PointersInfo(val startedPosition: Offset?, val pointersDown: Int) +internal data class PointersInfo( + val startedPosition: Offset?, + val pointersDown: Int, + val isMouseWheel: Boolean, +) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 2657d7cf8156..3c3c612c028b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -430,6 +430,10 @@ internal class MutableSceneTransitionLayoutStateImpl( // Replace the transition. transitionStates = transitionStates.subList(0, transitionStates.lastIndex) + transition + + // Make sure it is removed from the finishedTransitions set if it was already + // finished. + finishedTransitions.remove(currentState) } else { // Append the new transition. transitionStates = transitionStates + transition diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index 57b9423e85d1..a0fed9064181 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -127,7 +127,7 @@ class DraggableHandlerTest { val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal) var pointerInfoOwner: () -> PointersInfo = { - PointersInfo(startedPosition = Offset.Zero, pointersDown = 1) + PointersInfo(startedPosition = Offset.Zero, pointersDown = 1, isMouseWheel = false) } fun nestedScrollConnection( @@ -1187,7 +1187,9 @@ class DraggableHandlerTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) // Drag from the **top** of the screen - pointerInfoOwner = { PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1) } + pointerInfoOwner = { + PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = false) + } assertIdle(currentScene = SceneC) nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) @@ -1205,7 +1207,11 @@ class DraggableHandlerTest { // Drag from the **bottom** of the screen pointerInfoOwner = { - PointersInfo(startedPosition = Offset(0f, SCREEN_SIZE), pointersDown = 1) + PointersInfo( + startedPosition = Offset(0f, SCREEN_SIZE), + pointersDown = 1, + isMouseWheel = false, + ) } assertIdle(currentScene = SceneC) @@ -1220,6 +1226,22 @@ class DraggableHandlerTest { } @Test + fun ignoreMouseWheel() = runGestureTest { + // Start at scene C. + navigateToSceneC() + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) + + // Use mouse wheel + pointerInfoOwner = { + PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = true) + } + assertIdle(currentScene = SceneC) + + nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) + assertIdle(currentScene = SceneC) + } + + @Test fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest { // Swipe up from the middle to transition to scene B. val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index f3a34884c756..f5bb5ba032c2 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -727,4 +727,45 @@ class SceneTransitionLayoutStateTest { // The previous job is cancelled and does not infinitely collect the progress. job.join() } + + @Test + fun replacedTransitionIsRemovedFromFinishedTransitions() = runTest { + val state = MutableSceneTransitionLayoutState(SceneA) + + val aToB = + transition( + SceneA, + SceneB, + onFreezeAndAnimate = { + // Do nothing, so that this transition stays in the transitionStates list and we + // can finish() it manually later. + }, + ) + val replacingAToB = transition(SceneB, SceneC) + val replacingBToC = transition(SceneB, SceneC, replacedTransition = replacingAToB) + + // Start A => B. + val aToBJob = state.startTransitionImmediately(animationScope = this, aToB) + + // Start B => C and immediately finish it. It will be flagged as finished in + // STLState.finishedTransitions given that A => B is not finished yet. + val bToCJob = state.startTransitionImmediately(animationScope = this, replacingAToB) + replacingAToB.finish() + bToCJob.join() + + // Start a new B => C that replaces the previously finished B => C. + val replacingBToCJob = + state.startTransitionImmediately(animationScope = this, replacingBToC) + + // Finish A => B. + aToB.finish() + aToBJob.join() + + // Finish the new B => C. + replacingBToC.finish() + replacingBToCJob.join() + + assertThat(state.transitionState).isIdle() + assertThat(state.transitionState).hasCurrentScene(SceneC) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 1711f3145cae..3001505d0e02 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -17,6 +17,8 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.rememberScrollableState +import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -39,12 +41,14 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.ScrollWheel import androidx.compose.ui.test.assertPositionInRootIsEqualTo import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performMouseInput import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeRight import androidx.compose.ui.test.swipeUp @@ -498,6 +502,89 @@ class SwipeToSceneTest { } @Test + fun mouseWheel_pointerInputApi_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + TestContent(layoutState) + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + } + + @Test + fun mouseWheel_scrollableCannotScroll_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + Box( + Modifier.fillMaxSize() + // A scrollable that does not consume the scroll gesture + .scrollable(rememberScrollableState { 0f }, Orientation.Vertical) + ) + } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + } + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + } + + @Test + fun mouseWheel_scrollableConsume_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + var lastScroll = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + Box( + Modifier.fillMaxSize() + // A scrollable that consumes the scroll gesture + .scrollable( + rememberScrollableState { + lastScroll = it + it + }, + Orientation.Vertical, + ) + ) + } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + } + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop * 10, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + + // Mouse wheel scroll can still be consumed by the scrollable + assertThat(lastScroll).isNotEqualTo(0f) + assertThat(touchSlop).isNotEqualTo(0f) + } + + @Test fun transitionKey() { val transitionkey = TransitionKey(debugName = "foo") val state = diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md index 234c7a032d2e..bf15b4feadfb 100644 --- a/packages/SystemUI/docs/scene.md +++ b/packages/SystemUI/docs/scene.md @@ -63,7 +63,7 @@ the instructions below to turn it on. NOTE: in case these instructions become stale and don't actually enable the framework, please make sure `SceneContainerFlag.isEnabled` in the [`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt) -file evalutes to `true`. +file evaluates to `true`. 1. Set a collection of **aconfig flags** to `true` by running the following commands: @@ -73,7 +73,6 @@ file evalutes to `true`. $ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true $ adb shell device_config override systemui com.android.systemui.notification_avalanche_throttle_hun true $ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true - $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true $ adb shell device_config override systemui com.android.systemui.scene_container true ``` 2. **Restart** System UI by issuing the following command: diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt deleted file mode 100644 index 13306becf6d2..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager -import org.junit.Assert.assertFalse -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.junit.MockitoJUnit - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper -class UdfpsBpViewControllerTest : SysuiTestCase() { - - @JvmField @Rule var rule = MockitoJUnit.rule() - - @Mock lateinit var udfpsBpView: UdfpsBpView - @Mock lateinit var statusBarStateController: StatusBarStateController - @Mock lateinit var shadeInteractor: ShadeInteractor - @Mock lateinit var systemUIDialogManager: SystemUIDialogManager - @Mock lateinit var dumpManager: DumpManager - @Mock lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor - - private lateinit var udfpsBpViewController: UdfpsBpViewController - - @Before - fun setup() { - udfpsBpViewController = - UdfpsBpViewController( - udfpsBpView, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - - @TestableLooper.RunWithLooper(setAsMainLooper = true) - @Test - fun testShouldNeverPauseAuth() { - assertFalse(udfpsBpViewController.shouldPauseAuth()) - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 437a4b357372..21c6583d4e84 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -16,34 +16,17 @@ package com.android.systemui.biometrics; -import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; -import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; -import static android.view.MotionEvent.ACTION_DOWN; -import static android.view.MotionEvent.ACTION_MOVE; -import static android.view.MotionEvent.ACTION_UP; - -import static com.android.internal.util.FunctionalUtils.ThrowingConsumer; -import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.graphics.Rect; -import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; @@ -58,13 +41,9 @@ import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.testing.TestableLooper.RunWithLooper; -import android.util.Pair; -import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.Surface; import android.view.View; -import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityManager; @@ -75,15 +54,11 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.biometrics.udfps.InteractionEvent; -import com.android.systemui.biometrics.udfps.NormalizedTouchData; import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor; -import com.android.systemui.biometrics.udfps.TouchProcessorResult; import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel; import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -102,7 +77,6 @@ import com.android.systemui.power.data.repository.FakePowerRepository; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.shared.model.WakeSleepReason; import com.android.systemui.power.shared.model.WakefulnessState; -import com.android.systemui.res.R; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; @@ -130,7 +104,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -204,16 +177,6 @@ public class UdfpsControllerTest extends SysuiTestCase { private FeatureFlags mFeatureFlags; // Stuff for configuring mocks @Mock - private UdfpsView mUdfpsView; - @Mock - private UdfpsBpView mBpView; - @Mock - private UdfpsFpmEmptyView mFpmEmptyView; - @Mock - private UdfpsKeyguardViewLegacy mKeyguardView; - private final UdfpsAnimationViewController mUdfpsKeyguardViewController = - mock(UdfpsKeyguardViewControllerLegacy.class); - @Mock private SystemUIDialogManager mSystemUIDialogManager; @Mock private ActivityTransitionAnimator mActivityTransitionAnimator; @@ -241,8 +204,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<View> mViewCaptor; @Captor - private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor; - @Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor; @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor; @@ -287,18 +248,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mContext.getOrCreateTestableResources() .addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false); - when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)) - .thenReturn(mUdfpsView); - when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) - .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD - when(mLayoutInflater.inflate(R.layout.udfps_bp_view, null)) - .thenReturn(mBpView); - when(mLayoutInflater.inflate(R.layout.udfps_fpm_empty_view, null)) - .thenReturn(mFpmEmptyView); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); when(mSessionTracker.getSessionId(anyInt())).thenReturn( (new InstanceIdSequence(1 << 20)).newInstanceId()); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */, @@ -327,7 +279,6 @@ public class UdfpsControllerTest extends SysuiTestCase { // Create a fake background executor. mBiometricExecutor = new FakeExecutor(new FakeSystemClock()); - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); initUdfpsController(mOpticalProps); } @@ -349,7 +300,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mFalsingManager, mPowerManager, mAccessibilityManager, - mLockscreenShadeTransitionController, mScreenLifecycle, mVibrator, mUdfpsHapticsSimulator, @@ -390,101 +340,6 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void dozeTimeTick() throws RemoteException { - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - mUdfpsController.dozeTimeTick(); - verify(mUdfpsView).dozeTimeTick(); - } - - @Test - public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { - onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */); - } - - @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale() - throws RemoteException { - onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */); - } - - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale) - throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_MOVE is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - if (stale) { - mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId); - mFgExecutor.runAllReady(); - } - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager, stale ? never() : times(1)) - .notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test - public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); - - // GIVEN that the overlay is showing - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException { // GIVEN overlay was showing and the udfps bouncer is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, @@ -524,61 +379,6 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception { - final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)}; - final int[] displayWidth = new int[]{1080, 1440}; - final int[] displayHeight = new int[]{1920, 2560}; - final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]}; - final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90}; - final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0], - sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0], - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); - - for (int i1 = 0; i1 <= 1; ++i1) { - for (int i2 = 0; i2 <= 1; ++i2) { - for (int i3 = 0; i3 <= 1; ++i3) { - for (int i4 = 0; i4 <= 1; ++i4) { - for (int i5 = 0; i5 <= 1; ++i5) { - final UdfpsOverlayParams newParams = new UdfpsOverlayParams( - sensorBounds[i1], sensorBounds[i1], displayWidth[i2], - displayHeight[i3], scaleFactor[i4], rotation[i5], - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); - - if (newParams.equals(oldParams)) { - continue; - } - - // Initialize the overlay with old parameters. - mUdfpsController.updateOverlayParams(mOpticalProps, oldParams); - - // Show the overlay. - reset(mWindowManager); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, - mOpticalProps.sensorId, - BiometricRequestConstants.REASON_ENROLL_ENROLLING, - mUdfpsOverlayControllerCallback); - - mFgExecutor.runAllReady(); - verify(mWindowManager).addView(mViewCaptor.capture(), any()); - when(mViewCaptor.getValue().getParent()) - .thenReturn(mock(ViewGroup.class)); - - // Update overlay parameters. - reset(mWindowManager); - mUdfpsController.updateOverlayParams(mOpticalProps, newParams); - mFgExecutor.runAllReady(); - - // Ensure the overlay was recreated. - verify(mWindowManager).removeView(any()); - verify(mWindowManager).addView(any(), any()); - } - } - } - } - } - } - - @Test public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception { final Rect sensorBounds = new Rect(10, 10, 20, 20); final int displayWidth = 1080; @@ -595,7 +395,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); - verify(mWindowManager).addView(any(), any()); // Update overlay with the same parameters. mUdfpsController.updateOverlayParams(mOpticalProps, @@ -606,870 +405,4 @@ public class UdfpsControllerTest extends SysuiTestCase { // Ensure the overlay was not recreated. verify(mWindowManager, never()).removeView(any()); } - - private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor, - float major) { - MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties(); - pp.id = 1; - MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords(); - pc.x = x; - pc.y = y; - pc.touchMinor = minor; - pc.touchMajor = major; - return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp}, - new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0); - } - - private static class TestParams { - public final FingerprintSensorPropertiesInternal sensorProps; - - TestParams(FingerprintSensorPropertiesInternal sensorProps) { - this.sensorProps = sensorProps; - } - } - - private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) { - for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps, - mUltrasonicProps)) { - initUdfpsController(sensorProps); - testParamsConsumer.accept(new TestParams(sensorProps)); - } - } - - @Test - public void onTouch_propagatesTouchInNativeOrientationAndResolution() { - runWithAllParams( - this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized); - } - - private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized( - TestParams testParams) throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner. - final int pointerId = 0; - final int displayWidth = 1080; - final int displayHeight = 1920; - final float scaleFactor = 1f; // This means the native resolution is 1440x2560. - final float touchMinor = 10f; - final float touchMajor = 20f; - final float orientation = 30f; - - // Expecting a touch at the very bottom right corner in native orientation and resolution. - final float expectedX = displayWidth / scaleFactor; - final float expectedY = displayHeight / scaleFactor; - final float expectedMinor = touchMinor / scaleFactor; - final float expectedMajor = touchMajor / scaleFactor; - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // GIVEN a valid touch on sensor - NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth, - displayHeight, touchMinor, touchMajor, orientation, 0L, 0L); - TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1, touchData); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorDownResult); - - // Show the overlay. - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // Test ROTATION_0 - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_0, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, - touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_90 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_90, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_270 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_270, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_180 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_180, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - // ROTATION_180 is not supported. It should be treated like ROTATION_0. - event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void fingerDown() { - runWithAllParams(this::fingerDownParameterized); - } - - private void fingerDownParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView, mFingerprintManager, mLatencyTracker, - mKeyguardUpdateMonitor); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1 /* pointerId */, touchData); - - initUdfpsController(testParams.sensorProps); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // WHEN ACTION_DOWN is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch provider is notified about onPointerDown. - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - - // AND display configuration begins - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - } else { - verify(mLatencyTracker, never()).onActionStart( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mUdfpsView, never()).configureDisplay(any()); - } - verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // AND onDisplayConfigured notifies FingerprintManager about onUiReady - mOnDisplayConfiguredCaptor.getValue().run(); - mBiometricExecutor.runAllReady(); - InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); - inOrder.verify(mFingerprintManager).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId)); - inOrder.verify(mLatencyTracker).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - } else { - verify(mFingerprintManager, never()).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt()); - verify(mLatencyTracker, never()).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - } - } - - @Test - public void aodInterrupt() { - runWithAllParams(this::aodInterruptParameterized); - } - - private void aodInterruptParameterized(TestParams testParams) throws RemoteException { - mUdfpsController.cancelAodSendFingerUpAction(); - reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor); - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // GIVEN that the overlay is showing and screen is on and fp is running - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - // WHEN fingerprint is requested because of AOD interrupt - mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN display configuration begins - // AND onDisplayConfigured notifies FingerprintManager about onUiReady - verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - mOnDisplayConfiguredCaptor.getValue().run(); - } else { - verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - } - mBiometricExecutor.runAllReady(); - - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - } - - @Test - public void tryAodSendFingerUp_displayConfigurationChanges() { - runWithAllParams(this::cancelAodInterruptParameterized); - } - - private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - // WHEN it is cancelled - mUdfpsController.tryAodSendFingerUp(); - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - // WHEN it is cancelled - mUdfpsController.tryAodSendFingerUp(); - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void onFingerUp_displayConfigurationChange() { - runWithAllParams(this::onFingerUp_displayConfigurationParameterized); - } - - private void onFingerUp_displayConfigurationParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // GIVEN AOD interrupt - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - - // WHEN up-action received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - mFgExecutor.runAllReady(); - - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // WHEN up-action received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - mFgExecutor.runAllReady(); - - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void onAcquiredGood_displayConfigurationChange() { - runWithAllParams(this::onAcquiredGood_displayConfigurationParameterized); - } - - private void onAcquiredGood_displayConfigurationParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - - // GIVEN overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - // WHEN ACQUIRED_GOOD received - mOverlayController.onAcquired(testParams.sensorProps.sensorId, - BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - // WHEN ACQUIRED_GOOD received - mOverlayController.onAcquired(testParams.sensorProps.sensorId, - BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptTimeout() { - runWithAllParams(this::aodInterruptTimeoutParameterized); - } - - private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - // WHEN it times out - mFgExecutor.advanceClockToNext(); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN the display is unconfigured. - verify(mUdfpsView).unconfigureDisplay(); - } else { - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptCancelTimeoutActionOnFingerUp() { - runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized); - } - - private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // GIVEN AOD interrupt - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - mFgExecutor.runAllReady(); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // Configure UdfpsView to accept the ACTION_UP event - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - - // WHEN ACTION_UP is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - mFgExecutor.runAllReady(); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // Configure UdfpsView to accept the finger up event - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - - // WHEN it times out - mFgExecutor.advanceClockToNext(); - mFgExecutor.runAllReady(); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN the display should be unconfigured once. If the timeout action is not - // cancelled, the display would be unconfigured twice which would cause two - // FP attempts. - verify(mUdfpsView).unconfigureDisplay(); - } else { - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptScreenOff() { - runWithAllParams(this::aodInterruptScreenOffParameterized); - } - - private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN screen off - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOff(); - mFgExecutor.runAllReady(); - - // WHEN aod interrupt is received - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - - // THEN display doesn't get configured because it's off - verify(mUdfpsView, never()).configureDisplay(any()); - } - - @Test - public void aodInterrupt_fingerprintNotRunning() { - runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized); - } - - private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - - // GIVEN showing overlay - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - - // WHEN aod interrupt is received when the fingerprint service isn't running - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - - // THEN display doesn't get configured because it's off - verify(mUdfpsView, never()).configureDisplay(any()); - } - - @Test - public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true); - - // WHEN ACTION_HOVER is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); - MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); - mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent); - enterEvent.recycle(); - - // THEN context click haptic is played - verify(mVibrator).performHapticFeedback( - any(), - eq(HapticFeedbackConstants.CONTEXT_CLICK) - ); - } - - @Test - public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN NO haptic played - verify(mVibrator, never()).performHapticFeedback(any(), anyInt()); - } - - @Test - public void fingerDown_falsingManagerInformed() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN falsing manager is informed of the touch - verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION); - } - - private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent( - InteractionEvent event1, InteractionEvent event2, boolean a11y) - throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - event1, 1 /* pointerId */, touchData); - final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch( - event2, 1 /* pointerId */, touchData); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - if (a11y) { - verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); - } else { - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - } - - return new Pair<>(processorResultDown, processorResultUp); - } - - @Test - public void onTouch_forwardToKeyguard() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - - // THEN the touch is forwarded to Keyguard - verify(mStatusBarKeyguardViewManager).onTouch(downEvent); - } - - @Test - public void onTouch_pilferPointer() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - - // WHEN ACTION_MOVE is received after - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - event.setAction(ACTION_MOVE); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - - // THEN only pilfer once on the initial down - verify(mInputManager).pilferPointers(any()); - } - - @Test - public void onTouch_doNotPilferPointer() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultUnchanged = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received and touch is not within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultUnchanged); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch is NOT pilfered - verify(mInputManager, never()).pilferPointers(any()); - } - - @Test - public void onDownTouchReceivedWithoutPreviousUp() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); - mBiometricExecutor.runAllReady(); - firstDownEvent.recycle(); - - // And another ACTION_DOWN is received without an ACTION_UP before - MotionEvent secondDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, secondDownEvent); - mBiometricExecutor.runAllReady(); - secondDownEvent.recycle(); - - // THEN the touch is still processed - verify(mFingerprintManager, times(2)).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onAodDownAndDownTouchReceived() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN fingerprint is requested because of AOD interrupt - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - mFgExecutor.runAllReady(); - - // and an ACTION_DOWN is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); - mBiometricExecutor.runAllReady(); - firstDownEvent.recycle(); - - // THEN the touch is only processed once - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onTouch_pilferPointerWhenAltBouncerShowing() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); - - // WHEN alternate bouncer is showing - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // WHEN ACTION_DOWN is received and touch is not within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch is pilfered - verify(mInputManager).pilferPointers(any()); - } - - @Test - public void onTouch_doNotProcessTouchWhenPullingUpBouncer() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); - - // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS - when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true); - when(mLockscreenShadeTransitionController.getFractionToShade()).thenReturn(1f); - - // WHEN ACTION_MOVE is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN the touch is NOT processed - verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - - @Test - public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - // GIVEN swipe down for QS - when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false); - when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f); - - // WHEN ACTION_MOVE is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN the touch is still processed - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException { - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - - // THEN finger is considered down - assertTrue(mUdfpsController.isFingerDown()); - - // WHEN udfps receives an ACQUIRED_GOOD after the display is configured - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - verify(mFingerprintManager).setUdfpsOverlayController( - mUdfpsOverlayControllerCaptor.capture()); - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - - // THEN is fingerDown should be FALSE - assertFalse(mUdfpsController.isFingerDown()); - } - - @Test - public void playHaptic_onAodInterrupt_onAcquiredBad_performHapticFeedback() - throws RemoteException { - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - - // THEN vibrate is used - verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); - } - - @Test - public void onAcquiredCalbacks() { - runWithAllParams( - this::ultrasonicCallbackOnAcquired); - } - - public void ultrasonicCallbackOnAcquired(TestParams testParams) throws RemoteException{ - if (testParams.sensorProps.sensorType - == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC) { - reset(mUdfpsView); - - UdfpsController.Callback callbackMock = mock(UdfpsController.Callback.class); - mUdfpsController.addCallback(callbackMock); - - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, - mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mFingerprintManager).setUdfpsOverlayController( - mUdfpsOverlayControllerCaptor.capture()); - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_START); - mFgExecutor.runAllReady(); - - verify(callbackMock).onFingerDown(); - - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - - verify(callbackMock).onFingerUp(); - } - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java deleted file mode 100644 index 7986051de3e0..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Flags; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityTransitionAnimator; -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; -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.flags.FakeFeatureFlags; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shade.ShadeExpansionChangeEvent; -import com.android.systemui.shade.ShadeExpansionStateManager; -import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.SystemUIDialogManager; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.user.domain.interactor.SelectedUserInteractor; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import org.junit.Before; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { - // Dependencies - protected @Mock UdfpsKeyguardViewLegacy mView; - protected @Mock Context mResourceContext; - protected @Mock StatusBarStateController mStatusBarStateController; - protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager; - protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController; - protected @Mock DumpManager mDumpManager; - protected @Mock DelayableExecutor mExecutor; - protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - protected @Mock KeyguardStateController mKeyguardStateController; - protected @Mock KeyguardViewMediator mKeyguardViewMediator; - protected @Mock ConfigurationController mConfigurationController; - protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; - protected @Mock SystemUIDialogManager mDialogManager; - protected @Mock UdfpsController mUdfpsController; - protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator; - protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; - protected @Mock ShadeInteractor mShadeInteractor; - protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor; - protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; - protected @Mock SelectedUserInteractor mSelectedUserInteractor; - protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor; - protected @Mock UdfpsOverlayInteractor mUdfpsOverlayInteractor; - - protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); - - protected UdfpsKeyguardViewControllerLegacy mController; - - // Capture listeners so that they can be used to send events - private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; - protected StatusBarStateController.StateListener mStatusBarStateListener; - - private @Captor ArgumentCaptor<KeyguardStateController.Callback> - mKeyguardStateControllerCallbackCaptor; - protected KeyguardStateController.Callback mKeyguardStateControllerCallback; - - private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.KeyguardViewManagerCallback> - mKeyguardViewManagerCallbackArgumentCaptor; - protected StatusBarKeyguardViewManager.KeyguardViewManagerCallback mKeyguardViewManagerCallback; - - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); - when(mView.getContext()).thenReturn(mResourceContext); - when(mResourceContext.getString(anyInt())).thenReturn("test string"); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false); - when(mView.getUnpausedAlpha()).thenReturn(255); - when(mShadeExpansionStateManager.addExpansionListener(any())).thenReturn( - new ShadeExpansionChangeEvent(0, false, false)); - mController = createUdfpsKeyguardViewController(); - } - - protected void sendStatusBarStateChanged(int statusBarState) { - mStatusBarStateListener.onStateChanged(statusBarState); - } - - protected void captureStatusBarStateListeners() { - verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture()); - mStatusBarStateListener = mStateListenerCaptor.getValue(); - } - - protected void captureKeyguardStateControllerCallback() { - verify(mKeyguardStateController).addCallback( - mKeyguardStateControllerCallbackCaptor.capture()); - mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue(); - } - - public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(false); - } - - public void captureKeyGuardViewManagerCallback() { - verify(mStatusBarKeyguardViewManager).addCallback( - mKeyguardViewManagerCallbackArgumentCaptor.capture()); - mKeyguardViewManagerCallback = mKeyguardViewManagerCallbackArgumentCaptor.getValue(); - } - - protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController( - boolean useModernBouncer) { - UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy( - mView, - mStatusBarStateController, - mStatusBarKeyguardViewManager, - mKeyguardUpdateMonitor, - mDumpManager, - mLockscreenShadeTransitionController, - mConfigurationController, - mKeyguardStateController, - mUnlockedScreenOffAnimationController, - mDialogManager, - mUdfpsController, - mActivityTransitionAnimator, - mPrimaryBouncerInteractor, - mAlternateBouncerInteractor, - mUdfpsKeyguardAccessibilityDelegate, - mSelectedUserInteractor, - mKeyguardTransitionInteractor, - mShadeInteractor, - mUdfpsOverlayInteractor); - return controller; - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java deleted file mode 100644 index 98d8b054716c..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.TestableLooper; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.systemui.statusbar.StatusBarState; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) - -@TestableLooper.RunWithLooper(setAsMainLooper = true) -public class UdfpsKeyguardViewLegacyControllerTest extends - UdfpsKeyguardViewLegacyControllerBaseTest { - @Override - public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(/* useModernBouncer */ false); - } - - @Test - public void testShouldPauseAuth_bouncerShowing() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); - when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true); - when(mView.getUnpausedAlpha()).thenReturn(0); - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testRegistersStatusBarStateListenersOnAttached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - } - - @Test - public void testViewControllerQueriesSBStateOnAttached() { - mController.onViewAttached(); - verify(mStatusBarStateController).getState(); - - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); - captureStatusBarStateListeners(); - - mController.onViewAttached(); - verify(mView, atLeast(1)).setPauseAuth(true); - } - - @Test - public void testListenersUnregisteredOnDetached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureKeyguardStateControllerCallback(); - mController.onViewDetached(); - - verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); - verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback); - } - - @Test - public void testShouldPauseAuthUnpausedAlpha0() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - when(mView.getUnpausedAlpha()).thenReturn(0); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldNotPauseAuthOnKeyguard() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void onBiometricAuthenticated_pauseAuth() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN biometric is authenticated - captureKeyguardStateControllerCallback(); - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardStateControllerCallback.onUnlockedChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthIsLaunchTransitionFadingAway() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN isLaunchTransitionFadingAway=true - captureKeyguardStateControllerCallback(); - when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true); - mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN not on keyguard yet (shade = home) - sendStatusBarStateChanged(StatusBarState.SHADE); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthAnimatingScreenOffFromShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN transitioning from home/shade => keyguard + animating screen off - mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testDoNotPauseAuthAnimatingScreenOffFromLS() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN animating screen off transition from LS => AOD - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN don't pause auth - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void testOverrideShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - assertTrue(mController.shouldPauseAuth()); - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt deleted file mode 100644 index 29a6e56891af..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN -import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor -import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository -import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor -import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor -import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.TransitionState -import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.kosmos.testScope -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mockito -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@RunWith(AndroidJUnit4::class) -@SmallTest -@TestableLooper.RunWithLooper -@kotlinx.coroutines.ExperimentalCoroutinesApi -class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : - UdfpsKeyguardViewLegacyControllerBaseTest() { - private val kosmos = testKosmos() - private val testScope = kosmos.testScope - - private val keyguardBouncerRepository = kosmos.fakeKeyguardBouncerRepository - private val transitionRepository = kosmos.fakeKeyguardTransitionRepository - - @Before - override fun setUp() { - allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread - MockitoAnnotations.initMocks(this) - super.setUp() - } - - override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy { - mPrimaryBouncerInteractor = kosmos.primaryBouncerInteractor - mAlternateBouncerInteractor = kosmos.alternateBouncerInteractor - mKeyguardTransitionInteractor = kosmos.keyguardTransitionInteractor - mUdfpsOverlayInteractor = kosmos.udfpsOverlayInteractor - return createUdfpsKeyguardViewController(/* useModernBouncer */ true) - } - - @Test - fun bouncerExpansionChange_fadeIn() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - captureKeyguardStateControllerCallback() - Mockito.reset(mView) - - // WHEN status bar expansion is 0 - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN alpha is 0 - verify(mView).unpausedAlpha = 0 - - job.cancel() - } - - @Test - fun bouncerExpansionChange_pauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - Mockito.reset(mView) - - // WHEN panelViewExpansion changes to hide - whenever(mView.unpausedAlpha).thenReturn(0) - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN pause auth is updated to PAUSE - verify(mView, Mockito.atLeastOnce()).setPauseAuth(true) - - job.cancel() - } - - @Test - fun bouncerExpansionChange_unpauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the keyguard + panel expansion is 0f - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - Mockito.reset(mView) - - // WHEN panelViewExpansion changes to expanded - whenever(mView.unpausedAlpha).thenReturn(255) - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - - // THEN pause auth is updated to NOT pause - verify(mView, Mockito.atLeastOnce()).setPauseAuth(false) - - job.cancel() - } - - @Test - fun shadeLocked_showAlternateBouncer_unpauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing) - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED) - - // WHEN alternate bouncer is requested - val job = mController.listenForAlternateBouncerVisibility(this) - keyguardBouncerRepository.setAlternateVisible(true) - runCurrent() - - // THEN udfps view will animate in & pause auth is updated to NOT pause - verify(mView).animateInUdfpsBouncer(any()) - assertFalse(mController.shouldPauseAuth()) - - job.cancel() - } - - /** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */ - @Test - fun shouldPauseAuthBouncerShowing() = - testScope.runTest { - // GIVEN view attached and we're on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - - // WHEN the bouncer expansion is VISIBLE - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN UDFPS shouldPauseAuth == true - assertTrue(mController.shouldPauseAuth()) - - job.cancel() - } - - @Test - fun shouldHandleTouchesChange() = - testScope.runTest { - val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches) - - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - whenever(mView.setPauseAuth(true)).thenReturn(true) - whenever(mView.unpausedAlpha).thenReturn(0) - - // WHEN panelViewExpansion changes to expanded - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN UDFPS auth is paused and should not handle touches - assertThat(mController.shouldPauseAuth()).isTrue() - assertThat(shouldHandleTouches!!).isFalse() - - job.cancel() - } - - @Test - fun shouldHandleTouchesOnDetach() = - testScope.runTest { - val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches) - - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - whenever(mView.setPauseAuth(true)).thenReturn(true) - whenever(mView.unpausedAlpha).thenReturn(0) - - // WHEN panelViewExpansion changes to expanded - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - mController.onViewDetached() - - // THEN UDFPS auth is paused and should not handle touches - assertThat(mController.shouldPauseAuth()).isTrue() - assertThat(shouldHandleTouches!!).isFalse() - - job.cancel() - } - - @Test - fun fadeFromDialogSuggestedAlpha() = - testScope.runTest { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached() - captureStatusBarStateListeners() - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - Mockito.reset(mView) - - // WHEN dialog suggested alpha is .6f - whenever(mView.dialogSuggestedAlpha).thenReturn(.6f) - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - - // THEN alpha is updated based on dialog suggested alpha - verify(mView).unpausedAlpha = (.6f * 255).toInt() - - job.cancel() - } - - @Test - fun transitionToFullShadeProgress() = - testScope.runTest { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached() - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - Mockito.reset(mView) - whenever(mView.dialogSuggestedAlpha).thenReturn(1f) - - // WHEN we're transitioning to the full shade - val transitionProgress = .6f - mController.setTransitionToFullShadeProgress(transitionProgress) - - // THEN alpha is between 0 and 255 - verify(mView).unpausedAlpha = ((1f - transitionProgress) * 255).toInt() - - job.cancel() - } - - @Test - fun aodToLockscreen_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForLockscreenAodTransitions(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - job.cancel() - } - - @Test - fun lockscreenToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForLockscreenAodTransitions(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - job.cancel() - } - - @Test - fun goneToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForGoneToAodTransition(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - job.cancel() - } - - @Test - fun aodToOccluded_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForAodToOccludedTransitions(this) - - // WHEN transitioning from aod to occluded - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.OCCLUDED, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged(eq(.7f), eq(.7f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.OCCLUDED, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) - - job.cancel() - } - - @Test - fun occludedToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForOccludedToAodTransition(this) - - // WHEN transitioning from occluded to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.OCCLUDED, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.OCCLUDED, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - job.cancel() - } - - @Test - fun cancelledAodToLockscreen_dozeAmountChangedToZero() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - val job = mController.listenForLockscreenAodTransitions(this) - runCurrent() - Mockito.reset(mView) - - // WHEN aod to lockscreen transition is cancelled - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, - value = 1f, - transitionState = TransitionState.CANCELED - ) - ) - runCurrent() - // ... and WHEN the next transition is from lockscreen => occluded - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - value = .4f, - transitionState = TransitionState.STARTED - ) - ) - runCurrent() - - // THEN doze amount is updated to zero - verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) - job.cancel() - } - - @Test - fun cancelledLockscreenToAod_dozeAmountNotUpdatedToZero() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - val job = mController.listenForLockscreenAodTransitions(this) - runCurrent() - Mockito.reset(mView) - - // WHEN lockscreen to aod transition is cancelled - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.CANCELED - ) - ) - runCurrent() - - // THEN doze amount is NOT updated to zero - verify(mView, never()).onDozeAmountChanged(eq(0f), eq(0f), anyInt()) - job.cancel() - } - - @Test - fun dreamingToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForDreamingToAodTransitions(this) - // WHEN dreaming to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.DREAMING, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF)) - job.cancel() - } - - @Test - fun alternateBouncerToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForAlternateBouncerToAodTransitions(this) - // WHEN alternate bouncer to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView) - .onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) - job.cancel() - } - - @Test - fun bouncerToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForPrimaryBouncerToAodTransitions(this) - // WHEN alternate bouncer to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF)) - job.cancel() - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index 68cfa28dabd7..82ff61795e98 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -16,12 +16,9 @@ package com.android.systemui.bouncer.domain.interactor -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.keyguardUpdateMonitor -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.authenticationInteractor @@ -61,12 +58,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { underTest = kosmos.alternateBouncerInteractor } - @Test(expected = IllegalStateException::class) - @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() { - underTest.show() - } - @Test @DisableSceneContainer fun canShowAlternateBouncer_false_dueToTransitionState() = @@ -101,21 +92,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun canShowAlternateBouncerForFingerprint_givenCanShow() { - givenCanShowAlternateBouncer() - assertTrue(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test - fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() { - givenCanShowAlternateBouncer() - kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false) - - assertFalse(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() { givenCanShowAlternateBouncer() kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) @@ -140,15 +116,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun show_whenCanShow() { - givenCanShowAlternateBouncer() - - assertTrue(underTest.show()) - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value) - } - - @Test fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() { givenCanShowAlternateBouncer() whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true) @@ -165,15 +132,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun show_whenCannotShow() { - givenCannotShowAlternateBouncer() - - assertFalse(underTest.show()) - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value) - } - - @Test fun hide_wasPreviouslyShowing() { kosmos.keyguardBouncerRepository.setAlternateVisible(true) @@ -190,7 +148,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun canShowAlternateBouncerForFingerprint_rearFps() { givenCanShowAlternateBouncer() kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer @@ -198,29 +155,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { assertFalse(underTest.canShowAlternateBouncerForFingerprint()) } - @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun alternateBouncerUiAvailable_fromMultipleSources() { - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // GIVEN there are two different sources indicating the alternate bouncer is available - underTest.setAlternateBouncerUIAvailable(true, "source1") - underTest.setAlternateBouncerUIAvailable(true, "source2") - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // WHEN one of the sources no longer says the UI is available - underTest.setAlternateBouncerUIAvailable(false, "source1") - - // THEN alternate bouncer UI is still available (from the other source) - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // WHEN all sources say the UI is not available - underTest.setAlternateBouncerUIAvailable(false, "source2") - - // THEN alternate boucer UI is not available - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - } - private fun givenAlternateBouncerSupported() { kosmos.givenAlternateBouncerSupported() } @@ -228,8 +162,4 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { private fun givenCanShowAlternateBouncer() { kosmos.givenCanShowAlternateBouncer() } - - private fun givenCannotShowAlternateBouncer() { - kosmos.givenCannotShowAlternateBouncer() - } } 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 ee65fbd810ae..1e8651683ec1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal +import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings @@ -77,7 +78,11 @@ class CommunalSceneStartableTest : SysuiTestCase() { @Before fun setUp() { with(kosmos) { - fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT) + fakeSettings.putIntForUser( + Settings.System.SCREEN_OFF_TIMEOUT, + SCREEN_TIMEOUT, + UserHandle.USER_CURRENT, + ) kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) underTest = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java index fad52e090c69..ba43a236b8f9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java @@ -38,9 +38,9 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt index 2a2a82d53671..b5ea305544ff 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt @@ -39,7 +39,6 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.kosmos.testScope -import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes @@ -100,18 +99,14 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, - faceAuthInteractor + faceAuthInteractor, ) alternateBouncerInteractor = AlternateBouncerInteractor( - mock(StatusBarStateController::class.java), - mock(KeyguardStateController::class.java), bouncerRepository, FakeFingerprintPropertyRepository(), - biometricSettingsRepository, FakeSystemClock(), - keyguardUpdateMonitor, { mock(DeviceEntryBiometricsAllowedInteractor::class.java) }, { mock(KeyguardInteractor::class.java) }, { mock(KeyguardTransitionInteractor::class.java) }, @@ -121,13 +116,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) } @@ -142,7 +136,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isTrue() } @@ -158,7 +152,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = false, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -169,13 +163,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { testScope.runTest { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) val showIndicatorForDeviceEntry by @@ -185,7 +178,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isTrue() } @@ -196,13 +189,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { testScope.runTest { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) val showIndicatorForDeviceEntry by @@ -212,7 +204,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -228,7 +220,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = false, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -245,7 +237,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = false + isUnlockingWithFpAllowed = false, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -261,7 +253,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = false, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -277,7 +269,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = false + isUnlockingWithFpAllowed = false, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -294,7 +286,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -325,7 +317,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) // Another request to show indicator for deviceEntryFingerprintAuthRepository update @@ -355,7 +347,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, - true + true, ) } @@ -366,7 +358,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { ) { kosmos.sceneInteractor.changeScene( if (isActive) Scenes.Bouncer else Scenes.Lockscreen, - "reason" + "reason", ) whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) @@ -375,7 +367,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, - true + true, ) runCurrent() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt index c4eabd84e031..380060865282 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.ui.binder -import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper import android.view.View import android.view.layoutInflater @@ -24,7 +23,6 @@ import android.view.mockedLayoutInflater import android.view.windowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -63,7 +61,7 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { kosmos.mockedLayoutInflater.inflate( eq(R.layout.alternate_bouncer), isNull(), - anyBoolean() + anyBoolean(), ) ) .thenReturn(mockedAltBouncerView) @@ -71,7 +69,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun addViewToWindowManager() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() @@ -85,7 +82,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun viewRemovedImmediatelyIfAlreadyAttachedToWindow() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() @@ -107,7 +103,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun viewNotRemovedUntilAttachedToWindow() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt index c1bd37811787..5d9548057bae 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository import com.android.systemui.coroutines.collectLastValue @@ -34,7 +33,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @ExperimentalCoroutinesApi @RunWith(AndroidJUnit4::class) @@ -49,7 +47,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsUdfps() @@ -64,28 +61,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { assertThat(alternateBouncerWindowRequired).isTrue() transitionRepository.sendTransitionSteps( - listOf( - stepFromAlternateBouncer(1.0f, TransitionState.FINISHED), - ), - testScope, - ) - assertThat(alternateBouncerWindowRequired).isFalse() - } - - @Test - fun deviceEntryUdfpsFlagDisabled_alternateBouncerWindowRequiredFalse() = - testScope.runTest { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - val alternateBouncerWindowRequired by - collectLastValue(underTest.alternateBouncerWindowRequired) - fingerprintPropertyRepository.supportsUdfps() - transitionRepository.sendTransitionSteps( - listOf( - stepFromAlternateBouncer(0f, TransitionState.STARTED), - stepFromAlternateBouncer(.4f), - stepFromAlternateBouncer(.6f), - stepFromAlternateBouncer(1f), - ), + listOf(stepFromAlternateBouncer(1.0f, TransitionState.FINISHED)), testScope, ) assertThat(alternateBouncerWindowRequired).isFalse() @@ -94,7 +70,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun lockscreenTransition_alternateBouncerWindowRequiredFalse() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsUdfps() @@ -113,7 +88,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun rearFps_alternateBouncerWindowRequiredFalse() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsRearFps() @@ -131,7 +105,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { private fun stepFromAlternateBouncer( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.ALTERNATE_BOUNCER, @@ -143,7 +117,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { private fun stepFromDozingToLockscreen( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.DOZING, @@ -157,14 +131,14 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { from: KeyguardState, to: KeyguardState, value: Float, - transitionState: TransitionState + transitionState: TransitionState, ): TransitionStep { return TransitionStep( from = from, to = to, value = value, transitionState = transitionState, - ownerName = "AlternateBouncerViewModelTest" + ownerName = "AlternateBouncerViewModelTest", ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java index 7798f46fdb46..b23262a87e2e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java @@ -34,9 +34,9 @@ import android.graphics.drawable.Drawable; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.service.quicksettings.Tile; -import android.testing.UiThreadTest; import android.widget.ImageView; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt new file mode 100644 index 000000000000..cdf6bda91301 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles.impl.hearingdevices.domain + +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class HearingDevicesTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val qsTileConfig = kosmos.qsHearingDevicesTileConfig + private val mapper by lazy { + HearingDevicesTileMapper( + context.orCreateTestableResources + .apply { addOverride(R.drawable.qs_hearing_devices_icon, TestStubDrawable()) } + .resources, + context.theme, + ) + } + + @Test + fun map_anyActiveHearingDevice_anyPairedHearingDevice_activeState() { + val tileState: QSTileState = + mapper.map( + qsTileConfig, + HearingDevicesTileModel( + isAnyActiveHearingDevice = true, + isAnyPairedHearingDevice = true, + ), + ) + val expectedState = + createHearingDevicesTileState( + QSTileState.ActivationState.ACTIVE, + context.getString(R.string.quick_settings_hearing_devices_connected), + ) + QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState) + } + + @Test + fun map_noActiveHearingDevice_anyPairedHearingDevice_inactiveState() { + val tileState: QSTileState = + mapper.map( + qsTileConfig, + HearingDevicesTileModel( + isAnyActiveHearingDevice = false, + isAnyPairedHearingDevice = true, + ), + ) + val expectedState = + createHearingDevicesTileState( + QSTileState.ActivationState.INACTIVE, + context.getString(R.string.quick_settings_hearing_devices_disconnected), + ) + QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState) + } + + @Test + fun map_noActiveHearingDevice_noPairedHearingDevice_inactiveState() { + val tileState: QSTileState = + mapper.map( + qsTileConfig, + HearingDevicesTileModel( + isAnyActiveHearingDevice = false, + isAnyPairedHearingDevice = false, + ), + ) + val expectedState = + createHearingDevicesTileState(QSTileState.ActivationState.INACTIVE, secondaryLabel = "") + QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState) + } + + private fun createHearingDevicesTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String, + ): QSTileState { + val label = context.getString(R.string.quick_settings_hearing_devices_label) + val iconRes = R.drawable.qs_hearing_devices_icon + return QSTileState( + { Icon.Loaded(context.getDrawable(iconRes)!!, null) }, + iconRes, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + label, + null, + QSTileState.SideViewIcon.Chevron, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName, + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt new file mode 100644 index 000000000000..1dfa2cd26491 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.os.UserHandle +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.annotations.EnabledOnRavenwood +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.statusbar.policy.fakeBluetoothController +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@EnabledOnRavenwood +@RunWith(AndroidJUnit4::class) +class HearingDevicesTileDataInteractorTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val testUser = UserHandle.of(1) + + private val controller = kosmos.fakeBluetoothController + private lateinit var underTest: HearingDevicesTileDataInteractor + + @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var checker: HearingDevicesChecker + + @Before + fun setup() { + underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker) + } + + @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG) + @Test + fun availability_flagEnabled_returnTrue() = + testScope.runTest { + val availability by collectLastValue(underTest.availability(testUser)) + + assertThat(availability).isTrue() + } + + @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG) + @Test + fun availability_flagDisabled_returnFalse() = + testScope.runTest { + val availability by collectLastValue(underTest.availability(testUser)) + + assertThat(availability).isFalse() + } + + @Test + fun tileData_bluetoothStateChanged_dataMatchesChecker() = + testScope.runTest { + val flowValues: List<HearingDevicesTileModel> by + collectValues( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup() + + whenever(checker.isAnyPairedHearingDevice).thenReturn(false) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.isBluetoothEnabled = false + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.isBluetoothEnabled = true + runCurrent() + assertThat(flowValues.size).isEqualTo(2) + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(true) + controller.isBluetoothEnabled = true + runCurrent() + assertThat(flowValues.size).isEqualTo(3) + + assertThat(flowValues.map { it.isAnyPairedHearingDevice }) + .containsExactly(false, true, true) + .inOrder() + assertThat(flowValues.map { it.isAnyActiveHearingDevice }) + .containsExactly(false, false, true) + .inOrder() + } + + @Test + fun tileData_bluetoothDeviceChanged_dataMatchesChecker() = + testScope.runTest { + val flowValues: List<HearingDevicesTileModel> by + collectValues( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup() + + whenever(checker.isAnyPairedHearingDevice).thenReturn(false) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.onBluetoothDevicesChanged() + runCurrent() + assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(false) + controller.onBluetoothDevicesChanged() + runCurrent() + assertThat(flowValues.size).isEqualTo(2) + + whenever(checker.isAnyPairedHearingDevice).thenReturn(true) + whenever(checker.isAnyActiveHearingDevice).thenReturn(true) + controller.onBluetoothDevicesChanged() + runCurrent() + assertThat(flowValues.size).isEqualTo(3) + + assertThat(flowValues.map { it.isAnyPairedHearingDevice }) + .containsExactly(false, true, true) + .inOrder() + assertThat(flowValues.map { it.isAnyActiveHearingDevice }) + .containsExactly(false, false, true) + .inOrder() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..00ee1c36590c --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.platform.test.annotations.EnabledOnRavenwood +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager +import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.verify + +@SmallTest +@EnabledOnRavenwood +@RunWith(AndroidJUnit4::class) +class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val inputHandler = FakeQSTileIntentUserInputHandler() + + private lateinit var underTest: HearingDevicesTileUserActionInteractor + + @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var dialogManager: HearingDevicesDialogManager + + @Before + fun setUp() { + underTest = + HearingDevicesTileUserActionInteractor( + testScope.coroutineContext, + inputHandler, + dialogManager, + ) + } + + @Test + fun handleClick_launchDialog() = + testScope.runTest { + val input = + HearingDevicesTileModel( + isAnyActiveHearingDevice = true, + isAnyPairedHearingDevice = true, + ) + + underTest.handleInput(QSTileInputTestKtx.click(input)) + + verify(dialogManager).showDialog(anyOrNull(), eq(LAUNCH_SOURCE_QS_TILE)) + } + + @Test + fun handleLongClick_launchSettings() = + testScope.runTest { + val input = + HearingDevicesTileModel( + isAnyActiveHearingDevice = true, + isAnyPairedHearingDevice = true, + ) + + underTest.handleInput(QSTileInputTestKtx.longClick(input)) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 6f2302a22d7b..9fe52991c3a0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -30,7 +30,6 @@ import android.view.ViewGroup import android.view.ViewTreeObserver import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityContainerController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.Flags import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT @@ -54,7 +53,6 @@ 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 import com.android.systemui.res.R -import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.statusbar.DragDownHelper @@ -71,7 +69,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider @@ -80,6 +77,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow @@ -98,11 +96,10 @@ 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.MockitoAnnotations import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters -import java.util.Optional -import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -125,12 +122,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController - @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var quickSettingsController: QuickSettingsControllerImpl @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock private lateinit var lockIconViewController: LegacyLockIconViewController @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController @Mock private lateinit var pulsingGestureListener: PulsingGestureListener @Mock @@ -144,7 +139,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @Mock private lateinit var unfoldTransitionProgressProvider: - Optional<UnfoldTransitionProgressProvider> + Optional<UnfoldTransitionProgressProvider> @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var dragDownHelper: DragDownHelper @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor @@ -176,20 +171,17 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : MockitoAnnotations.initMocks(this) whenever(view.bottom).thenReturn(VIEW_BOTTOM) whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container)) - .thenReturn(mock(ViewGroup::class.java)) + .thenReturn(mock(ViewGroup::class.java)) whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java))) - .thenReturn(keyguardBouncerComponent) + .thenReturn(keyguardBouncerComponent) whenever(keyguardBouncerComponent.securityContainerController) - .thenReturn(keyguardSecurityContainerController) + .thenReturn(keyguardSecurityContainerController) whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING))) - .thenReturn(emptyFlow<TransitionStep>()) + .thenReturn(emptyFlow<TransitionStep>()) featureFlagsClassic = FakeFeatureFlagsClassic() featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - if (!SceneContainerFlag.isEnabled) { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES) testScope = TestScope() @@ -208,9 +200,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : panelExpansionInteractor, ShadeExpansionStateManager(), stackScrollLayoutController, - statusBarKeyguardViewManager, statusBarWindowStateController, - lockIconViewController, centralSurfaces, dozeServiceHost, dozeScrimController, @@ -233,7 +223,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : quickSettingsController, primaryBouncerInteractor, alternateBouncerInteractor, - mock(BouncerViewBinder::class.java) + mock(BouncerViewBinder::class.java), ) underTest.setupExpandedStatusBar() underTest.setDragDownHelper(dragDownHelper) @@ -294,7 +284,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true) val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -309,7 +299,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Item we're testing whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(false) @@ -327,7 +317,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) // Item we're testing whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(false) + .thenReturn(false) val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -341,7 +331,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Item we're testing whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false) @@ -358,7 +348,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Down event first interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -379,7 +369,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : // GIVEN touch dispatcher in a state that returns true underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) - .thenReturn(true) + .thenReturn(true) assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue() // WHEN launch animation is running for 2 seconds @@ -432,47 +422,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : } @Test - fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() { - // down event should be intercepted by keyguardViewManager - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(true) - - // Then touch should not be intercepted - val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT) - assertThat(shouldIntercept).isTrue() - } - - @Test - @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) - fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() { - // GIVEN dozing - whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND quick settings controller doesn't want it - whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(false) - // AND the lock icon wants the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true) - - // THEN touch should NOT be intercepted by NotificationShade - assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse() - } - - @Test @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() { // GIVEN dozing whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller doesn't want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(false) + .thenReturn(false) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -483,14 +439,9 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() { // GIVEN dozing whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller DOES want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(true) + .thenReturn(true) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -503,20 +454,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) // AND pulsing whenever(dozeServiceHost.isPulsing()).thenReturn(true) - // AND status bar doesn't want it - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND shade is not fully expanded (mock is false by default) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller DOES want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(true) + .thenReturn(true) // AND bouncer is not showing whenever(centralSurfaces.isBouncerShowing()).thenReturn(false) // AND panel view controller wants it - whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)) - .thenReturn(true) + whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)).thenReturn(true) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -589,12 +533,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setupCommunalHubLayout() // Simluate attaching the view so flow collection starts. - val onAttachStateChangeListenerArgumentCaptor = ArgumentCaptor.forClass( - View.OnAttachStateChangeListener::class.java - ) - verify(view, atLeast(1)).addOnAttachStateChangeListener( - onAttachStateChangeListenerArgumentCaptor.capture() - ) + val onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java) + verify(view, atLeast(1)) + .addOnAttachStateChangeListener(onAttachStateChangeListenerArgumentCaptor.capture()) for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) { listener.onViewAttachedToWindow(view) } @@ -608,7 +550,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @RequiresFlagsDisabled(Flags.FLAG_COMMUNAL_HUB) fun doesNotSetupCommunalHubLayout_whenFlagDisabled() { whenever(mGlanceableHubContainerController.communalAvailable()) - .thenReturn(MutableStateFlow(false)) + .thenReturn(MutableStateFlow(false)) val mockCommunalPlaceholder = mock(View::class.java) val fakeViewIndex = 20 diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index ca29dd98a637..9093b2bcbbf8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -23,7 +23,6 @@ import android.widget.FrameLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityContainerController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase @@ -57,7 +56,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider @@ -104,11 +102,9 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController - @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock private lateinit var lockIconViewController: LegacyLockIconViewController @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var shadeLogger: ShadeLogger @@ -161,7 +157,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { val featureFlags = FakeFeatureFlags() featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES) testScope = TestScope() controller = @@ -176,9 +171,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { panelExpansionInteractor, ShadeExpansionStateManager(), notificationStackScrollLayoutController, - statusBarKeyguardViewManager, statusBarWindowStateController, - lockIconViewController, centralSurfaces, dozeServiceHost, dozeScrimController, @@ -221,48 +214,18 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { } @Test - fun testInterceptTouchWhenShowingAltAuth() = - testScope.runTest { - captureInteractionEventHandler() - - // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept - whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true) - whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) - - // THEN we should intercept touch - assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue() - } - - @Test fun testNoInterceptTouch() = testScope.runTest { captureInteractionEventHandler() - // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept + // WHEN not dozing, drag down helper doesn't want to intercept whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())) - .thenReturn(false) whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) // THEN we shouldn't intercept touch assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse() } - @Test - fun testHandleTouchEventWhenShowingAltAuth() = - testScope.runTest { - captureInteractionEventHandler() - - // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept - whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true) - whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) - - // THEN we should handle the touch - assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue() - } - private fun captureInteractionEventHandler() { verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture()) interactionEventHandler = interactionEventHandlerCaptor.value diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt index 118dea6376bb..69a76271f726 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.packageManager import android.graphics.drawable.BitmapDrawable import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.commandline.CommandRegistry @@ -40,13 +40,13 @@ import org.mockito.kotlin.any import org.mockito.kotlin.whenever @SmallTest -class DemoRonChipViewModelTest : SysuiTestCase() { +class DemoNotifChipViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val commandRegistry = kosmos.commandRegistry private val pw = PrintWriter(StringWriter()) - private val underTest = kosmos.demoRonChipViewModel + private val underTest = kosmos.demoNotifChipViewModel @Before fun setUp() { @@ -56,61 +56,61 @@ class DemoRonChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_flagOff_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - addDemoRonChip() + addDemoNotifChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_noPackage_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-ron")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasPackage_shown() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasText_shownWithText() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test") + arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"), ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_supportsColor() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343") + arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"), ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) @@ -119,28 +119,28 @@ class DemoRonChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasHideArg_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) // First, show a chip - addDemoRonChip() + addDemoNotifChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) // Then, hide the chip - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } - private fun addDemoRonChip() { - Companion.addDemoRonChip(commandRegistry, pw) + private fun addDemoNotifChip() { + addDemoNotifChip(commandRegistry, pw) } companion object { - fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) { - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) { + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt new file mode 100644 index 000000000000..eb5d9318c88f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.ui.viewmodel + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +@EnableFlags(StatusBarNotifChips.FLAG_NAME) +class NotifChipsViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val activeNotificationListRepository = kosmos.activeNotificationListRepository + + private val underTest = kosmos.notifChipsViewModel + + @Test + fun chips_noNotifs_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + setNotifs(emptyList()) + + assertThat(latest).isEmpty() + } + + @Test + fun chips_notifMissingStatusBarChipIconView_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null))) + + assertThat(latest).isEmpty() + } + + @Test + fun chips_oneNotif_statusBarIconViewMatches() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val icon = mock<StatusBarIconView>() + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon))) + + assertThat(latest).hasSize(1) + val chip = latest!![0] + assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java) + assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon)) + } + + @Test + fun chips_twoNotifs_twoChips() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon), + ) + ) + + assertThat(latest).hasSize(2) + assertIsNotifChip(latest!![0], firstIcon) + assertIsNotifChip(latest!![1], secondIcon) + } + + private fun setNotifs(notifs: List<ActiveNotificationModel>) { + activeNotificationListRepository.activeNotifications.value = + ActiveNotificationsStore.Builder() + .apply { notifs.forEach { addIndividualNotif(it) } } + .build() + testScope.runCurrent() + } + + companion object { + fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) { + assertThat(latest) + .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java) + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon)) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 26ce7b956fde..e96def6d43a3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -25,7 +25,6 @@ import android.platform.test.annotations.DisableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue @@ -40,7 +39,8 @@ import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.phone.SystemUIDialog @@ -66,13 +66,11 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -/** - * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled. - */ +/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) +@DisableFlags(StatusBarNotifChips.FLAG_NAME) class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val kosmos = Kosmos().also { it.testCase = this } private val testScope = kosmos.testScope @@ -99,11 +97,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoRonChipViewModel.start() + kosmos.demoNotifChipViewModel.start() val icon = BitmapDrawable( context.resources, - Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888), ) whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } @@ -325,7 +323,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { latest: OngoingActivityChipModel?, chipView: View, dialog: SystemUIDialog, - kosmos: Kosmos + kosmos: Kosmos, ): DialogInterface.OnClickListener { // Capture the action that would get invoked when the user clicks "Stop" on the dialog lateinit var dialogStopAction: DialogInterface.OnClickListener diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt index 631120b39805..b12d7c57e1fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt @@ -24,7 +24,6 @@ import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope @@ -34,10 +33,13 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -46,6 +48,10 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsVie import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog import com.android.systemui.statusbar.commandline.commandRegistry +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel @@ -67,14 +73,12 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -/** - * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled. - */ +/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) -class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { +@EnableFlags(StatusBarNotifChips.FLAG_NAME) +class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock @@ -83,6 +87,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { private val screenRecordState = kosmos.screenRecordRepository.screenRecordState private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState private val callRepo = kosmos.ongoingCallRepository + private val activeNotificationListRepository = kosmos.activeNotificationListRepository private val pw = PrintWriter(StringWriter()) @@ -103,16 +108,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoRonChipViewModel.start() + kosmos.demoNotifChipViewModel.start() val icon = BitmapDrawable( context.resources, - Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888), ) whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } - // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the + // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the // flow has the right behavior to verify that we don't break any existing functionality. @Test @@ -249,13 +254,13 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) val latest by collectLastValue(underTest.chips) assertIsScreenRecordChip(latest!!.primary) assertIsCallChip(latest!!.secondary) - // Demo RON chip is dropped + // Demo notif chip is dropped } @Test @@ -288,10 +293,101 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { } @Test + fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val icon = mock<StatusBarIconView>() + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon))) + + assertIsNotifChip(latest!!.primary, icon) + assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + } + + @Test + fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon), + ) + ) + + assertIsNotifChip(latest!!.primary, firstIcon) + assertIsNotifChip(latest!!.secondary, secondIcon) + } + + @Test + fun chips_threeNotifChips_topTwoShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + val thirdIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon), + activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon), + ) + ) + + assertIsNotifChip(latest!!.primary, firstIcon) + assertIsNotifChip(latest!!.secondary, secondIcon) + } + + @Test + fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + val firstIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel( + key = "secondNotif", + statusBarChipIcon = mock<StatusBarIconView>(), + ), + ) + ) + + assertIsCallChip(latest!!.primary) + assertIsNotifChip(latest!!.secondary, firstIcon) + } + + @Test + fun chips_screenRecordAndCallAndNotifs_notifsNotShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + screenRecordState.value = ScreenRecordModel.Recording + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock<StatusBarIconView>(), + ) + ) + ) + + assertIsScreenRecordChip(latest!!.primary) + assertIsCallChip(latest!!.secondary) + } + + @Test fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -299,7 +395,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.primaryChip) - assertIsDemoRonChip(latest) + assertIsDemoNotifChip(latest) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) @@ -333,7 +429,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) val latest by collectLastValue(underTest.primaryChip) @@ -355,15 +451,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { // WHEN the higher priority call is removed callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN the lower priority demo RON is used - assertIsDemoRonChip(latest) + // THEN the lower priority demo notif is used + assertIsDemoNotifChip(latest) } @Test fun chips_movesChipsAroundAccordingToPriority() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -371,16 +467,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chips) - assertIsDemoRonChip(latest!!.primary) + assertIsDemoNotifChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - // THEN the higher priority call chip is used as primary and demo ron is demoted to + // THEN the higher priority call chip is used as primary and demo notif is demoted to // secondary assertIsCallChip(latest!!.primary) - assertIsDemoRonChip(latest!!.secondary) + assertIsDemoNotifChip(latest!!.secondary) // WHEN the higher priority media projection chip is added mediaProjectionState.value = @@ -391,7 +487,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { ) // THEN the higher priority media projection chip is used as primary and call is demoted - // to secondary (and demo RON is dropped altogether) + // to secondary (and demo notif is dropped altogether) assertIsShareToAppChip(latest!!.primary) assertIsCallChip(latest!!.secondary) @@ -405,15 +501,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN media projection and demo RON remain + // THEN media projection and demo notif remain assertIsShareToAppChip(latest!!.primary) - assertIsDemoRonChip(latest!!.secondary) + assertIsDemoNotifChip(latest!!.secondary) // WHEN media projection is dropped mediaProjectionState.value = MediaProjectionState.NotProjecting - // THEN demo RON is promoted to primary - assertIsDemoRonChip(latest!!.primary) + // THEN demo notif is promoted to primary + assertIsDemoNotifChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @@ -526,9 +622,17 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false)) } - private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) { + private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).icon) .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java) } + + private fun setNotifs(notifs: List<ActiveNotificationModel>) { + activeNotificationListRepository.activeNotifications.value = + ActiveNotificationsStore.Builder() + .apply { notifs.forEach { addIndividualNotif(it) } } + .build() + testScope.runCurrent() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt new file mode 100644 index 000000000000..8dcc44463213 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt @@ -0,0 +1,136 @@ +/* + * 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.core + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.google.common.truth.Expect +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.verify + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) +class MultiDisplayStatusBarStarterTest : SysuiTestCase() { + @get:Rule val expect: Expect = Expect.create() + + private val kosmos = + testKosmos().also { + it.statusBarOrchestratorFactory = it.fakeStatusBarOrchestratorFactory + it.statusBarInitializerStore = it.fakeStatusBarInitializerStore + } + private val testScope = kosmos.testScope + private val fakeDisplayRepository = kosmos.displayRepository + private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory + private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore + + // Lazy, so that @EnableFlags is set before initializer is instantiated. + private val underTest by lazy { kosmos.multiDisplayStatusBarStarter } + + @Test + fun start_startsInitializersForCurrentDisplays() = + testScope.runTest { + fakeDisplayRepository.addDisplay(displayId = 1) + fakeDisplayRepository.addDisplay(displayId = 2) + + underTest.start() + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 1).startedByCoreStartable) + .isTrue() + expect + .that(fakeInitializerStore.forDisplay(displayId = 2).startedByCoreStartable) + .isTrue() + } + + @Test + fun start_startsOrchestratorForCurrentDisplays() = + testScope.runTest { + fakeDisplayRepository.addDisplay(displayId = 1) + fakeDisplayRepository.addDisplay(displayId = 2) + + underTest.start() + runCurrent() + + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 1)!!).start() + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 2)!!).start() + } + + @Test + fun displayAdded_orchestratorForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + runCurrent() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start() + } + + @Test + fun displayAdded_initializerForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + runCurrent() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .isTrue() + } + + @Test + fun displayAddedDuringStart_initializerForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .isTrue() + } + + @Test + fun displayAddedDuringStart_orchestratorForNewDisplayIsStarted() = + testScope.runTest { + underTest.start() + + fakeDisplayRepository.addDisplay(displayId = 3) + runCurrent() + + expect + .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable) + .isTrue() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt index f64387c95e3d..c737bf7a8bec 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt @@ -60,10 +60,9 @@ class StatusBarInitializerTest : SysuiTestCase() { val underTest = StatusBarInitializerImpl( - displayId = context.displayId, - statusBarWindowControllerStore = windowControllerStore, collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) }, creationListeners = setOf(), + statusBarWindowController = windowController, ) @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt index bb3fb1e71f78..ab8e878ab309 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt @@ -38,13 +38,10 @@ import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT -import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository -import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions -import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController +import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository import com.android.systemui.statusbar.window.data.model.StatusBarWindowState -import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore +import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository +import com.android.systemui.statusbar.window.fakeStatusBarWindowController import com.android.systemui.testKosmos import com.android.wm.shell.bubbles.bubbles import com.google.common.truth.Truth.assertThat @@ -60,25 +57,20 @@ import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class StatusBarOrchestratorTest : SysuiTestCase() { - private val kosmos = - testKosmos().also { - it.testDispatcher = it.unconfinedTestDispatcher - it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore - } + private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher } private val testScope = kosmos.testScope - private val statusBarViewController = kosmos.mockPhoneStatusBarViewController - private val statusBarWindowControllerStore = kosmos.fakeStatusBarWindowControllerStore - private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository - private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider - private val notificationShadeWindowViewController = + private val fakeStatusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository + private val mockPluginDependencyProvider = kosmos.mockPluginDependencyProvider + private val mockNotificationShadeWindowViewController = kosmos.mockNotificationShadeWindowViewController - private val shadeSurface = kosmos.mockShadeSurface - private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository - private val fakeStatusBarWindowStateRepositoryStore = - kosmos.fakeStatusBarWindowStateRepositoryStore + private val mockShadeSurface = kosmos.mockShadeSurface + private val fakeBouncerRepository = kosmos.fakeKeyguardBouncerRepository + private val fakeStatusBarWindowStatePerDisplayRepository = + kosmos.fakeStatusBarWindowStatePerDisplayRepository private val fakePowerRepository = kosmos.fakePowerRepository - private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions private val mockBubbles = kosmos.bubbles + private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController + private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer private val orchestrator = kosmos.statusBarOrchestrator @@ -86,30 +78,31 @@ class StatusBarOrchestratorTest : SysuiTestCase() { fun start_setsUpPluginDependencies() { orchestrator.start() - verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java) - verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java) + verify(mockPluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java) + verify(mockPluginDependencyProvider) + .allowPluginDependency(StatusBarStateController::class.java) } @Test fun start_attachesWindow() { orchestrator.start() - assertThat(statusBarWindowControllerStore.defaultDisplay.isAttached).isTrue() + assertThat(fakeStatusBarWindowController.isAttached).isTrue() } @Test fun start_setsStatusBarControllerOnShade() { orchestrator.start() - verify(notificationShadeWindowViewController) - .setStatusBarViewController(statusBarViewController) + verify(mockNotificationShadeWindowViewController) + .setStatusBarViewController(fakeStatusBarInitializer.statusBarViewController) } @Test fun start_updatesShadeExpansion() { orchestrator.start() - verify(shadeSurface).updateExpansionAndVisibility() + verify(mockShadeSurface).updateExpansionAndVisibility() } @Test @@ -117,9 +110,9 @@ class StatusBarOrchestratorTest : SysuiTestCase() { testScope.runTest { orchestrator.start() - bouncerRepository.setPrimaryShow(isShowing = true) + fakeBouncerRepository.setPrimaryShow(isShowing = true) - verify(statusBarViewController) + verify(fakeStatusBarInitializer.statusBarViewController) .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) } @@ -128,9 +121,9 @@ class StatusBarOrchestratorTest : SysuiTestCase() { testScope.runTest { orchestrator.start() - bouncerRepository.setPrimaryShow(isShowing = false) + fakeBouncerRepository.setPrimaryShow(isShowing = false) - verify(statusBarViewController) + verify(fakeStatusBarInitializer.statusBarViewController) .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) } @@ -141,7 +134,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions).finishAnimations() + verify(fakeStatusBarInitializer.statusBarTransitions).finishAnimations() } @Test @@ -151,7 +144,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions, never()).finishAnimations() + verify(fakeStatusBarInitializer.statusBarTransitions, never()).finishAnimations() } @Test @@ -208,7 +201,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true) } @@ -222,19 +215,19 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true) setStatusBarMode(OPAQUE) - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true) setStatusBarMode(LIGHTS_OUT) - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true) setStatusBarMode(LIGHTS_OUT_TRANSPARENT) - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true) } @@ -248,7 +241,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false) } @@ -262,7 +255,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false) } @@ -276,7 +269,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { orchestrator.start() - verify(mockPhoneStatusBarTransitions) + verify(fakeStatusBarInitializer.statusBarTransitions) .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false) } @@ -295,7 +288,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { setTransientStatusBar() clearTransientStatusBar() - verify(mockPhoneStatusBarTransitions, times(1)) + verify(fakeStatusBarInitializer.statusBarTransitions, times(1)) .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true) } @@ -318,18 +311,18 @@ class StatusBarOrchestratorTest : SysuiTestCase() { } private fun setTransientStatusBar() { - statusBarModeRepository.defaultDisplay.showTransient() + fakeStatusBarModePerDisplayRepository.showTransient() } private fun clearTransientStatusBar() { - statusBarModeRepository.defaultDisplay.clearTransient() + fakeStatusBarModePerDisplayRepository.clearTransient() } private fun setStatusBarWindowState(state: StatusBarWindowState) { - fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state) + fakeStatusBarWindowStatePerDisplayRepository.setWindowState(state) } private fun setStatusBarMode(statusBarMode: StatusBarMode) { - statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode + fakeStatusBarModePerDisplayRepository.statusBarMode.value = statusBarMode } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java index d04d6fc5f593..0947cd5aaabb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java @@ -45,12 +45,12 @@ import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.service.notification.StatusBarNotification; -import android.testing.UiThreadTest; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java index 22f1e4604bbd..6435e8203d3d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java @@ -24,9 +24,9 @@ import static org.mockito.Mockito.mock; import android.provider.Settings; import android.testing.TestableResources; -import android.testing.UiThreadTest; import android.util.KeyValueListParser; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index 8d678ef00b4a..bf144729dea3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -21,7 +21,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest -import com.android.app.tracing.coroutines.flow.map import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.DisableSceneContainer @@ -51,6 +50,7 @@ import com.android.systemui.util.ui.isAnimating import com.android.systemui.util.ui.value import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 1d74331e429b..94753f7e5203 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -34,7 +34,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,9 +42,7 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.service.trust.TrustAgentService; import android.testing.TestableLooper; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; @@ -67,7 +64,6 @@ import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; @@ -580,22 +576,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @DisableSceneContainer - @DisableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testShowAlternateBouncer_unlockingWithBiometricAllowed() { - // GIVEN will show alternate bouncer - when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mAlternateBouncerInteractor.show()).thenReturn(true); - - // WHEN showGenericBouncer is called - mStatusBarKeyguardViewManager.showBouncer(true); - - // THEN alt auth bouncer is shown - verify(mAlternateBouncerInteractor).show(); - verify(mPrimaryBouncerInteractor, never()).show(anyBoolean()); - } - - @Test public void testUpdateResources_delegatesToBouncer() { mStatusBarKeyguardViewManager.updateResources(); @@ -841,145 +821,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN the touch is not acted upon - verify(mCallback, never()).onTouch(any()); - } - - @Test - @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void onInterceptTouch_alternateBouncerViewFlagEnabled() { - // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN the touch is not intercepted - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - } - - @Test - public void handleDispatchTouchEvent_alternateBouncerNotVisible() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // THEN handleDispatchTouchEvent doesn't use the touches - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is not acted upon - verify(mCallback, never()).onTouch(any()); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_shouldInterceptTouchAndHandleTouch() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN all touches are NOT the udfps overlay - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent - // to its child views (handleDispatchTouchEvent returns true) - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is acted upon once for each dispatchTOuchEvent call - verify(mCallback, times(3)).onTouch(any()); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_shouldInterceptTouchButNotHandleTouch() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN all touches are within the udfps overlay - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(true); - - // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent - // to its child views (handleDispatchTouchEvent returns true) - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is NOT acted upon at the moment - verify(mCallback, never()).onTouch(any()); - } - - @Test - @DisableSceneContainer - public void shouldInterceptTouch_alternateBouncerNotVisible() { - // GIVEN the alternate bouncer is not visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // THEN no motion events are intercepted - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void shouldInterceptTouch_alternateBouncerVisible() { - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN all motion events are intercepted - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - } - - @Test public void alternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() { // GIVEN the alternate bouncer has shown and calls to hide() will result in successfully // hiding it @@ -997,106 +838,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void alternateBouncerOnTouch_actionDownThenUp_noMinTimeShown_noHideAltBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(false); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN ACTION_DOWN and ACTION_UP touch event comes - boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); - when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true); - boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the touches are handled (doesn't let touches through to underlying views) - assertTrue(touchHandledDown); - assertTrue(touchHandledUp); - - // THEN alternate bouncer does NOT attempt to hide since min showing time wasn't met - verify(mAlternateBouncerInteractor, never()).hide(); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void alternateBouncerOnTouch_actionDownThenUp_handlesTouch_hidesAltBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN ACTION_DOWN and ACTION_UP touch event comes - boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); - when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true); - boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the touches are handled - assertTrue(touchHandledDown); - assertTrue(touchHandledUp); - - // THEN alternate bouncer attempts to hide - verify(mAlternateBouncerInteractor).hide(); - } - - @Test - @DisableSceneContainer - public void alternateBouncerOnTouch_actionUp_doesNotHideAlternateBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN only ACTION_UP touch event comes - mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the alternateBouncer doesn't hide - verify(mAlternateBouncerInteractor, never()).hide(); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() { - // GIVEN keyguard update monitor callback is registered - verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); - - reset(mKeyguardUpdateMonitor); - reset(mKeyguardMessageAreaController); - - // GIVEN alternate bouncer state = not visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // WHEN the device is trusted by active unlock - mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser( - true, - true, - new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD - | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE), - null - ); - - // THEN the false visibility state is propagated to the keyguardUpdateMonitor - verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false)); - - // THEN message area visibility updated to FALSE with empty message - verify(mKeyguardMessageAreaController).setIsVisible(eq(false)); - verify(mKeyguardMessageAreaController).setMessage(eq("")); - } - - @Test - @DisableSceneContainer @DisableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART) public void testShowBouncerOrKeyguard_needsFullScreen() { when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt index b8f581574848..a4b3916b7a55 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt @@ -35,7 +35,7 @@ class DrawableSizeTest : SysuiTestCase() { val drawable = BitmapDrawable( resources, - Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888), ) val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300) assertThat(result).isSameInstanceAs(drawable) @@ -48,7 +48,7 @@ class DrawableSizeTest : SysuiTestCase() { val drawable = BitmapDrawable( resources, - Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888), ) val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75) @@ -64,4 +64,31 @@ class DrawableSizeTest : SysuiTestCase() { val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) assertThat(result).isSameInstanceAs(drawable) } + + @Test + fun testDownscaleToSize_layerDrawable_allLayersSameType_resized() { + val drawable = + resources.getDrawable( + com.android.systemui.tests.R.drawable.layer_drawable_all_same_type, + resources.newTheme(), + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + + assertThat(result).isNotSameInstanceAs(drawable) + } + + /** Regression test for b/244282477. */ + @Test + fun testDownscaleToSize_layerDrawable_layersAreDifferentTypes_unchanged() { + val drawable = + resources.getDrawable( + com.android.systemui.tests.R.drawable.layer_drawable_different_types, + resources.newTheme(), + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + + assertThat(result).isSameInstanceAs(drawable) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt new file mode 100644 index 000000000000..7361de7d21b0 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.volume.domain.interactor + +import android.media.AudioManager +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.volume.shared.model.AudioStream +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.android.systemui.volume.data.repository.audioRepository +import com.android.systemui.volume.panel.component.volume.domain.model.SliderType +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class AudioSlidersInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + + private lateinit var underTest: AudioSlidersInteractor + + @Before + fun setUp() = + with(kosmos) { + audioRepository.setMode(AudioManager.MODE_NORMAL) + underTest = audioSlidersInteractor + } + + @Test + fun shouldAddAllStreams_notInCall() = + with(kosmos) { + testScope.runTest { + val sliders by collectLastValue(underTest.volumePanelSliders) + runCurrent() + + assertThat(sliders).isEqualTo( + mutableListOf( + AudioManager.STREAM_MUSIC, + AudioManager.STREAM_VOICE_CALL, + AudioManager.STREAM_RING, + AudioManager.STREAM_NOTIFICATION, + AudioManager.STREAM_ALARM + ).map { SliderType.Stream(AudioStream(it)) }) + } + } + + @Test + fun shouldAddAllStreams_inCall() = + with(kosmos) { + testScope.runTest { + audioRepository.setMode(AudioManager.MODE_IN_CALL) + + val sliders by collectLastValue(underTest.volumePanelSliders) + runCurrent() + + // Call stream is before music stream while in call. + assertThat(sliders).isEqualTo( + mutableListOf( + AudioManager.STREAM_VOICE_CALL, + AudioManager.STREAM_MUSIC, + AudioManager.STREAM_RING, + AudioManager.STREAM_NOTIFICATION, + AudioManager.STREAM_ALARM + ).map { SliderType.Stream(AudioStream(it)) }) + } + } +} diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml index 690a89a044b7..d0a1ce8ae629 100644 --- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml @@ -45,31 +45,26 @@ android:tint="?android:attr/colorPrimary" /> - <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever - be shown at one time. --> + <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text, + ongoing_activity_chip_short_time_delta] will ever be shown at one time. --> + + <!-- Shows a timer, like 00:01. --> <com.android.systemui.statusbar.chips.ui.view.ChipChronometer android:id="@+id/ongoing_activity_chip_time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:gravity="center|start" - android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" - android:textAppearance="@android:style/TextAppearance.Material.Small" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:textColor="?android:attr/colorPrimary" + style="@style/StatusBar.Chip.Text" /> - <!-- Used to show generic text in the chip instead of a timer. --> + <!-- Shows generic text. --> <TextView android:id="@+id/ongoing_activity_chip_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:gravity="center|start" - android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" - android:textAppearance="@android:style/TextAppearance.Material.Small" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:textColor="?android:attr/colorPrimary" + style="@style/StatusBar.Chip.Text" + android:visibility="gone" + /> + + <!-- Shows a time delta in short form, like "15min" or "1hr". --> + <android.widget.DateTimeView + android:id="@+id/ongoing_activity_chip_short_time_delta" + style="@style/StatusBar.Chip.Text" android:visibility="gone" /> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index e21466671363..77fbb642f664 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -128,12 +128,6 @@ <include layout="@layout/dock_info_bottom_area_overlay" /> - <com.android.keyguard.LockIconView - android:id="@+id/lock_icon_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - </com.android.keyguard.LockIconView> - <include layout="@layout/keyguard_bottom_area" android:visibility="gone" /> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 22d34eb7b115..fbb07bed4b50 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -58,22 +58,22 @@ android:layout_height="match_parent" android:visibility="invisible" /> - <!-- Shared container for the notification stack. Can be positioned by either - the keyguard_root_view or notification_panel --> - <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer - android:id="@+id/shared_notification_container" + <!-- Root for all keyguard content. It was previously located within the shade. --> + <com.android.systemui.keyguard.ui.view.KeyguardRootView + android:id="@id/keyguard_root_view" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" - android:clipToPadding="false" /> - <!-- Root for all keyguard content. It was previously located within the shade. --> - <com.android.systemui.keyguard.ui.view.KeyguardRootView - android:id="@id/keyguard_root_view" + <!-- Shared container for the notification stack. Can be positioned by either + the keyguard_root_view or notification_panel --> + <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer + android:id="@+id/shared_notification_container" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" + android:clipToPadding="false" /> <include layout="@layout/brightness_mirror_container" /> diff --git a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml b/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml deleted file mode 100644 index 4799f8c5b668..000000000000 --- a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<com.android.systemui.biometrics.UdfpsFpmEmptyView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <!-- The layout height/width are placeholders, which will be overwritten by - FingerprintSensorPropertiesInternal. --> - <View - android:id="@+id/udfps_enroll_accessibility_view" - android:layout_gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:contentDescription="@string/accessibility_fingerprint_label"/> -</com.android.systemui.biometrics.UdfpsFpmEmptyView> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml deleted file mode 100644 index 257d238f5c54..000000000000 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<com.android.systemui.biometrics.UdfpsView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:id="@+id/udfps_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - systemui:sensorTouchAreaCoefficient="1.0" - android:contentDescription="@string/accessibility_fingerprint_label"> - - <ViewStub - android:id="@+id/animation_view" - android:layout_width="match_parent" - android:layout_height="match_parent"/> - -</com.android.systemui.biometrics.UdfpsView> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index e1f25f99cc98..7cebac2feaba 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -70,6 +70,20 @@ <item name="android:fontWeight">700</item> </style> + <style name="StatusBar" /> + <style name="StatusBar.Chip" /> + + <style name="StatusBar.Chip.Text"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:singleLine">true</item> + <item name="android:gravity">center|start</item> + <item name="android:paddingStart">@dimen/ongoing_activity_chip_icon_text_padding</item> + <item name="android:textAppearance">@android:style/TextAppearance.Material.Small</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textColor">?android:attr/colorPrimary</item> + </style> + <style name="Chipbar" /> <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title"> @@ -677,10 +691,12 @@ <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="android:windowActionBar">false</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> <item name="preferenceTheme">@style/TunerPreferenceTheme</item> </style> <style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> <style name="TextAppearance.NotificationInfo.Confirmation"> diff --git a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt index b792db354b36..306d68217e50 100644 --- a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt +++ b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt @@ -17,6 +17,7 @@ package com.android.keyguard import android.view.MotionEvent +import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.res.R @@ -34,11 +35,10 @@ import javax.inject.Inject @SysUISingleton class EmptyLockIconViewController @Inject -constructor( - private val keyguardRootView: Lazy<KeyguardRootView>, -) : LockIconViewController { +constructor(private val keyguardRootView: Lazy<KeyguardRootView>) : LockIconViewController { private val deviceEntryIconViewId = R.id.device_entry_icon_view - override fun setLockIconView(lockIconView: LockIconView) { + + override fun setLockIconView(lockIconView: View) { // no-op } diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java deleted file mode 100644 index 03b13fe47c10..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; -import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; - -import static com.android.keyguard.LockIconView.ICON_FINGERPRINT; -import static com.android.keyguard.LockIconView.ICON_LOCK; -import static com.android.keyguard.LockIconView.ICON_UNLOCK; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; -import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricSourceType; -import android.os.VibrationAttributes; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.MathUtils; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; - -import com.android.systemui.Dumpable; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.biometrics.AuthRippleController; -import com.android.systemui.biometrics.UdfpsController; -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.KeyguardBottomAreaRefactor; -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.KeyguardState; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.res.R; -import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import dagger.Lazy; - -import kotlinx.coroutines.ExperimentalCoroutinesApi; - -import java.io.PrintWriter; -import java.util.Objects; -import java.util.function.Consumer; - -import javax.inject.Inject; - -/** - * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen. - * - * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock - * icon will show a set distance from the bottom of the device. - */ -@SysUISingleton -public class LegacyLockIconViewController implements Dumpable, LockIconViewController { - private static final String TAG = "LockIconViewController"; - private static final float sDefaultDensity = - (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT; - private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36); - private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); - - private static final long FADE_OUT_DURATION_MS = 250L; - - private final long mLongPressTimeout; - @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @NonNull private final KeyguardViewController mKeyguardViewController; - @NonNull private final StatusBarStateController mStatusBarStateController; - @NonNull private final KeyguardStateController mKeyguardStateController; - @NonNull private final FalsingManager mFalsingManager; - @NonNull private final AuthController mAuthController; - @NonNull private final AccessibilityManager mAccessibilityManager; - @NonNull private final ConfigurationController mConfigurationController; - @NonNull private final DelayableExecutor mExecutor; - private boolean mUdfpsEnrolled; - private Resources mResources; - private Context mContext; - @NonNull private CharSequence mUnlockedLabel; - @NonNull private CharSequence mLockedLabel; - @NonNull private final VibratorHelper mVibrator; - @Nullable private final AuthRippleController mAuthRippleController; - @NonNull private final FeatureFlags mFeatureFlags; - @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; - @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; - @NonNull private final KeyguardInteractor mKeyguardInteractor; - @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate; - @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor; - - // Tracks the velocity of a touch to help filter out the touches that move too fast. - private VelocityTracker mVelocityTracker; - // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. - private int mActivePointerId = -1; - - private boolean mIsDozing; - private boolean mIsActiveDreamLockscreenHosted; - private boolean mIsBouncerShowing; - private boolean mRunningFPS; - private boolean mCanDismissLockScreen; - private int mStatusBarState; - private boolean mIsKeyguardShowing; - private Runnable mLongPressCancelRunnable; - - private boolean mUdfpsSupported; - private float mHeightPixels; - private float mWidthPixels; - private int mBottomPaddingPx; - private int mDefaultPaddingPx; - - private boolean mShowUnlockIcon; - private boolean mShowLockIcon; - - // for udfps when strong auth is required or unlocked on AOD - private boolean mShowAodLockIcon; - private boolean mShowAodUnlockedIcon; - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; - private float mInterpolatedDarkAmount; - - private boolean mDownDetected; - private final Rect mSensorTouchLocation = new Rect(); - private LockIconView mView; - - @VisibleForTesting - final Consumer<Float> mDozeTransitionCallback = (Float value) -> { - mInterpolatedDarkAmount = value; - mView.setDozeAmount(value); - updateBurnInOffsets(); - }; - - @VisibleForTesting - final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> { - mIsDozing = isDozing; - updateBurnInOffsets(); - updateVisibility(); - }; - - @VisibleForTesting - final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = - (Boolean isLockscreenHosted) -> { - mIsActiveDreamLockscreenHosted = isLockscreenHosted; - updateVisibility(); - }; - - @Inject - public LegacyLockIconViewController( - @NonNull StatusBarStateController statusBarStateController, - @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, - @NonNull KeyguardViewController keyguardViewController, - @NonNull KeyguardStateController keyguardStateController, - @NonNull FalsingManager falsingManager, - @NonNull AuthController authController, - @NonNull DumpManager dumpManager, - @NonNull AccessibilityManager accessibilityManager, - @NonNull ConfigurationController configurationController, - @NonNull @Main DelayableExecutor executor, - @NonNull VibratorHelper vibrator, - @Nullable AuthRippleController authRippleController, - @NonNull @Main Resources resources, - @NonNull KeyguardTransitionInteractor transitionInteractor, - @NonNull KeyguardInteractor keyguardInteractor, - @NonNull FeatureFlags featureFlags, - PrimaryBouncerInteractor primaryBouncerInteractor, - Context context, - Lazy<DeviceEntryInteractor> deviceEntryInteractor - ) { - mStatusBarStateController = statusBarStateController; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mAuthController = authController; - mKeyguardViewController = keyguardViewController; - mKeyguardStateController = keyguardStateController; - mFalsingManager = falsingManager; - mAccessibilityManager = accessibilityManager; - mConfigurationController = configurationController; - mExecutor = executor; - mVibrator = vibrator; - mAuthRippleController = authRippleController; - mTransitionInteractor = transitionInteractor; - mKeyguardInteractor = keyguardInteractor; - mFeatureFlags = featureFlags; - mPrimaryBouncerInteractor = primaryBouncerInteractor; - - mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); - mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); - mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button); - mLockedLabel = resources.getString(R.string.accessibility_lock_icon); - mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress); - dumpManager.registerDumpable(TAG, this); - mResources = resources; - mContext = context; - mDeviceEntryInteractor = deviceEntryInteractor; - - mAccessibilityDelegate = new View.AccessibilityDelegate() { - private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint = - new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfoCompat.ACTION_CLICK, - mResources.getString(R.string.accessibility_authenticate_hint)); - private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint = - new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfoCompat.ACTION_CLICK, - mResources.getString(R.string.accessibility_enter_hint)); - public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(v, info); - if (isActionable()) { - if (mShowLockIcon) { - info.addAction(mAccessibilityAuthenticateHint); - } else if (mShowUnlockIcon) { - info.addAction(mAccessibilityEnterHint); - } - } - } - }; - } - - /** Sets the LockIconView to the controller and rebinds any that depend on it. */ - @SuppressLint("ClickableViewAccessibility") - @Override - public void setLockIconView(LockIconView lockIconView) { - mView = lockIconView; - mView.setAccessibilityDelegate(mAccessibilityDelegate); - - if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD), - mDozeTransitionCallback); - collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback); - } - - if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { - collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(), - mIsActiveDreamLockscreenHostedCallback); - } - - updateIsUdfpsEnrolled(); - updateConfiguration(); - updateKeyguardShowing(); - - mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); - mIsDozing = mStatusBarStateController.isDozing(); - mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount(); - mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); - mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); - mStatusBarState = mStatusBarStateController.getState(); - - updateColors(); - mDownDetected = false; - updateBurnInOffsets(); - updateVisibility(); - - updateAccessibility(); - - lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View view) { - registerCallbacks(); - } - - @Override - public void onViewDetachedFromWindow(View view) { - unregisterCallbacks(); - } - }); - - if (lockIconView.isAttachedToWindow()) { - registerCallbacks(); - } - - lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent)); - } - - private void registerCallbacks() { - mConfigurationController.addCallback(mConfigurationListener); - mAuthController.addCallback(mAuthControllerCallback); - mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mKeyguardStateController.addCallback(mKeyguardStateCallback); - mAccessibilityManager.addAccessibilityStateChangeListener( - mAccessibilityStateChangeListener); - - } - - private void unregisterCallbacks() { - mAuthController.removeCallback(mAuthControllerCallback); - mConfigurationController.removeCallback(mConfigurationListener); - mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); - mStatusBarStateController.removeCallback(mStatusBarStateListener); - mKeyguardStateController.removeCallback(mKeyguardStateCallback); - mAccessibilityManager.removeAccessibilityStateChangeListener( - mAccessibilityStateChangeListener); - - } - - private void updateAccessibility() { - if (mAccessibilityManager.isEnabled()) { - mView.setOnClickListener(mA11yClickListener); - } else { - mView.setOnClickListener(null); - } - } - - @Override - public float getTop() { - return mView.getLocationTop(); - } - - @Override - public float getBottom() { - return mView.getLocationBottom(); - } - - private void updateVisibility() { - if (!mIsKeyguardShowing && !mIsDozing) { - mView.setVisibility(View.INVISIBLE); - return; - } - - if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) { - mView.setVisibility(View.INVISIBLE); - return; - } - - boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon - && !mShowAodUnlockedIcon && !mShowAodLockIcon; - mShowLockIcon = !mCanDismissLockScreen && isLockScreen() - && (!mUdfpsEnrolled || !mRunningFPS); - mShowUnlockIcon = mCanDismissLockScreen && isLockScreen(); - mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen; - mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen; - - final CharSequence prevContentDescription = mView.getContentDescription(); - if (mShowLockIcon) { - if (wasShowingFpIcon) { - // fp icon was shown by UdfpsView, and now we still want to animate the transition - // in this drawable - mView.updateIcon(ICON_FINGERPRINT, false); - } - mView.updateIcon(ICON_LOCK, false); - mView.setContentDescription(mLockedLabel); - mView.setVisibility(View.VISIBLE); - } else if (mShowUnlockIcon) { - if (wasShowingFpIcon) { - // fp icon was shown by UdfpsView, and now we still want to animate the transition - // in this drawable - mView.updateIcon(ICON_FINGERPRINT, false); - } - mView.updateIcon(ICON_UNLOCK, false); - mView.setContentDescription(mUnlockedLabel); - mView.setVisibility(View.VISIBLE); - } else if (mShowAodUnlockedIcon) { - mView.updateIcon(ICON_UNLOCK, true); - mView.setContentDescription(mUnlockedLabel); - mView.setVisibility(View.VISIBLE); - } else if (mShowAodLockIcon) { - mView.updateIcon(ICON_LOCK, true); - mView.setContentDescription(mLockedLabel); - mView.setVisibility(View.VISIBLE); - } else { - mView.clearIcon(); - mView.setVisibility(View.INVISIBLE); - mView.setContentDescription(null); - } - - boolean accessibilityEnabled = - !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser(); - mView.setImportantForAccessibility( - accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES - : View.IMPORTANT_FOR_ACCESSIBILITY_NO); - - if (!Objects.equals(prevContentDescription, mView.getContentDescription()) - && mView.getContentDescription() != null && accessibilityEnabled) { - mView.announceForAccessibility(mView.getContentDescription()); - } - } - - private boolean isLockScreen() { - return !mIsDozing - && !mIsBouncerShowing - && mStatusBarState == StatusBarState.KEYGUARD; - } - - private void updateKeyguardShowing() { - mIsKeyguardShowing = mKeyguardStateController.isShowing() - && !mKeyguardStateController.isKeyguardGoingAway(); - } - - private void updateColors() { - mView.updateColorAndBackgroundVisibility(); - } - - private void updateConfiguration() { - WindowManager windowManager = mContext.getSystemService(WindowManager.class); - Rect bounds = windowManager.getCurrentWindowMetrics().getBounds(); - mWidthPixels = bounds.right; - if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) { - // Assumed to be initially neglected as there are no left or right insets in portrait - // However, on landscape, these insets need to included when calculating the midpoint - WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets(); - mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight(); - } - mHeightPixels = bounds.bottom; - mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom); - mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding); - mUnlockedLabel = mResources.getString( - R.string.accessibility_unlock_button); - mLockedLabel = mResources.getString(R.string.accessibility_lock_icon); - updateLockIconLocation(); - } - - private void updateLockIconLocation() { - final float scaleFactor = mAuthController.getScaleFactor(); - final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor); - if (KeyguardBottomAreaRefactor.isEnabled() || MigrateClocksToBlueprint.isEnabled()) { - // positioning in this case is handled by [DefaultDeviceEntrySection] - mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding, - scaledPadding); - } else { - if (mUdfpsSupported) { - mView.setCenterLocation(mAuthController.getUdfpsLocation(), - mAuthController.getUdfpsRadius(), scaledPadding); - } else { - mView.setCenterLocation( - new Point((int) mWidthPixels / 2, - (int) (mHeightPixels - - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))), - sLockIconRadiusPx * scaleFactor, scaledPadding); - } - } - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("mUdfpsSupported: " + mUdfpsSupported); - pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled); - pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing); - pw.println(); - pw.println(" mShowUnlockIcon: " + mShowUnlockIcon); - pw.println(" mShowLockIcon: " + mShowLockIcon); - pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon); - pw.println(); - pw.println(" mIsDozing: " + mIsDozing); - pw.println(" isFlagEnabled(DOZING_MIGRATION_1): " - + mFeatureFlags.isEnabled(DOZING_MIGRATION_1)); - pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); - pw.println(" mRunningFPS: " + mRunningFPS); - pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); - pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState)); - pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); - pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); - pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx); - pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted); - - if (mView != null) { - mView.dump(pw, args); - } - } - - /** Every minute, update the aod icon's burn in offset */ - @Override - public void dozeTimeTick() { - updateBurnInOffsets(); - } - - private void updateBurnInOffsets() { - float offsetX = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) - - mMaxBurnInOffsetX, mInterpolatedDarkAmount); - float offsetY = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - - mMaxBurnInOffsetY, mInterpolatedDarkAmount); - - mView.setTranslationX(offsetX); - mView.setTranslationY(offsetY); - } - - private void updateIsUdfpsEnrolled() { - boolean wasUdfpsSupported = mUdfpsSupported; - boolean wasUdfpsEnrolled = mUdfpsEnrolled; - - mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported(); - mView.setUseBackground(mUdfpsSupported); - - mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled(); - if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) { - updateVisibility(); - } - } - - private StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { - @Override - public void onDozeAmountChanged(float linear, float eased) { - if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - mInterpolatedDarkAmount = eased; - mView.setDozeAmount(eased); - updateBurnInOffsets(); - } - } - - @Override - public void onDozingChanged(boolean isDozing) { - if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { - mIsDozing = isDozing; - updateBurnInOffsets(); - updateVisibility(); - } - } - - @Override - public void onStateChanged(int statusBarState) { - mStatusBarState = statusBarState; - updateVisibility(); - } - }; - - private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onKeyguardBouncerStateChanged(boolean bouncer) { - mIsBouncerShowing = bouncer; - updateVisibility(); - } - - @Override - public void onBiometricRunningStateChanged(boolean running, - BiometricSourceType biometricSourceType) { - final boolean wasRunningFps = mRunningFPS; - - if (biometricSourceType == FINGERPRINT) { - mRunningFPS = running; - } - - if (wasRunningFps != mRunningFPS) { - updateVisibility(); - } - } - }; - - private final KeyguardStateController.Callback mKeyguardStateCallback = - new KeyguardStateController.Callback() { - @Override - public void onUnlockedChanged() { - mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); - updateKeyguardShowing(); - updateVisibility(); - } - - @Override - public void onKeyguardShowingChanged() { - // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe). - // If biometrics were removed, local vars mCanDismissLockScreen and - // mUserUnlockedWithBiometric may not be updated. - mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen(); - - // reset mIsBouncerShowing state in case it was preemptively set - // onLongPress - mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); - - updateKeyguardShowing(); - updateVisibility(); - } - - @Override - public void onKeyguardFadingAwayChanged() { - updateKeyguardShowing(); - updateVisibility(); - } - }; - - private final ConfigurationController.ConfigurationListener mConfigurationListener = - new ConfigurationController.ConfigurationListener() { - @Override - public void onUiModeChanged() { - updateColors(); - } - - @Override - public void onThemeChanged() { - updateColors(); - } - - @Override - public void onConfigChanged(Configuration newConfig) { - updateConfiguration(); - updateColors(); - } - }; - - /** - * Handles the touch if {@link #isActionable()} is true. - * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon - * area for {@link #mLongPressTimeout} ms. - * - * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}. - */ - private boolean onTouchEvent(MotionEvent event) { - if (!actionableDownEventStartedOnView(event)) { - cancelTouches(); - return false; - } - - switch(event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_HOVER_ENTER: - if (!mDownDetected && mAccessibilityManager.isTouchExplorationEnabled()) { - vibrateOnTouchExploration(); - } - - // The pointer that causes ACTION_DOWN is always at index 0. - // We need to persist its ID to track it during ACTION_MOVE that could include - // data for many other pointers because of multi-touch support. - mActivePointerId = event.getPointerId(0); - if (mVelocityTracker == null) { - // To simplify the lifecycle of the velocity tracker, make sure it's never null - // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP. - mVelocityTracker = VelocityTracker.obtain(); - } else { - // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new - // ACTION_DOWN, in that case we should just reuse the old instance. - mVelocityTracker.clear(); - } - mVelocityTracker.addMovement(event); - - mDownDetected = true; - mLongPressCancelRunnable = mExecutor.executeDelayed( - this::onLongPress, mLongPressTimeout); - break; - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_HOVER_MOVE: - mVelocityTracker.addMovement(event); - // Compute pointer velocity in pixels per second. - mVelocityTracker.computeCurrentVelocity(1000); - float velocity = computePointerSpeed(mVelocityTracker, - mActivePointerId); - if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS - && exceedsVelocityThreshold(velocity)) { - Log.v(TAG, "lock icon long-press rescheduled due to " - + "high pointer velocity=" + velocity); - mLongPressCancelRunnable.run(); - mLongPressCancelRunnable = mExecutor.executeDelayed( - this::onLongPress, mLongPressTimeout); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_HOVER_EXIT: - cancelTouches(); - break; - } - - return true; - } - - /** - * Calculate the pointer speed given a velocity tracker and the pointer id. - * This assumes that the velocity tracker has already been passed all relevant motion events. - */ - private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { - final float vx = tracker.getXVelocity(pointerId); - final float vy = tracker.getYVelocity(pointerId); - return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0)); - } - - /** - * Whether the velocity exceeds the acceptable UDFPS debouncing threshold. - */ - private static boolean exceedsVelocityThreshold(float velocity) { - return velocity > 750f; - } - - private boolean actionableDownEventStartedOnView(MotionEvent event) { - if (!isActionable()) { - return false; - } - - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - return true; - } - - return mDownDetected; - } - - @ExperimentalCoroutinesApi - @VisibleForTesting - protected void onLongPress() { - cancelTouches(); - if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) { - Log.v(TAG, "lock icon long-press rejected by the falsing manager."); - return; - } - - // pre-emptively set to true to hide view - mIsBouncerShowing = true; - if (!DeviceEntryUdfpsRefactor.isEnabled() - && mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) { - mAuthRippleController.showUnlockRipple(FINGERPRINT); - } - updateVisibility(); - - // play device entry haptic (consistent with UDFPS controller longpress) - vibrateOnLongPress(); - - if (SceneContainerFlag.isEnabled()) { - mDeviceEntryInteractor.get().attemptDeviceEntry(); - } else { - mKeyguardViewController.showPrimaryBouncer(/* scrim */ true); - } - } - - - private void cancelTouches() { - mDownDetected = false; - if (mLongPressCancelRunnable != null) { - mLongPressCancelRunnable.run(); - } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - - private boolean isActionable() { - if (mIsBouncerShowing) { - Log.v(TAG, "lock icon long-press ignored, bouncer already showing."); - // a long press gestures from AOD may have already triggered the bouncer to show, - // so this touch is no longer actionable - return false; - } - return mUdfpsSupported || mShowUnlockIcon; - } - - /** - * Set the alpha of this view. - */ - @Override - public void setAlpha(float alpha) { - mView.setAlpha(alpha); - } - - private void updateUdfpsConfig() { - // must be called from the main thread since it may update the views - mExecutor.execute(() -> { - updateIsUdfpsEnrolled(); - updateConfiguration(); - }); - } - - @VisibleForTesting - void vibrateOnTouchExploration() { - mVibrator.performHapticFeedback( - mView, - HapticFeedbackConstants.CONTEXT_CLICK - ); - } - - @VisibleForTesting - void vibrateOnLongPress() { - mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS); - } - - private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { - @Override - public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsConfig(); - } - } - - @Override - public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { - if (modality == TYPE_FINGERPRINT) { - updateUdfpsConfig(); - } - } - - @Override - public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) { - updateUdfpsConfig(); - } - }; - - /** - * Whether the lock icon will handle a touch while dozing. - */ - @Override - public boolean willHandleTouchWhileDozing(MotionEvent event) { - // is in lock icon area - mView.getHitRect(mSensorTouchLocation); - final boolean inLockIconArea = - mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) - && mView.getVisibility() == View.VISIBLE; - - return inLockIconArea && actionableDownEventStartedOnView(event); - } - - private final View.OnClickListener mA11yClickListener = v -> onLongPress(); - - private final AccessibilityManager.AccessibilityStateChangeListener - mAccessibilityStateChangeListener = enabled -> updateAccessibility(); -} diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java deleted file mode 100644 index ff6a3d0cc6f0..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; - -import com.android.internal.graphics.ColorUtils; -import com.android.settingslib.Utils; -import com.android.systemui.Dumpable; -import com.android.systemui.res.R; - -import java.io.PrintWriter; - -/** - * A view positioned under the notification shade. - */ -public class LockIconView extends FrameLayout implements Dumpable { - @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK}) - public @interface IconType {} - - public static final int ICON_NONE = -1; - public static final int ICON_LOCK = 0; - public static final int ICON_FINGERPRINT = 1; - public static final int ICON_UNLOCK = 2; - - private @IconType int mIconType; - private boolean mAod; - - @NonNull private final RectF mSensorRect; - @NonNull private Point mLockIconCenter = new Point(0, 0); - private float mRadius; - private int mLockIconPadding; - - private ImageView mLockIcon; - private ImageView mBgView; - - private int mLockIconColor; - private boolean mUseBackground = false; - private float mDozeAmount = 0f; - - @SuppressLint("ClickableViewAccessibility") - public LockIconView(Context context, AttributeSet attrs) { - super(context, attrs); - mSensorRect = new RectF(); - - addBgImageView(context, attrs); - addLockIconImageView(context, attrs); - } - - void setDozeAmount(float dozeAmount) { - mDozeAmount = dozeAmount; - updateColorAndBackgroundVisibility(); - } - - void updateColorAndBackgroundVisibility() { - if (mUseBackground && mLockIcon.getDrawable() != null) { - mLockIconColor = ColorUtils.blendARGB( - Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary), - Color.WHITE, - mDozeAmount); - int backgroundColor = Utils.getColorAttrDefaultColor(getContext(), - com.android.internal.R.attr.colorSurface); - mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor)); - mBgView.setAlpha(1f - mDozeAmount); - mBgView.setVisibility(View.VISIBLE); - } else { - mLockIconColor = ColorUtils.blendARGB( - Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent), - Color.WHITE, - mDozeAmount); - mBgView.setVisibility(View.GONE); - } - - mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor)); - } - - /** - * Whether or not to render the lock icon background. Mainly used for UDPFS. - */ - public void setUseBackground(boolean useBackground) { - mUseBackground = useBackground; - updateColorAndBackgroundVisibility(); - } - - /** - * Set the location of the lock icon. - */ - public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) { - mLockIconCenter = center; - mRadius = radius; - mLockIconPadding = drawablePadding; - - mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding, - mLockIconPadding); - - mSensorRect.set(mLockIconCenter.x - mRadius, - mLockIconCenter.y - mRadius, - mLockIconCenter.x + mRadius, - mLockIconCenter.y + mRadius); - - final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); - if (lp != null) { - lp.width = (int) (mSensorRect.right - mSensorRect.left); - lp.height = (int) (mSensorRect.bottom - mSensorRect.top); - lp.topMargin = (int) mSensorRect.top; - lp.setMarginStart((int) mSensorRect.left); - setLayoutParams(lp); - } - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } - - float getLocationTop() { - Rect r = new Rect(); - mLockIcon.getGlobalVisibleRect(r); - return r.top; - } - - float getLocationBottom() { - Rect r = new Rect(); - mLockIcon.getGlobalVisibleRect(r); - return r.bottom; - - } - - /** - * Updates the icon its default state where no visual is shown. - */ - public void clearIcon() { - updateIcon(ICON_NONE, false); - } - - /** - * Transition the current icon to a new state - * @param icon type (ie: lock icon, unlock icon, fingerprint icon) - * @param aod whether to use the aod icon variant (some icons don't have aod variants and will - * therefore show no icon) - */ - public void updateIcon(@IconType int icon, boolean aod) { - mIconType = icon; - mAod = aod; - - mLockIcon.setImageState(getLockIconState(mIconType, mAod), true); - } - - public ImageView getLockIcon() { - return mLockIcon; - } - - private void addLockIconImageView(Context context, AttributeSet attrs) { - mLockIcon = new ImageView(context, attrs); - mLockIcon.setId(R.id.lock_icon); - mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP); - mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon)); - addView(mLockIcon); - LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams(); - lp.height = MATCH_PARENT; - lp.width = MATCH_PARENT; - lp.gravity = Gravity.CENTER; - mLockIcon.setLayoutParams(lp); - } - - private void addBgImageView(Context context, AttributeSet attrs) { - mBgView = new ImageView(context, attrs); - mBgView.setId(R.id.lock_icon_bg); - mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg)); - mBgView.setVisibility(View.INVISIBLE); - addView(mBgView); - LayoutParams lp = (LayoutParams) mBgView.getLayoutParams(); - lp.height = MATCH_PARENT; - lp.width = MATCH_PARENT; - mBgView.setLayoutParams(lp); - } - - private static int[] getLockIconState(@IconType int icon, boolean aod) { - if (icon == ICON_NONE) { - return new int[0]; - } - - int[] lockIconState = new int[2]; - switch (icon) { - case ICON_LOCK: - lockIconState[0] = android.R.attr.state_first; - break; - case ICON_FINGERPRINT: - lockIconState[0] = android.R.attr.state_middle; - break; - case ICON_UNLOCK: - lockIconState[0] = android.R.attr.state_last; - break; - } - - if (aod) { - lockIconState[1] = android.R.attr.state_single; - } else { - lockIconState[1] = -android.R.attr.state_single; - } - - return lockIconState; - } - - private String typeToString(@IconType int type) { - switch (type) { - case ICON_NONE: - return "none"; - case ICON_LOCK: - return "lock"; - case ICON_FINGERPRINT: - return "fingerprint"; - case ICON_UNLOCK: - return "unlock"; - } - - return "invalid"; - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("Lock Icon View Parameters:"); - pw.println(" Center in px (x, y)= (" - + mLockIconCenter.x + ", " + mLockIconCenter.y + ")"); - pw.println(" Radius in pixels: " + mRadius); - pw.println(" Drawable padding: " + mLockIconPadding); - pw.println(" mIconType=" + typeToString(mIconType)); - pw.println(" mAod=" + mAod); - pw.println("Lock Icon View actual measurements:"); - pw.println(" topLeft= (" + getX() + ", " + getY() + ")"); - pw.println(" width=" + getWidth() + " height=" + getHeight()); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt index 10d5a0cc3dd5..c5012b01dd3e 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt @@ -17,13 +17,19 @@ package com.android.keyguard import android.view.MotionEvent +import android.view.View /** Controls the [LockIconView]. */ interface LockIconViewController { - fun setLockIconView(lockIconView: LockIconView) + fun setLockIconView(lockIconView: View) + fun getTop(): Float + fun getBottom(): Float + fun dozeTimeTick() + fun setAlpha(alpha: Float) + fun willHandleTouchWhileDozing(event: MotionEvent): Boolean } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt index cd9efaf6e6bb..610e3f8a8c84 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt @@ -39,6 +39,10 @@ import com.android.systemui.qs.tiles.impl.fontscaling.domain.FontScalingTileMapp import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor @@ -159,6 +163,13 @@ interface QSAccessibilityModule { impl: NightDisplayTileDataInteractor ): QSTileAvailabilityInteractor + @Binds + @IntoMap + @StringKey(HEARING_DEVICES_TILE_SPEC) + fun provideHearingDevicesAvailabilityInteractor( + impl: HearingDevicesTileDataInteractor + ): QSTileAvailabilityInteractor + companion object { const val COLOR_CORRECTION_TILE_SPEC = "color_correction" const val COLOR_INVERSION_TILE_SPEC = "inversion" @@ -191,7 +202,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>, mapper: ColorCorrectionTileMapper, stateInteractor: ColorCorrectionTileDataInteractor, - userActionInteractor: ColorCorrectionUserActionInteractor + userActionInteractor: ColorCorrectionUserActionInteractor, ): QSTileViewModel = factory.create( TileSpec.create(COLOR_CORRECTION_TILE_SPEC), @@ -223,7 +234,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<ColorInversionTileModel>, mapper: ColorInversionTileMapper, stateInteractor: ColorInversionTileDataInteractor, - userActionInteractor: ColorInversionUserActionInteractor + userActionInteractor: ColorInversionUserActionInteractor, ): QSTileViewModel = factory.create( TileSpec.create(COLOR_INVERSION_TILE_SPEC), @@ -255,7 +266,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<FontScalingTileModel>, mapper: FontScalingTileMapper, stateInteractor: FontScalingTileDataInteractor, - userActionInteractor: FontScalingTileUserActionInteractor + userActionInteractor: FontScalingTileUserActionInteractor, ): QSTileViewModel = factory.create( TileSpec.create(FONT_SCALING_TILE_SPEC), @@ -279,21 +290,6 @@ interface QSAccessibilityModule { category = TileCategory.DISPLAY, ) - @Provides - @IntoMap - @StringKey(HEARING_DEVICES_TILE_SPEC) - fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = - QSTileConfig( - tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC), - uiConfig = - QSTileUIConfig.Resource( - iconRes = R.drawable.qs_hearing_devices_icon, - labelRes = R.string.quick_settings_hearing_devices_label, - ), - instanceId = uiEventLogger.getNewInstanceId(), - category = TileCategory.ACCESSIBILITY, - ) - /** * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden * behind a flag. @@ -305,7 +301,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>, mapper: ReduceBrightColorsTileMapper, stateInteractor: ReduceBrightColorsTileDataInteractor, - userActionInteractor: ReduceBrightColorsTileUserActionInteractor + userActionInteractor: ReduceBrightColorsTileUserActionInteractor, ): QSTileViewModel = if (Flags.qsNewTilesFuture()) factory.create( @@ -339,7 +335,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<OneHandedModeTileModel>, mapper: OneHandedModeTileMapper, stateInteractor: OneHandedModeTileDataInteractor, - userActionInteractor: OneHandedModeTileUserActionInteractor + userActionInteractor: OneHandedModeTileUserActionInteractor, ): QSTileViewModel = if (Flags.qsNewTilesFuture()) factory.create( @@ -376,7 +372,7 @@ interface QSAccessibilityModule { factory: QSTileViewModelFactory.Static<NightDisplayTileModel>, mapper: NightDisplayTileMapper, stateInteractor: NightDisplayTileDataInteractor, - userActionInteractor: NightDisplayTileUserActionInteractor + userActionInteractor: NightDisplayTileUserActionInteractor, ): QSTileViewModel = if (Flags.qsNewTilesFuture()) factory.create( @@ -386,5 +382,43 @@ interface QSAccessibilityModule { mapper, ) else StubQSTileViewModel + + @Provides + @IntoMap + @StringKey(HEARING_DEVICES_TILE_SPEC) + fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.qs_hearing_devices_icon, + labelRes = R.string.quick_settings_hearing_devices_label, + ), + instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.ACCESSIBILITY, + ) + + /** + * Inject HearingDevices Tile into tileViewModelMap in QSModule. The tile is hidden behind a + * flag. + */ + @Provides + @IntoMap + @StringKey(HEARING_DEVICES_TILE_SPEC) + fun provideHearingDevicesTileViewModel( + factory: QSTileViewModelFactory.Static<HearingDevicesTileModel>, + mapper: HearingDevicesTileMapper, + stateInteractor: HearingDevicesTileDataInteractor, + userActionInteractor: HearingDevicesTileUserActionInteractor, + ): QSTileViewModel { + return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) { + factory.create( + TileSpec.create(HEARING_DEVICES_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + } else StubQSTileViewModel + } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index c95a94e5e388..f6cc72431db0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -37,11 +37,9 @@ import com.android.systemui.biometrics.data.repository.FacePropertyRepository import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.log.core.LogLevel import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.statusbar.CircleReveal @@ -87,7 +85,7 @@ constructor( private val lightRevealScrim: LightRevealScrim, private val authRippleInteractor: AuthRippleInteractor, private val facePropertyRepository: FacePropertyRepository, - rippleView: AuthRippleView? + rippleView: AuthRippleView?, ) : ViewController<AuthRippleView>(rippleView), CoreStartable, @@ -108,15 +106,13 @@ constructor( } init { - if (DeviceEntryUdfpsRefactor.isEnabled) { - rippleView?.repeatWhenAttached { - repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) { - authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource -> - if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) { - showUnlockRippleInternal(BiometricSourceType.FINGERPRINT) - } else { - showUnlockRippleInternal(BiometricSourceType.FACE) - } + rippleView?.repeatWhenAttached { + repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) { + authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource -> + if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) { + showUnlockRippleInternal(BiometricSourceType.FINGERPRINT) + } else { + showUnlockRippleInternal(BiometricSourceType.FACE) } } } @@ -134,29 +130,8 @@ constructor( keyguardStateController.addCallback(this) wakefulnessLifecycle.addObserver(this) commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() } - if (!DeviceEntryUdfpsRefactor.isEnabled) { - biometricUnlockController.addListener(biometricModeListener) - } } - private val biometricModeListener = - object : BiometricUnlockController.BiometricUnlockEventsListener { - override fun onBiometricUnlockedWithKeyguardDismissal( - biometricSourceType: BiometricSourceType? - ) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - if (biometricSourceType != null) { - showUnlockRippleInternal(biometricSourceType) - } else { - logger.log( - TAG, - LogLevel.ERROR, - "Unexpected scenario where biometricSourceType is null" - ) - } - } - } - @VisibleForTesting public override fun onViewDetached() { udfpsController?.removeCallback(udfpsControllerCallback) @@ -166,17 +141,10 @@ constructor( keyguardStateController.removeCallback(this) wakefulnessLifecycle.removeObserver(this) commandRegistry.unregisterCommand("auth-ripple") - biometricUnlockController.removeListener(biometricModeListener) notificationShadeWindowController.setForcePluginOpen(false, this) } - @Deprecated("Update authRippleInteractor.showUnlockRipple instead of calling this.") - fun showUnlockRipple(biometricSourceType: BiometricSourceType) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - showUnlockRippleInternal(biometricSourceType) - } - private fun showUnlockRippleInternal(biometricSourceType: BiometricSourceType) { val keyguardNotShowing = !keyguardStateController.isShowing val unlockNotAllowed = @@ -197,8 +165,8 @@ constructor( 0, Math.max( Math.max(it.x, displayMetrics.widthPixels - it.x), - Math.max(it.y, displayMetrics.heightPixels - it.y) - ) + Math.max(it.y, displayMetrics.heightPixels - it.y), + ), ) logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius") showUnlockedRipple() @@ -213,8 +181,8 @@ constructor( 0, Math.max( Math.max(it.x, displayMetrics.widthPixels - it.x), - Math.max(it.y, displayMetrics.heightPixels - it.y) - ) + Math.max(it.y, displayMetrics.heightPixels - it.y), + ), ) logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple") showUnlockedRipple() @@ -322,7 +290,7 @@ constructor( override fun onBiometricAuthenticated( userId: Int, biometricSourceType: BiometricSourceType, - isStrongBiometric: Boolean + isStrongBiometric: Boolean, ) { if (biometricSourceType == BiometricSourceType.FINGERPRINT) { mView.fadeDwellRipple() @@ -337,7 +305,7 @@ constructor( override fun onBiometricAcquired( biometricSourceType: BiometricSourceType, - acquireInfo: Int + acquireInfo: Int, ) { if ( biometricSourceType == BiometricSourceType.FINGERPRINT && diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt deleted file mode 100644 index 242601d46fa4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.content.Context -import android.util.AttributeSet - -/** - * Class that coordinates non-HBM animations during BiometricPrompt. - * - * Currently doesn't draw anything. - * - * Note that [AuthBiometricFingerprintViewController] also shows UDFPS animations. At some point we should - * de-dupe this if necessary. - */ -class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) { - - // Drawable isn't ever added to the view, so we don't currently show anything - private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context) - - override fun getDrawable(): UdfpsDrawable = fingerprintDrawable -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt deleted file mode 100644 index e0455b58b919..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager - -/** - * Class that coordinates non-HBM animations for biometric prompt. - */ -class UdfpsBpViewController( - view: UdfpsBpView, - statusBarStateController: StatusBarStateController, - shadeInteractor: ShadeInteractor, - systemUIDialogManager: SystemUIDialogManager, - dumpManager: DumpManager, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : UdfpsAnimationViewController<UdfpsBpView>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, -) { - override val tag = "UdfpsBpViewController" - - override fun shouldPauseAuth(): Boolean { - return false - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index a3904caa9dcc..2863e29c9a34 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -87,7 +87,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.ScreenLifecycle; @@ -98,7 +97,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.shared.system.SysUiStatsLog; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.SystemUIDialogManager; @@ -162,7 +160,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final FalsingManager mFalsingManager; @NonNull private final PowerManager mPowerManager; @NonNull private final AccessibilityManager mAccessibilityManager; - @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @NonNull private final ConfigurationController mConfigurationController; @NonNull private final SystemClock mSystemClock; @NonNull private final UnlockedScreenOffAnimationController @@ -283,7 +280,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mKeyguardUpdateMonitor, mDialogManager, mDumpManager, - mLockscreenShadeTransitionController, mConfigurationController, mKeyguardStateController, mUnlockedScreenOffAnimationController, @@ -291,10 +287,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { requestId, reason, callback, - (view, event, fromUdfpsView) -> onTouch( + (view, event) -> onTouch( requestId, - event, - fromUdfpsView + event ), mActivityTransitionAnimator, mPrimaryBouncerInteractor, @@ -374,9 +369,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mOverlay == null || mOverlay.isHiding()) { return; } - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message); - } }); } @@ -391,7 +383,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { */ public void debugOnTouch(MotionEvent event) { final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L; - UdfpsController.this.onTouch(requestId, event, true); + UdfpsController.this.onTouch(requestId, event); } /** @@ -449,22 +441,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mOverlayParams.equals(overlayParams)) { mOverlayParams = overlayParams; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) { - mOverlay.updateOverlayParams(mOverlayParams); - } else { - redrawOverlay(); - } + if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) { + mOverlay.updateOverlayParams(mOverlayParams); } else { - final boolean wasShowingAlternateBouncer = - mAlternateBouncerInteractor.isVisibleState(); - // When the bounds change it's always to re-create the overlay's window with new - // LayoutParams. If the overlay needs to be shown, this will re-create and show the - // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden. redrawOverlay(); - if (wasShowingAlternateBouncer) { - mKeyguardViewManager.showBouncer(true); - } } } } @@ -563,11 +543,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } } - private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { - if (!fromUdfpsView) { - Log.e(TAG, "ignoring the touch injected from outside of UdfpsView"); - return false; - } + private boolean onTouch(long requestId, @NonNull MotionEvent event) { if (mOverlay == null) { Log.w(TAG, "ignoring onTouch with null overlay"); return false; @@ -591,13 +567,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mIsAodInterruptActive) { mOnFingerDown = false; } - } else if (!DeviceEntryUdfpsRefactor.isEnabled()) { - if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f - && !mAlternateBouncerInteractor.isVisibleState()) - || mPrimaryBouncerInteractor.isInTransit()) { - Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor"); - return false; - } } final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId, @@ -661,22 +630,13 @@ public class UdfpsController implements DozeReceiver, Dumpable { mStatusBarStateController.isDozing()); break; - case UNCHANGED: - if (mActivePointerId == MotionEvent.INVALID_POINTER_ID - && mAlternateBouncerInteractor.isVisibleState()) { - // No pointer on sensor, forward to keyguard if alternateBouncer is visible - mKeyguardViewManager.onTouch(event); - } - default: break; } logBiometricTouch(processedTouch.getEvent(), data); // Always pilfer pointers that are within sensor area or when alternate bouncer is showing - if (mActivePointerId != MotionEvent.INVALID_POINTER_ID - || (mAlternateBouncerInteractor.isVisibleState() - && !DeviceEntryUdfpsRefactor.isEnabled())) { + if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) { shouldPilfer = true; } @@ -692,14 +652,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } private boolean shouldTryToDismissKeyguard() { - boolean onKeyguard = false; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - onKeyguard = mKeyguardStateController.isShowing(); - } else { - onKeyguard = mOverlay != null - && mOverlay.getAnimationViewController() - instanceof UdfpsKeyguardViewControllerLegacy; - } + boolean onKeyguard = mKeyguardStateController.isShowing(); return onKeyguard && mKeyguardStateController.canDismissLockScreen() && !mAttemptedToDismissKeyguard; @@ -719,7 +672,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, @NonNull AccessibilityManager accessibilityManager, - @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController, @NonNull ScreenLifecycle screenLifecycle, @NonNull VibratorHelper vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @@ -769,7 +721,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mFalsingManager = falsingManager; mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; - mLockscreenShadeTransitionController = lockscreenShadeTransitionController; screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mConfigurationController = configurationController; @@ -849,13 +800,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @Override public void dozeTimeTick() { - if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - final View view = mOverlay.getTouchOverlay(); - if (view != null) { - ((UdfpsView) view).dozeTimeTick(); - } - } + } private void redrawOverlay() { @@ -915,17 +860,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!isOptical()) { return; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mUdfpsDisplayMode != null) { - mUdfpsDisplayMode.disable(null); - } - } else { - if (view != null) { - UdfpsView udfpsView = (UdfpsView) view; - if (udfpsView.isDisplayConfigured()) { - udfpsView.unconfigureDisplay(); - } - } + if (mUdfpsDisplayMode != null) { + mUdfpsDisplayMode.disable(null); } } @@ -1118,11 +1054,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mIgnoreRefreshRate) { dispatchOnUiReady(requestId); } else { - if (DeviceEntryUdfpsRefactor.isEnabled()) { mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId)); - } else { - ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId)); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 1bac0bc26a94..a1efc196dbee 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -23,8 +23,6 @@ import android.graphics.PixelFormat import android.graphics.Rect import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR import android.hardware.biometrics.BiometricRequestConstants.RequestReason @@ -42,7 +40,6 @@ import android.view.View import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener -import androidx.annotation.LayoutRes import androidx.annotation.VisibleForTesting import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor @@ -56,7 +53,6 @@ import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlay import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState @@ -64,7 +60,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController @@ -102,7 +97,6 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val dialogManager: SystemUIDialogManager, private val dumpManager: DumpManager, - private val transitionController: LockscreenShadeTransitionController, private val configurationController: ConfigurationController, private val keyguardStateController: KeyguardStateController, private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, @@ -110,7 +104,7 @@ constructor( val requestId: Long, @RequestReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, - private val onTouch: (View, MotionEvent, Boolean) -> Boolean, + private val onTouch: (View, MotionEvent) -> Boolean, private val activityTransitionAnimator: ActivityTransitionAnimator, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, @@ -133,23 +127,15 @@ constructor( .map {} // map to Unit private var listenForCurrentKeyguardState: Job? = null private var addViewRunnable: Runnable? = null - private var overlayViewLegacy: UdfpsView? = null - private set - private var overlayTouchView: UdfpsTouchOverlay? = null /** - * Get the current UDFPS overlay touch view which is a different View depending on whether the - * DeviceEntryUdfpsRefactor flag is enabled or not. + * Get the current UDFPS overlay touch view * * @return The view, when [isShowing], else null */ fun getTouchOverlay(): View? { - return if (DeviceEntryUdfpsRefactor.isEnabled) { - overlayTouchView - } else { - overlayViewLegacy - } + return overlayTouchView } private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() @@ -161,7 +147,7 @@ constructor( WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 0 /* flags set in computeLayoutParams() */, - PixelFormat.TRANSLUCENT + PixelFormat.TRANSLUCENT, ) .apply { title = TAG @@ -188,10 +174,6 @@ constructor( val isHiding: Boolean get() = getTouchOverlay() == null - /** The animation controller if the overlay [isShowing]. */ - val animationViewController: UdfpsAnimationViewController<*>? - get() = overlayViewLegacy?.animationViewController - private var touchExplorationEnabled = false private fun shouldRemoveEnrollmentUi(): Boolean { @@ -199,7 +181,7 @@ constructor( return Settings.Global.getInt( context.contentResolver, SETTING_REMOVE_ENROLLMENT_UI, - 0 /* def */ + 0, /* def */ ) != 0 } return false @@ -212,63 +194,43 @@ constructor( overlayParams = params sensorBounds = Rect(params.sensorBounds) try { - if (DeviceEntryUdfpsRefactor.isEnabled) { - overlayTouchView = - (inflater.inflate(R.layout.udfps_touch_overlay, null, false) - as UdfpsTouchOverlay) - .apply { - // This view overlaps the sensor area - // prevent it from being selectable during a11y - if (requestReason.isImportantForAccessibility()) { - importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO - } - - addViewNowOrLater(this, null) - when (requestReason) { - REASON_AUTH_KEYGUARD -> - UdfpsTouchOverlayBinder.bind( - view = this, - viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(), - udfpsOverlayInteractor = udfpsOverlayInteractor, - ) - else -> - UdfpsTouchOverlayBinder.bind( - view = this, - viewModel = defaultUdfpsTouchOverlayViewModel.get(), - udfpsOverlayInteractor = udfpsOverlayInteractor, - ) - } - } - } else { - overlayViewLegacy = - (inflater.inflate(R.layout.udfps_view, null, false) as UdfpsView).apply { - overlayParams = params - setUdfpsDisplayModeProvider(udfpsDisplayModeProvider) - val animation = inflateUdfpsAnimation(this, controller) - if (animation != null) { - animation.init() - animationViewController = animation - } + overlayTouchView = + (inflater.inflate(R.layout.udfps_touch_overlay, null, false) + as UdfpsTouchOverlay) + .apply { // This view overlaps the sensor area // prevent it from being selectable during a11y if (requestReason.isImportantForAccessibility()) { importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO } - addViewNowOrLater(this, animation) - sensorRect = sensorBounds + addViewNowOrLater(this, null) + when (requestReason) { + REASON_AUTH_KEYGUARD -> + UdfpsTouchOverlayBinder.bind( + view = this, + viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(), + udfpsOverlayInteractor = udfpsOverlayInteractor, + ) + else -> + UdfpsTouchOverlayBinder.bind( + view = this, + viewModel = defaultUdfpsTouchOverlayViewModel.get(), + udfpsOverlayInteractor = udfpsOverlayInteractor, + ) + } } - } + getTouchOverlay()?.apply { touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled overlayTouchListener = TouchExplorationStateChangeListener { if (accessibilityManager.isTouchExplorationEnabled) { - setOnHoverListener { v, event -> onTouch(v, event, true) } + setOnHoverListener { v, event -> onTouch(v, event) } setOnTouchListener(null) touchExplorationEnabled = true } else { setOnHoverListener(null) - setOnTouchListener { v, event -> onTouch(v, event, true) } + setOnTouchListener { v, event -> onTouch(v, event) } touchExplorationEnabled = false } } @@ -312,7 +274,6 @@ constructor( } fun updateOverlayParams(updatedOverlayParams: UdfpsOverlayParams) { - DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() overlayParams = updatedOverlayParams sensorBounds = updatedOverlayParams.sensorBounds getTouchOverlay()?.let { @@ -326,108 +287,11 @@ constructor( } } - fun inflateUdfpsAnimation( - view: UdfpsView, - controller: UdfpsController - ): UdfpsAnimationViewController<*>? { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - - val isEnrollment = - when (requestReason) { - REASON_ENROLL_FIND_SENSOR, - REASON_ENROLL_ENROLLING -> true - else -> false - } - - val filteredRequestReason = - if (isEnrollment && shouldRemoveEnrollmentUi()) { - REASON_AUTH_OTHER - } else { - requestReason - } - - return when (filteredRequestReason) { - REASON_ENROLL_FIND_SENSOR, - REASON_ENROLL_ENROLLING -> { - // Enroll udfps UI is handled by settings, so use empty view here - UdfpsFpmEmptyViewController( - view.addUdfpsView(R.layout.udfps_fpm_empty_view) { - updateAccessibilityViewLocation(sensorBounds) - }, - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_KEYGUARD -> { - UdfpsKeyguardViewControllerLegacy( - view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) { - updateSensorLocation(sensorBounds) - }, - statusBarStateController, - statusBarKeyguardViewManager, - keyguardUpdateMonitor, - dumpManager, - transitionController, - configurationController, - keyguardStateController, - unlockedScreenOffAnimationController, - dialogManager, - controller, - activityTransitionAnimator, - primaryBouncerInteractor, - alternateBouncerInteractor, - udfpsKeyguardAccessibilityDelegate, - selectedUserInteractor, - transitionInteractor, - shadeInteractor, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_BP -> { - // note: empty controller, currently shows no visual affordance - UdfpsBpViewController( - view.addUdfpsView(R.layout.udfps_bp_view), - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_OTHER, - REASON_AUTH_SETTINGS -> { - UdfpsFpmEmptyViewController( - view.addUdfpsView(R.layout.udfps_fpm_empty_view), - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - else -> { - Log.e(TAG, "Animation for reason $requestReason not supported yet") - null - } - } - } - /** Hide the overlay or return false and do nothing if it is already hidden. */ fun hide(): Boolean { val wasShowing = isShowing - overlayViewLegacy?.apply { - if (isDisplayConfigured) { - unconfigureDisplay() - } - animationViewController = null - } - if (DeviceEntryUdfpsRefactor.isEnabled) { - udfpsDisplayModeProvider.disable(null) - } + udfpsDisplayModeProvider.disable(null) getTouchOverlay()?.apply { if (this.parent != null) { windowManager.removeView(this) @@ -440,7 +304,6 @@ constructor( } } - overlayViewLegacy = null overlayTouchView = null overlayTouchListener = null listenForCurrentKeyguardState?.cancel() @@ -490,7 +353,7 @@ constructor( Surface.rotationToString(rot) + " animation=$animation" + " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" + - " isOccluded=${keyguardStateController.isOccluded}" + " isOccluded=${keyguardStateController.isOccluded}", ) } else { Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot)) @@ -498,14 +361,14 @@ constructor( rotatedBounds, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight, - rot + rot, ) RotationUtils.rotateBounds( sensorBounds, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight, - rot + rot, ) } } @@ -519,14 +382,7 @@ constructor( } private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean { - val keyguardNotShowing = - if (DeviceEntryUdfpsRefactor.isEnabled) { - !keyguardStateController.isShowing - } else { - animation !is UdfpsKeyguardViewControllerLegacy - } - - if (keyguardNotShowing) { + if (!keyguardStateController.isShowing) { // always rotate view if we're not on the keyguard return true } @@ -534,16 +390,6 @@ constructor( // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded return !(keyguardUpdateMonitor.isGoingToSleep || !keyguardStateController.isOccluded) } - - private inline fun <reified T : View> UdfpsView.addUdfpsView( - @LayoutRes id: Int, - init: T.() -> Unit = {} - ): T { - val subView = inflater.inflate(id, null) as T - addView(subView) - subView.init() - return subView - } } @RequestReason diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt deleted file mode 100644 index 0838855f4756..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.content.Context -import android.graphics.Rect -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import com.android.systemui.res.R - -/** - * View corresponding with udfps_fpm_empty_view.xml - * - * Currently doesn't draw anything. - */ -class UdfpsFpmEmptyView( - context: Context, - attrs: AttributeSet? -) : UdfpsAnimationView(context, attrs) { - - // Drawable isn't ever added to the view, so we don't currently show anything - private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context) - - override fun getDrawable(): UdfpsDrawable = fingerprintDrawable - - fun updateAccessibilityViewLocation(sensorBounds: Rect) { - val fingerprintAccessibilityView: View = - requireViewById(R.id.udfps_enroll_accessibility_view) - val params: ViewGroup.LayoutParams = fingerprintAccessibilityView.layoutParams - params.width = sensorBounds.width() - params.height = sensorBounds.height() - fingerprintAccessibilityView.layoutParams = params - fingerprintAccessibilityView.requestLayout() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt deleted file mode 100644 index cfbbc26800d2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager - -/** - * Class that coordinates non-HBM animations for non keyguard, or biometric prompt states. - * - * Currently doesn't draw anything. - */ -class UdfpsFpmEmptyViewController( - view: UdfpsFpmEmptyView, - statusBarStateController: StatusBarStateController, - shadeInteractor: ShadeInteractor, - systemUIDialogManager: SystemUIDialogManager, - dumpManager: DumpManager, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : UdfpsAnimationViewController<UdfpsFpmEmptyView>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, -) { - override val tag = "UdfpsFpmOtherViewController" -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt deleted file mode 100644 index c3d9240c40a1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.content.res.Configuration -import android.util.MathUtils -import android.view.View -import androidx.annotation.VisibleForTesting -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle -import com.android.app.animation.Interpolators -import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.animation.ActivityTransitionAnimator -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -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.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.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 -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI -import com.android.systemui.statusbar.phone.SystemUIDialogManager -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.user.domain.interactor.SelectedUserInteractor -import java.io.PrintWriter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch - -/** Class that coordinates non-HBM animations during keyguard authentication. */ -@ExperimentalCoroutinesApi -open class UdfpsKeyguardViewControllerLegacy( - private val view: UdfpsKeyguardViewLegacy, - statusBarStateController: StatusBarStateController, - private val keyguardViewManager: StatusBarKeyguardViewManager, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - dumpManager: DumpManager, - private val lockScreenShadeTransitionController: LockscreenShadeTransitionController, - private val configurationController: ConfigurationController, - private val keyguardStateController: KeyguardStateController, - private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, - systemUIDialogManager: SystemUIDialogManager, - private val udfpsController: UdfpsController, - private val activityTransitionAnimator: ActivityTransitionAnimator, - private val primaryBouncerInteractor: PrimaryBouncerInteractor, - private val alternateBouncerInteractor: AlternateBouncerInteractor, - private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate, - private val selectedUserInteractor: SelectedUserInteractor, - private val transitionInteractor: KeyguardTransitionInteractor, - shadeInteractor: ShadeInteractor, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : - UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, - ) { - private val uniqueIdentifier = this.toString() - private var showingUdfpsBouncer = false - private var udfpsRequested = false - private var qsExpansion = 0f - private var faceDetectRunning = false - private var statusBarState = 0 - private var transitionToFullShadeProgress = 0f - private var lastDozeAmount = 0f - private var panelExpansionFraction = 0f - private var launchTransitionFadingAway = false - private var isLaunchingActivity = false - private var activityLaunchProgress = 0f - private var inputBouncerExpansion = 0f - - private val stateListener: StatusBarStateController.StateListener = - object : StatusBarStateController.StateListener { - override fun onStateChanged(statusBarState: Int) { - this@UdfpsKeyguardViewControllerLegacy.statusBarState = statusBarState - updateAlpha() - updatePauseAuth() - } - } - - private val configurationListener: ConfigurationController.ConfigurationListener = - object : ConfigurationController.ConfigurationListener { - override fun onUiModeChanged() { - view.updateColor() - } - - override fun onThemeChanged() { - view.updateColor() - } - - override fun onConfigChanged(newConfig: Configuration) { - updateScaleFactor() - view.updatePadding() - view.updateColor() - } - } - - private val keyguardStateControllerCallback: KeyguardStateController.Callback = - object : KeyguardStateController.Callback { - override fun onUnlockedChanged() { - updatePauseAuth() - } - - override fun onLaunchTransitionFadingAwayChanged() { - launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway - updatePauseAuth() - } - } - - private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener = - object : ActivityTransitionAnimator.Listener { - override fun onTransitionAnimationStart() { - isLaunchingActivity = true - activityLaunchProgress = 0f - updateAlpha() - } - - override fun onTransitionAnimationEnd() { - isLaunchingActivity = false - updateAlpha() - } - - override fun onTransitionAnimationProgress(linearProgress: Float) { - activityLaunchProgress = linearProgress - updateAlpha() - } - } - - private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback = - object : KeyguardViewManagerCallback { - override fun onQSExpansionChanged(qsExpansion: Float) { - this@UdfpsKeyguardViewControllerLegacy.qsExpansion = qsExpansion - updateAlpha() - updatePauseAuth() - } - } - - private val occludingAppBiometricUI: OccludingAppBiometricUI = - object : OccludingAppBiometricUI { - override fun requestUdfps(request: Boolean, color: Int) { - udfpsRequested = request - view.requestUdfps(request, color) - updateAlpha() - updatePauseAuth() - } - - override fun dump(pw: PrintWriter) { - pw.println(tag) - } - } - - override val tag: String - get() = TAG - - override fun onInit() { - super.onInit() - keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) - } - - init { - com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode() - view.repeatWhenAttached { - // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion - // can make the view not visible; and we still want to listen for events - // that may make the view visible again. - repeatOnLifecycle(Lifecycle.State.CREATED) { - listenForBouncerExpansion(this) - listenForAlternateBouncerVisibility(this) - listenForOccludedToAodTransition(this) - listenForGoneToAodTransition(this) - listenForLockscreenAodTransitions(this) - listenForAodToOccludedTransitions(this) - listenForAlternateBouncerToAodTransitions(this) - listenForDreamingToAodTransitions(this) - listenForPrimaryBouncerToAodTransitions(this) - } - } - } - - @VisibleForTesting - suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor - .transition( - edge = Edge.create(Scenes.Bouncer, AOD), - edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD) - ) - .collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - 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(Edge.create(AOD, OCCLUDED)).collect { transitionStep -> - view.onDozeAmountChanged( - 1f - transitionStep.value, - 1f - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_NONE, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job { - return scope.launch { - 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, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transitionValue(AOD).collect { - view.onDozeAmountChanged( - it, - it, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job { - return scope.launch { - primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float -> - inputBouncerExpansion = bouncerExpansion - - panelExpansionFraction = - if (keyguardViewManager.isPrimaryBouncerInTransit) { - aboutToShowBouncerProgress(1f - bouncerExpansion) - } else { - 1f - bouncerExpansion - } - updateAlpha() - updatePauseAuth() - } - } - } - - @VisibleForTesting - suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job { - return scope.launch { - alternateBouncerInteractor.isVisible.collect { isVisible: Boolean -> - showUdfpsBouncer(isVisible) - } - } - } - - public override fun onViewAttached() { - super.onViewAttached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier) - val dozeAmount = statusBarStateController.dozeAmount - lastDozeAmount = dozeAmount - stateListener.onDozeAmountChanged(dozeAmount, dozeAmount) - statusBarStateController.addCallback(stateListener) - udfpsRequested = false - launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway - keyguardStateController.addCallback(keyguardStateControllerCallback) - statusBarState = statusBarStateController.state - qsExpansion = keyguardViewManager.qsExpansion - keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback) - configurationController.addCallback(configurationListener) - updateScaleFactor() - view.updatePadding() - updateAlpha() - updatePauseAuth() - keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) - lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this - activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener) - view.startIconAsyncInflate { - val animationViewInternal: View = - view.requireViewById(R.id.udfps_animation_view_internal) - animationViewInternal.accessibilityDelegate = udfpsKeyguardAccessibilityDelegate - } - } - - public override fun onViewDetached() { - super.onViewDetached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier) - faceDetectRunning = false - keyguardStateController.removeCallback(keyguardStateControllerCallback) - statusBarStateController.removeCallback(stateListener) - keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI) - configurationController.removeCallback(configurationListener) - if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) { - lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null - } - activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener) - keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback) - } - - override fun dump(pw: PrintWriter, args: Array<String>) { - super.dump(pw, args) - pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer") - pw.println( - "altBouncerInteractor#isAlternateBouncerVisible=" + - "${alternateBouncerInteractor.isVisibleState()}" - ) - pw.println( - "altBouncerInteractor#canShowAlternateBouncerForFingerprint=" + - "${alternateBouncerInteractor.canShowAlternateBouncerForFingerprint()}" - ) - pw.println("faceDetectRunning=$faceDetectRunning") - pw.println("statusBarState=" + StatusBarState.toString(statusBarState)) - pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress") - pw.println("qsExpansion=$qsExpansion") - pw.println("panelExpansionFraction=$panelExpansionFraction") - pw.println("unpausedAlpha=" + view.unpausedAlpha) - pw.println("udfpsRequestedByApp=$udfpsRequested") - pw.println("launchTransitionFadingAway=$launchTransitionFadingAway") - pw.println("lastDozeAmount=$lastDozeAmount") - pw.println("inputBouncerExpansion=$inputBouncerExpansion") - view.dump(pw) - } - - /** - * Overrides non-bouncer show logic in shouldPauseAuth to still show icon. - * - * @return whether the udfpsBouncer has been newly shown or hidden - */ - private fun showUdfpsBouncer(show: Boolean): Boolean { - if (showingUdfpsBouncer == show) { - return false - } - val udfpsAffordanceWasNotShowing = shouldPauseAuth() - showingUdfpsBouncer = show - if (showingUdfpsBouncer) { - if (udfpsAffordanceWasNotShowing) { - view.animateInUdfpsBouncer(null) - } - view.announceForAccessibility( - view.context.getString(R.string.accessibility_fingerprint_bouncer) - ) - } - updateAlpha() - updatePauseAuth() - return true - } - - /** - * Returns true if the fingerprint manager is running but we want to temporarily pause - * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so - * this can be overridden with the showBouncer method. - */ - override fun shouldPauseAuth(): Boolean { - if (showingUdfpsBouncer) { - return false - } - if ( - udfpsRequested && - !notificationShadeVisible && - !isInputBouncerFullyVisible() && - keyguardStateController.isShowing - ) { - return false - } - if (launchTransitionFadingAway) { - return true - } - - // Only pause auth if we're not on the keyguard AND we're not transitioning to doze. - // For the UnlockedScreenOffAnimation, the statusBarState is - // delayed. However, we still animate in the UDFPS affordance with the - // unlockedScreenOffDozeAnimator. - if ( - statusBarState != StatusBarState.KEYGUARD && - !unlockedScreenOffAnimationController.isAnimationPlaying() - ) { - return true - } - if (isBouncerExpansionGreaterThan(.5f)) { - return true - } - if ( - keyguardUpdateMonitor.getUserUnlockedWithBiometric( - selectedUserInteractor.getSelectedUserId() - ) - ) { - // If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid - // overlap with the LockIconView. Shortly afterwards, UDFPS will stop running. - return true - } - return view.unpausedAlpha < 255 * .1 - } - - fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean { - return inputBouncerExpansion >= bouncerExpansionThreshold - } - - fun isInputBouncerFullyVisible(): Boolean { - return inputBouncerExpansion == 1f - } - - override fun listenForTouchesOutsideView(): Boolean { - return true - } - - /** - * Set the progress we're currently transitioning to the full shade. 0.0f means we're not - * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down - * to expand the notification shade from the empty space in the middle of the lock screen. - */ - fun setTransitionToFullShadeProgress(progress: Float) { - transitionToFullShadeProgress = progress - updateAlpha() - } - - /** - * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is - * based on the doze amount. - */ - override fun updateAlpha() { - // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested, - // then the keyguard is occluded by some application - so instead use the input bouncer - // hidden amount to determine the fade. - val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction - var alpha: Int = - if (showingUdfpsBouncer) 255 - else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt() - if (!showingUdfpsBouncer) { - // swipe from top of the lockscreen to expand full QS: - alpha = - (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion))) - .toInt() - - // swipe from the middle (empty space) of lockscreen to expand the notification shade: - alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt() - - // Fade out the icon if we are animating an activity launch over the lockscreen and the - // activity didn't request the UDFPS. - if (isLaunchingActivity && !udfpsRequested) { - val udfpsActivityLaunchAlphaMultiplier = - 1f - - (activityLaunchProgress * - (ActivityTransitionAnimator.TIMINGS.totalDuration / 83)) - .coerceIn(0f, 1f) - alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt() - } - - // Fade out alpha when a dialog is shown - // Fade in alpha when a dialog is hidden - alpha = (alpha * view.dialogSuggestedAlpha).toInt() - } - view.unpausedAlpha = alpha - } - - private fun getInputBouncerHiddenAmt(): Float { - return 1f - inputBouncerExpansion - } - - /** Update the scale factor based on the device's resolution. */ - 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/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java deleted file mode 100644 index 6d4eea852d26..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.MathUtils; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; - -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.asynclayoutinflater.view.AsyncLayoutInflater; - -import com.android.app.animation.Interpolators; -import com.android.settingslib.Utils; -import com.android.systemui.res.R; - -import com.airbnb.lottie.LottieAnimationView; -import com.airbnb.lottie.LottieProperty; -import com.airbnb.lottie.model.KeyPath; - -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * View corresponding with udfps_keyguard_view_legacy.xml - */ -public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { - private UdfpsDrawable mFingerprintDrawable; // placeholder - private LottieAnimationView mAodFp; - private LottieAnimationView mLockScreenFp; - - // used when highlighting fp icon: - private int mTextColorPrimary; - private ImageView mBgProtection; - boolean mUdfpsRequested; - - private AnimatorSet mBackgroundInAnimator = new AnimatorSet(); - private int mAlpha; // 0-255 - private float mScaleFactor = 1; - private Rect mSensorBounds = new Rect(); - - // AOD anti-burn-in offsets - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; - private float mInterpolatedDarkAmount; - private int mAnimationType = ANIMATION_NONE; - private boolean mFullyInflated; - private Runnable mOnFinishInflateRunnable; - - public UdfpsKeyguardViewLegacy(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - mFingerprintDrawable = new UdfpsFpDrawable(context); - - mMaxBurnInOffsetX = context.getResources() - .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); - mMaxBurnInOffsetY = context.getResources() - .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); - } - - /** - * Inflate internal udfps view on a background thread and call the onFinishRunnable - * when inflation is finished. - */ - public void startIconAsyncInflate(Runnable onFinishInflate) { - mOnFinishInflateRunnable = onFinishInflate; - // inflate Lottie views on a background thread in case it takes a while to inflate - AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext); - inflater.inflate(R.layout.udfps_keyguard_view_internal, this, - mLayoutInflaterFinishListener); - } - - @Override - public UdfpsDrawable getDrawable() { - return mFingerprintDrawable; - } - - @Override - void onSensorRectUpdated(RectF bounds) { - super.onSensorRectUpdated(bounds); - bounds.round(this.mSensorBounds); - postInvalidate(); - } - - @Override - void onDisplayConfiguring() { - } - - @Override - void onDisplayUnconfigured() { - } - - @Override - public boolean dozeTimeTick() { - updateBurnInOffsets(); - return true; - } - - private void updateBurnInOffsets() { - if (!mFullyInflated) { - return; - } - - // if we're animating from screen off, we can immediately place the icon in the - // AoD-burn in location, else we need to translate the icon from LS => AoD. - final float darkAmountForAnimation = mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF - ? 1f : mInterpolatedDarkAmount; - final float burnInOffsetX = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) - - mMaxBurnInOffsetX, darkAmountForAnimation); - final float burnInOffsetY = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - - mMaxBurnInOffsetY, darkAmountForAnimation); - final float burnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), - darkAmountForAnimation); - - if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) { - mLockScreenFp.setTranslationX(burnInOffsetX); - mLockScreenFp.setTranslationY(burnInOffsetY); - mBgProtection.setAlpha(1f - mInterpolatedDarkAmount); - mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount); - } else if (darkAmountForAnimation == 0f) { - // we're on the lockscreen and should use mAlpha (changes based on shade expansion) - mLockScreenFp.setTranslationX(0); - mLockScreenFp.setTranslationY(0); - mBgProtection.setAlpha(mAlpha / 255f); - mLockScreenFp.setAlpha(mAlpha / 255f); - } else { - mBgProtection.setAlpha(0f); - mLockScreenFp.setAlpha(0f); - } - mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount); - - mAodFp.setTranslationX(burnInOffsetX); - mAodFp.setTranslationY(burnInOffsetY); - mAodFp.setProgress(burnInProgress); - mAodFp.setAlpha(mInterpolatedDarkAmount); - - // done animating - final boolean doneAnimatingBetweenAodAndLS = - mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN - && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f); - final boolean doneAnimatingUnlockedScreenOff = - mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF - && (mInterpolatedDarkAmount == 1f); - if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) { - mAnimationType = ANIMATION_NONE; - } - } - - void requestUdfps(boolean request, int color) { - mUdfpsRequested = request; - } - - void updateColor() { - if (!mFullyInflated) { - return; - } - - mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, - com.android.internal.R.attr.materialColorOnSurface); - final int backgroundColor = Utils.getColorAttrDefaultColor(getContext(), - com.android.internal.R.attr.materialColorSurfaceContainerHigh); - mBgProtection.setImageTintList(ColorStateList.valueOf(backgroundColor)); - mLockScreenFp.invalidate(); // updated with a valueCallback - } - - void setScaleFactor(float scale) { - mScaleFactor = scale; - } - - void updatePadding() { - if (mLockScreenFp == null || mAodFp == null) { - return; - } - - final int defaultPaddingPx = - getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); - final int padding = (int) (defaultPaddingPx * mScaleFactor); - mLockScreenFp.setPadding(padding, padding, padding, padding); - mAodFp.setPadding(padding, padding, padding, padding); - } - - /** - * @param alpha between 0 and 255 - */ - void setUnpausedAlpha(int alpha) { - mAlpha = alpha; - updateAlpha(); - } - - /** - * @return alpha between 0 and 255 - */ - int getUnpausedAlpha() { - return mAlpha; - } - - @Override - protected int updateAlpha() { - int alpha = super.updateAlpha(); - updateBurnInOffsets(); - return alpha; - } - - @Override - int calculateAlpha() { - if (mPauseAuth) { - return 0; - } - return mAlpha; - } - - static final int ANIMATION_NONE = 0; - static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1; - static final int ANIMATE_APPEAR_ON_SCREEN_OFF = 2; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATE_APPEAR_ON_SCREEN_OFF}) - private @interface AnimationType {} - - void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) { - mAnimationType = animationType; - mInterpolatedDarkAmount = eased; - updateAlpha(); - } - - void updateSensorLocation(@NonNull Rect sensorBounds) { - mSensorBounds.set(sensorBounds); - } - - /** - * Animates in the bg protection circle behind the fp icon to highlight the icon. - */ - void animateInUdfpsBouncer(Runnable onEndAnimation) { - if (mBackgroundInAnimator.isRunning() || !mFullyInflated) { - // already animating in or not yet inflated - return; - } - - // fade in and scale up - mBackgroundInAnimator = new AnimatorSet(); - mBackgroundInAnimator.playTogether( - ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), - ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), - ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f)); - mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mBackgroundInAnimator.setDuration(500); - mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (onEndAnimation != null) { - onEndAnimation.run(); - } - } - }); - mBackgroundInAnimator.start(); - } - - /** - * Print debugging information for this class. - */ - public void dump(PrintWriter pw) { - pw.println("UdfpsKeyguardView (" + this + ")"); - pw.println(" mPauseAuth=" + mPauseAuth); - pw.println(" mUnpausedAlpha=" + getUnpausedAlpha()); - pw.println(" mUdfpsRequested=" + mUdfpsRequested); - pw.println(" mInterpolatedDarkAmount=" + mInterpolatedDarkAmount); - pw.println(" mAnimationType=" + mAnimationType); - } - - private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener = - new AsyncLayoutInflater.OnInflateFinishedListener() { - @Override - public void onInflateFinished(View view, int resid, ViewGroup parent) { - mFullyInflated = true; - mAodFp = view.findViewById(R.id.udfps_aod_fp); - mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp); - mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg); - - updatePadding(); - updateColor(); - updateAlpha(); - - final LayoutParams lp = (LayoutParams) view.getLayoutParams(); - lp.width = mSensorBounds.width(); - lp.height = mSensorBounds.height(); - RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds)); - lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top, - (int) relativeToView.right, (int) relativeToView.bottom); - parent.addView(view, lp); - - // requires call to invalidate to update the color - mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER, - frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, - PorterDuff.Mode.SRC_ATOP)); - mOnFinishInflateRunnable.run(); - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt deleted file mode 100644 index 76bcd6e2863b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.content.Context -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.Rect -import android.graphics.RectF -import android.util.AttributeSet -import android.util.Log -import android.view.MotionEvent -import android.widget.FrameLayout -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams -import com.android.systemui.doze.DozeReceiver - -private const val TAG = "UdfpsView" - -/** - * The main view group containing all UDFPS animations. - */ -class UdfpsView( - context: Context, - attrs: AttributeSet? -) : FrameLayout(context, attrs), DozeReceiver { - // sensorRect may be bigger than the sensor. True sensor dimensions are defined in - // overlayParams.sensorBounds - var sensorRect = Rect() - private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null - private val debugTextPaint = Paint().apply { - isAntiAlias = true - color = Color.BLUE - textSize = 32f - } - - /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */ - var animationViewController: UdfpsAnimationViewController<*>? = null - - /** Parameters that affect the position and size of the overlay. */ - var overlayParams = UdfpsOverlayParams() - - /** Debug message. */ - var debugMessage: String? = null - set(value) { - field = value - postInvalidate() - } - - /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */ - var isDisplayConfigured: Boolean = false - private set - - fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) { - mUdfpsDisplayMode = udfpsDisplayModeProvider - } - - // Don't propagate any touch events to the child views. - override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { - return (animationViewController == null || !animationViewController!!.shouldPauseAuth()) - } - - override fun dozeTimeTick() { - animationViewController?.dozeTimeTick() - } - - override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { - super.onLayout(changed, left, top, right, bottom) - - // Updates sensor rect in relation to the overlay view - animationViewController?.onSensorRectUpdated(RectF(sensorRect)) - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - Log.v(TAG, "onAttachedToWindow") - } - - override fun onDetachedFromWindow() { - super.onDetachedFromWindow() - Log.v(TAG, "onDetachedFromWindow") - } - - override fun onDraw(canvas: Canvas) { - super.onDraw(canvas) - if (!isDisplayConfigured) { - if (!debugMessage.isNullOrEmpty()) { - canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint) - } - } - } - - fun configureDisplay(onDisplayConfigured: Runnable) { - isDisplayConfigured = true - animationViewController?.onDisplayConfiguring() - mUdfpsDisplayMode?.enable(onDisplayConfigured) - } - - fun unconfigureDisplay() { - isDisplayConfigured = false - animationViewController?.onDisplayUnconfigured() - mUdfpsDisplayMode?.disable(null /* onDisabled */) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt index 7503a8b4362d..a105d663424c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt @@ -23,7 +23,6 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.lifecycle.repeatWhenAttached import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -42,14 +41,13 @@ object UdfpsTouchOverlayBinder { viewModel: UdfpsTouchOverlayViewModel, udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { launch { viewModel.shouldHandleTouches.collect { shouldHandleTouches -> Log.d( "UdfpsTouchOverlayBinder", - "[$view]: update shouldHandleTouches=$shouldHandleTouches" + "[$view]: update shouldHandleTouches=$shouldHandleTouches", ) view.isInvisible = !shouldHandleTouches udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches) @@ -58,7 +56,7 @@ object UdfpsTouchOverlayBinder { .invokeOnCompletion { Log.d( "UdfpsTouchOverlayBinder", - "[$view-detached]: update shouldHandleTouches=false" + "[$view-detached]: update shouldHandleTouches=false", ) udfpsOverlayInteractor.setHandleTouches(false) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt index f1c3f949ffba..22b2888a51a9 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt @@ -24,7 +24,6 @@ import com.android.systemui.bouncer.shared.model.BouncerDismissActionModel import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.log.dagger.BouncerTableLog import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable @@ -88,7 +87,6 @@ interface KeyguardBouncerRepository { val showMessage: StateFlow<BouncerShowMessageModel?> val resourceUpdateRequests: StateFlow<Boolean> val alternateBouncerVisible: StateFlow<Boolean> - val alternateBouncerUIAvailable: StateFlow<Boolean> /** Last shown security mode of the primary bouncer (ie: pin/pattern/password/SIM) */ val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode> @@ -126,8 +124,6 @@ interface KeyguardBouncerRepository { fun setAlternateVisible(isVisible: Boolean) - fun setAlternateBouncerUIAvailable(isAvailable: Boolean) - fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode) } @@ -199,9 +195,6 @@ constructor( private val _alternateBouncerVisible = MutableStateFlow(false) override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow() override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE - private val _alternateBouncerUIAvailable = MutableStateFlow(false) - override val alternateBouncerUIAvailable: StateFlow<Boolean> = - _alternateBouncerUIAvailable.asStateFlow() init { setUpLogging() @@ -220,11 +213,6 @@ constructor( _alternateBouncerVisible.value = isVisible } - override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - _alternateBouncerUIAvailable.value = isAvailable - } - override fun setPrimaryShow(isShowing: Boolean) { _primaryBouncerShow.value = isShowing } @@ -320,9 +308,6 @@ constructor( resourceUpdateRequests .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false) .launchIn(applicationScope) - alternateBouncerUIAvailable - .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false) - .launchIn(applicationScope) alternateBouncerVisible .logDiffsForTable(buffer, "", "AlternateBouncerVisible", false) .launchIn(applicationScope) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index 0949ea4d7797..9c2a10a444e2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -17,22 +17,17 @@ package com.android.systemui.bouncer.domain.interactor import android.util.Log -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.plugins.statusbar.StatusBarStateController 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.statusbar.policy.KeyguardStateController import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf import com.android.systemui.util.time.SystemClock import dagger.Lazy @@ -55,13 +50,9 @@ import kotlinx.coroutines.flow.stateIn class AlternateBouncerInteractor @Inject constructor( - private val statusBarStateController: StatusBarStateController, - private val keyguardStateController: KeyguardStateController, private val bouncerRepository: KeyguardBouncerRepository, fingerprintPropertyRepository: FingerprintPropertyRepository, - private val biometricSettingsRepository: BiometricSettingsRepository, private val systemClock: SystemClock, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val deviceEntryBiometricsAllowedInteractor: Lazy<DeviceEntryBiometricsAllowedInteractor>, private val keyguardInteractor: Lazy<KeyguardInteractor>, @@ -73,17 +64,10 @@ constructor( val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet() val alternateBouncerSupported: StateFlow<Boolean> = - if (DeviceEntryUdfpsRefactor.isEnabled) { - fingerprintPropertyRepository.sensorType - .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() } - .stateIn( - scope = scope, - started = SharingStarted.Eagerly, - initialValue = false, - ) - } else { - bouncerRepository.alternateBouncerUIAvailable - } + fingerprintPropertyRepository.sensorType + .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() } + .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false) + private val isDozingOrAod: Flow<Boolean> = anyOf( keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map { @@ -106,7 +90,7 @@ constructor( combine( keyguardTransitionInteractor.get().currentKeyguardState, sceneInteractor.get().currentScene, - ::Pair + ::Pair, ) .flatMapLatest { (currentKeyguardState, transitionState) -> if (currentKeyguardState == KeyguardState.GONE) { @@ -122,7 +106,7 @@ constructor( .isFingerprintAuthCurrentlyAllowed, keyguardInteractor.get().isKeyguardDismissible, bouncerRepository.primaryBouncerShow, - isDozingOrAod + isDozingOrAod, ) { fingerprintAllowed, keyguardDismissible, @@ -141,37 +125,17 @@ constructor( } .distinctUntilChanged() .onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") } - .stateIn( - scope = scope, - started = WhileSubscribed(), - initialValue = false, - ) + .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false) /** * Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before * calling this. */ fun forceShow() { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { - show() - return - } bouncerRepository.setAlternateVisible(true) } /** - * Sets the correct bouncer states to show the alternate bouncer if it can show. - * - * @return whether alternateBouncer is visible - * @deprecated use [forceShow] and manually check [canShowAlternateBouncer] beforehand - */ - fun show(): Boolean { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint()) - return isVisibleState() - } - - /** * Sets the correct bouncer states to hide the bouncer. Should only be called through * StatusBarKeyguardViewManager until ScrimController is refactored to use * alternateBouncerInteractor. @@ -189,28 +153,8 @@ constructor( return bouncerRepository.alternateBouncerVisible.value } - fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - if (isAvailable) { - alternateBouncerUiAvailableFromSource.add(token) - } else { - alternateBouncerUiAvailableFromSource.remove(token) - } - bouncerRepository.setAlternateBouncerUIAvailable( - alternateBouncerUiAvailableFromSource.isNotEmpty() - ) - } - fun canShowAlternateBouncerForFingerprint(): Boolean { - if (DeviceEntryUdfpsRefactor.isEnabled) { - return canShowAlternateBouncer.value - } - return alternateBouncerSupported.value && - biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value && - !keyguardUpdateMonitor.isFingerprintLockedOut && - !keyguardStateController.isUnlocked && - !statusBarStateController.isDozing && - !bouncerRepository.primaryBouncerShow.value + return canShowAlternateBouncer.value } /** diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index 4185aed3095c..148b9ea61e2c 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.bouncer.ui.viewmodel import android.annotation.StringRes -import com.android.app.tracing.coroutines.flow.collectLatest import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor @@ -28,6 +27,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow sealed class AuthMethodBouncerViewModel( diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt index e3f1174d4a5f..a695163ed155 100644 --- a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt @@ -19,7 +19,7 @@ package com.android.systemui.common.usagestats.data.repository import android.app.usage.UsageEvents import android.app.usage.UsageEventsQuery import android.app.usage.UsageStatsManager -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.common.usagestats.data.model.UsageStatsQuery import com.android.systemui.common.usagestats.shared.model.ActivityEventModel import com.android.systemui.common.usagestats.shared.model.ActivityEventModel.Lifecycle diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 08a7c395e57f..8510915d55af 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal +import android.os.UserHandle import android.provider.Settings import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey @@ -147,9 +148,10 @@ constructor( .emitOnStart() .onEach { screenTimeout = - systemSettings.getInt( + systemSettings.getIntForUser( Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_TIMEOUT, + UserHandle.USER_CURRENT, ) } .launchIn(bgScope) diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 3a04d026ee84..dc248059b3d4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -23,7 +23,7 @@ import android.content.pm.UserInfo import android.os.UserHandle import android.os.UserManager import android.provider.Settings -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt index 3826fb40ea3d..428b83ddbb3a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt @@ -16,7 +16,7 @@ package com.android.systemui.communal.domain.interactor -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt index ba96f4e56421..71bfe0c057e4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt @@ -24,8 +24,7 @@ import android.view.ViewGroup import android.widget.FrameLayout import androidx.compose.ui.unit.IntSize import androidx.core.view.doOnLayout -import com.android.app.tracing.coroutines.flow.flowOn -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalWidgetResizing import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.communal.domain.model.CommunalContentModel @@ -41,6 +40,7 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn object CommunalAppWidgetHostViewBinder { private const val TAG = "CommunalAppWidgetHostViewBinder" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt index 87fcdd7b97ee..a519649a8f1c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.communal.ui.viewmodel import androidx.compose.foundation.gestures.AnchoredDraggableState import androidx.compose.foundation.gestures.DraggableAnchors import androidx.compose.runtime.snapshotFlow -import com.android.app.tracing.coroutines.coroutineScope +import com.android.app.tracing.coroutines.coroutineScopeTraced as coroutineScope import com.android.systemui.lifecycle.ExclusiveActivatable import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.Flow diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt index 07a7c7cba2fd..d5d3a927181b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt @@ -19,7 +19,7 @@ package com.android.systemui.communal.util import android.content.Context import android.os.Bundle import android.util.SizeF -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate import com.android.systemui.communal.widgets.CommunalAppWidgetHost diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt index 542b98896986..c894267a61dd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt @@ -21,7 +21,7 @@ import android.app.PendingIntent import android.content.Intent import android.view.View import android.widget.RemoteViews -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.Flags.communalWidgetTrampolineFix import com.android.systemui.animation.ActivityTransitionAnimator diff --git a/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt new file mode 100644 index 000000000000..5b1c9c8b8020 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.coroutines + +import com.android.app.tracing.coroutines.createCoroutineTracingContext +import kotlin.coroutines.CoroutineContext + +fun newTracingContext(name: String): CoroutineContext { + return createCoroutineTracingContext(name) { className -> + className.startsWith("com.android.systemui.util.kotlin.JavaAdapter") || + className.startsWith("com.android.systemui.lifecycle.RepeatWhenAttached") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt index b8c03c071572..c464a66ea0c3 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -17,25 +17,17 @@ package com.android.systemui.deviceentry import com.android.keyguard.EmptyLockIconViewController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.LockIconViewController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import dagger.Lazy import dagger.Module import dagger.Provides import dagger.multibindings.Multibinds -@Module( - includes = - [ - DeviceEntryRepositoryModule::class, - FaceWakeUpTriggersConfigModule::class, - ], -) +@Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class]) abstract class DeviceEntryModule { /** * A set of DeviceEntryIconTransitions. Ensures that this can be injected even if it's empty. @@ -46,14 +38,9 @@ abstract class DeviceEntryModule { @Provides @SysUISingleton fun provideLockIconViewController( - legacyLockIconViewController: Lazy<LegacyLockIconViewController>, - emptyLockIconViewController: Lazy<EmptyLockIconViewController>, + emptyLockIconViewController: Lazy<EmptyLockIconViewController> ): LockIconViewController { - return if (DeviceEntryUdfpsRefactor.isEnabled) { - emptyLockIconViewController.get() - } else { - legacyLockIconViewController.get() - } + return emptyLockIconViewController.get() } } } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt index ffe392a52c9f..88daa5de8816 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt @@ -21,7 +21,6 @@ import android.hardware.face.FaceManager import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus import javax.inject.Inject @@ -55,9 +54,7 @@ constructor( faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>() init { - if (DeviceEntryUdfpsRefactor.isEnabled) { - startUpdatingFaceHelpMessageDeferral() - } + startUpdatingFaceHelpMessageDeferral() } /** diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt deleted file mode 100644 index b5d5803ca6fb..000000000000 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.deviceentry.shared - -import com.android.systemui.Flags -import com.android.systemui.flags.FlagToken -import com.android.systemui.flags.RefactorFlagUtils - -/** Helper for reading or using the device entry udfps refactor flag state. */ -@Suppress("NOTHING_TO_INLINE") -object DeviceEntryUdfpsRefactor { - /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR - - /** A token used for dependency declaration */ - val token: FlagToken - get() = FlagToken(FLAG_NAME, isEnabled) - - /** Is the refactor enabled */ - @JvmStatic - inline val isEnabled - get() = Flags.deviceEntryUdfpsRefactor() - - /** - * 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/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt index 30624757ebe3..e3fce0007f26 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt @@ -17,8 +17,8 @@ package com.android.systemui.display.data.repository import android.view.Display -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.systemui.CoreStartable +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.core.StatusBarConnectedDisplays @@ -69,7 +69,7 @@ constructor( backgroundApplicationScope } else { CoroutineScope( - backgroundDispatcher + createCoroutineTracingContext("DisplayScope$displayId") + backgroundDispatcher + newTracingContext("DisplayScope$displayId") ) } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt index 3992c3fb70b0..724f1c5323cf 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt @@ -22,6 +22,7 @@ import android.service.controls.ControlsProviderService import android.service.dreams.DreamService import android.window.TaskFragmentInfo import com.android.systemui.controls.settings.ControlsSettingsRepository +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dreams.DreamLogger import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor @@ -39,7 +40,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import com.android.app.tracing.coroutines.createCoroutineTracingContext class HomeControlsDreamService @Inject @@ -54,7 +54,8 @@ constructor( ) : DreamService() { private val serviceJob = SupervisorJob() - private val serviceScope = CoroutineScope(bgDispatcher + serviceJob + createCoroutineTracingContext("HomeControlsDreamService")) + private val serviceScope = + CoroutineScope(bgDispatcher + serviceJob + newTracingContext("HomeControlsDreamService")) private val logger = DreamLogger(logBuffer, TAG) private lateinit var taskFragmentComponent: TaskFragmentComponent private val wakeLock: WakeLock by lazy { diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt index 4caf95b707b1..7fa7da192ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt +++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt @@ -16,10 +16,10 @@ package com.android.systemui.education.dagger -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.systemui.CoreStartable import com.android.systemui.Flags import com.android.systemui.contextualeducation.GestureType +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.data.repository.ContextualEducationRepository import com.android.systemui.education.data.repository.UserContextualEducationRepository @@ -57,7 +57,9 @@ interface ContextualEducationModule { fun provideEduDataStoreScope( @Background bgDispatcher: CoroutineDispatcher ): CoroutineScope { - return CoroutineScope(bgDispatcher + SupervisorJob() + createCoroutineTracingContext("EduDataStoreScope")) + return CoroutineScope( + bgDispatcher + SupervisorJob() + newTracingContext("EduDataStoreScope") + ) } @EduClock diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt index e396767ecf8e..1dbcb3dfe399 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt @@ -22,7 +22,7 @@ import androidx.compose.foundation.interaction.InteractionSource import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.util.VelocityTracker import androidx.compose.ui.unit.Velocity -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig import com.android.systemui.haptics.slider.SliderDragVelocityProvider import com.android.systemui.haptics.slider.SliderEventType diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index 6df8355550a0..a94df091230d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -30,7 +30,7 @@ import android.net.Uri import android.os.Binder import android.os.Bundle import android.util.Log -import com.android.app.tracing.coroutines.runBlocking +import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking import com.android.systemui.SystemUIAppComponentFactoryBase import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback import com.android.systemui.dagger.qualifiers.Main diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 690ae71aa56e..b7d0d453779f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -24,7 +24,7 @@ import android.annotation.SuppressLint import android.os.Trace import android.util.Log import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.shared.model.KeyguardState diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt index 96260770d89f..03cf1a4b3e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt @@ -23,15 +23,12 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.BiometricType import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.res.R 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 javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -41,7 +38,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch /** * Encapsulates business logic for device entry events that impact the side fingerprint sensor @@ -51,7 +47,6 @@ import kotlinx.coroutines.launch class DeviceEntrySideFpsOverlayInteractor @Inject constructor( - @Application private val applicationScope: CoroutineScope, @Application private val context: Context, deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, private val sceneInteractor: SceneInteractor, @@ -60,18 +55,6 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, ) { - init { - if (!DeviceEntryUdfpsRefactor.isEnabled) { - applicationScope.launch { - deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType -> - if (sensorType == BiometricType.SIDE_FINGERPRINT) { - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG) - } - } - } - } - } - private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) @@ -90,7 +73,7 @@ constructor( primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(), // Bouncer scene visibility changes. isBouncerSceneActive, - deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it } + deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }, ) .map { isBouncerActive() && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 4cf9ec8890d4..9896365abff9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import android.util.Log import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 9a0a85823929..7b757657b1d9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -20,7 +20,7 @@ import android.animation.ValueAnimator import android.annotation.SuppressLint import android.app.DreamManager import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 6b6a3dce630a..a6f0db5a86aa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 58c8a0456241..606a7a988970 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background 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 6d1d9cbd9aae..4d3727657f3e 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 @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import android.util.MathUtils import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 2c3b481b9e16..26bf26b34a8a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -22,7 +22,7 @@ import android.app.admin.DevicePolicyManager import android.content.Context import android.content.Intent import android.util.Log -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.widget.LockPatternUtils import com.android.keyguard.logging.KeyguardQuickAffordancesLogger diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt index fe5f632c0b6a..23b7b664d4f1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.AuthKeyguardMessageArea import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt index 7ca2c2004452..6ef9863f1112 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt @@ -21,7 +21,7 @@ import android.content.res.ColorStateList import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt index 1891af2d04b0..7a368999ecc9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt @@ -29,7 +29,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index a3f3342b9a6c..6c104a0a2470 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.view.LongPressHandlingView import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index 0470e086ad27..5bad0168fe05 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -22,7 +22,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.shared.model.KeyguardBlueprint diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 660a650fb916..3bdf7dac75b3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -36,7 +36,7 @@ import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.settingslib.Utils import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.Expandable diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt index ba94f45528c7..8b947a3bcb1e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt @@ -22,7 +22,7 @@ import android.view.ViewGroup import android.widget.TextView import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt index 76d3389c25b4..2fd9818a38f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt @@ -22,7 +22,7 @@ import android.view.accessibility.AccessibilityNodeInfo import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.view.LongPressHandlingView import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index 2d225562081a..21d1db4a5052 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -33,7 +33,7 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.policy.SystemBarUtils import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.shared.model.ClockSizeSetting @@ -129,15 +129,19 @@ object KeyguardPreviewClockViewBinder { constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0) val largeClockTopMargin = SystemBarUtils.getStatusBarHeight(context) + - context.resources.getDimensionPixelSize( - customR.dimen.small_clock_padding_top - ) + + context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) + context.resources.getDimensionPixelSize( R.dimen.keyguard_smartspace_top_offset ) + getDimen(context, DATE_WEATHER_VIEW_HEIGHT) + getDimen(context, ENHANCED_SMARTSPACE_HEIGHT) - connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) + connect( + customR.id.lockscreen_clock_view_large, + TOP, + PARENT_ID, + TOP, + largeClockTopMargin, + ) connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START) connect( customR.id.lockscreen_clock_view_large, @@ -146,12 +150,11 @@ object KeyguardPreviewClockViewBinder { ConstraintSet.END, ) - // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS // devices, but we need position of device entry icon to constrain clock if (getConstraint(lockId) != null) { connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP) - } else { + } else { // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection val bottomPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt index 4b75b804e52b..baa681282a0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt @@ -22,7 +22,7 @@ import android.view.View import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index 27dd18d2b24e..cfd6481234ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt @@ -30,7 +30,7 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.logging.KeyguardQuickAffordancesLogger import com.android.settingslib.Utils import com.android.systemui.animation.Expandable diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index ea70fd02eed5..89bca0c6a373 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -39,7 +39,7 @@ import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD import com.android.keyguard.AuthInteractionProperties diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt index 4150ceb8aa31..79360370d937 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt @@ -22,7 +22,7 @@ import android.view.View import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.common.ui.binder.TextViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 8b74f5dc791d..de4a1b03203c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -21,7 +21,7 @@ import androidx.constraintlayout.helper.widget.Layer import androidx.constraintlayout.widget.ConstraintLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt index fd27dc39299b..3934917d13c4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.binder -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel import kotlinx.coroutines.CoroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt index b2ee68967878..2df17c39d90d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.LightRevealScrim diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt index ae46dd3a6ef3..b1ce47ee79e6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.binder -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel import kotlinx.coroutines.CoroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index dd8980dabb23..08d35a72ab12 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -47,7 +47,6 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.core.view.isInvisible -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.internal.policy.SystemBarUtils import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch @@ -57,6 +56,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -189,7 +189,7 @@ constructor( CoroutineScope( applicationScope.coroutineContext + Job() + - createCoroutineTracingContext("KeyguardPreviewRenderer") + newTracingContext("KeyguardPreviewRenderer") ) disposables += DisposableHandle { coroutineScope.cancel() } clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData()) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index 075a1d2893f7..9355200daec7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -25,7 +25,7 @@ import android.os.Messenger import android.util.ArrayMap import android.util.Log import androidx.annotation.VisibleForTesting -import com.android.app.tracing.coroutines.runBlocking +import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index 0d55709e94d6..f765e60c1c70 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.annotation.VisibleForTesting import com.android.app.tracing.FlowTracing.traceEmissionCount +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor @@ -141,6 +142,7 @@ constructor( fadeInAlpha, fadeOutAlpha, ) + .flowName("transitionAlpha") .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt index df1394bbfa5f..7c02f28c0696 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt @@ -19,7 +19,7 @@ package com.android.systemui.lifecycle import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.StateFactoryMarker -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.app.tracing.coroutines.traceCoroutine import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt index 555969859a1f..a86bfb18fb70 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt @@ -17,7 +17,6 @@ package com.android.systemui.lifecycle -import android.os.Trace import android.view.View import android.view.ViewTreeObserver import androidx.annotation.MainThread @@ -25,11 +24,8 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.createCoroutineTracingContext -import com.android.app.tracing.coroutines.traceCoroutine -import com.android.systemui.Flags.coroutineTracing +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.util.Assert -import com.android.systemui.util.Compile import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import kotlin.coroutines.CoroutineContext @@ -83,25 +79,13 @@ fun View.repeatWhenAttached( // default behavior. Instead, we want it to run on the view's UI thread since the user will // presumably want to call view methods that require being called from said UI thread. val lifecycleCoroutineContext = MAIN_DISPATCHER_SINGLETON + coroutineContext - val traceName = - if (Compile.IS_DEBUG && coroutineTracing()) { - inferTraceSectionName() - } else { - DEFAULT_TRACE_NAME - } var lifecycleOwner: ViewLifecycleOwner? = null val onAttachListener = object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { Assert.isMainThread() lifecycleOwner?.onDestroy() - lifecycleOwner = - createLifecycleOwnerAndRun( - traceName, - view, - lifecycleCoroutineContext, - block, - ) + lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block) } override fun onViewDetachedFromWindow(v: View) { @@ -112,13 +96,7 @@ fun View.repeatWhenAttached( addOnAttachStateChangeListener(onAttachListener) if (view.isAttachedToWindow) { - lifecycleOwner = - createLifecycleOwnerAndRun( - traceName, - view, - lifecycleCoroutineContext, - block, - ) + lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block) } return DisposableHandle { @@ -131,14 +109,15 @@ fun View.repeatWhenAttached( } private fun createLifecycleOwnerAndRun( - nameForTrace: String, view: View, coroutineContext: CoroutineContext, block: suspend LifecycleOwner.(View) -> Unit, ): ViewLifecycleOwner { return ViewLifecycleOwner(view).apply { onCreate() - lifecycleScope.launch(coroutineContext) { traceCoroutine(nameForTrace) { block(view) } } + // TODO(b/370595466): Refactor to support installing CoroutineTracingContext on the + // top-level CoroutineScope used as the lifecycleScope + lifecycleScope.launch(coroutineContext) { block(view) } } } @@ -167,9 +146,7 @@ private fun createLifecycleOwnerAndRun( * └───────────────┴───────────────────┴──────────────┴─────────────────┘ * ``` */ -class ViewLifecycleOwner( - private val view: View, -) : LifecycleOwner { +class ViewLifecycleOwner(private val view: View) : LifecycleOwner { private val windowVisibleListener = ViewTreeObserver.OnWindowVisibilityChangeListener { updateState() } @@ -205,28 +182,6 @@ class ViewLifecycleOwner( } } -private fun isFrameInteresting(frame: StackWalker.StackFrame): Boolean = - frame.className != CURRENT_CLASS_NAME && frame.className != JAVA_ADAPTER_CLASS_NAME - -/** Get a name for the trace section include the name of the call site. */ -private fun inferTraceSectionName(): String { - try { - Trace.traceBegin(Trace.TRACE_TAG_APP, "RepeatWhenAttachedKt#inferTraceSectionName") - val interestingFrame = - StackWalker.getInstance().walk { stream -> - stream.filter(::isFrameInteresting).limit(5).findFirst() - } - return if (interestingFrame.isPresent) { - val f = interestingFrame.get() - "${f.className}#${f.methodName}:${f.lineNumber} [$DEFAULT_TRACE_NAME]" - } else { - DEFAULT_TRACE_NAME - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_APP) - } -} - /** * Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is * at least at [state] (or immediately after calling this function if the window is already at least @@ -368,8 +323,7 @@ private val ViewTreeObserver.isWindowVisible * an extension function, and plumbing dagger-injected instances for static usage has little * benefit. */ -private val MAIN_DISPATCHER_SINGLETON = - Dispatchers.Main + createCoroutineTracingContext("RepeatWhenAttached") +private val MAIN_DISPATCHER_SINGLETON = Dispatchers.Main + newTracingContext("RepeatWhenAttached") private const val DEFAULT_TRACE_NAME = "repeatWhenAttached" private const val CURRENT_CLASS_NAME = "com.android.systemui.lifecycle.RepeatWhenAttachedKt" private const val JAVA_ADAPTER_CLASS_NAME = "com.android.systemui.util.kotlin.JavaAdapterKt" diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt index 544dbddeb3f0..f40ad065e5fc 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt @@ -16,13 +16,13 @@ package com.android.systemui.mediaprojection.appselector -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.app.Activity import android.content.ComponentName import android.content.Context import android.os.UserHandle import androidx.lifecycle.DefaultLifecycleObserver import com.android.launcher3.icons.IconFactory +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader @@ -134,7 +134,11 @@ interface MediaProjectionAppSelectorModule { @MediaProjectionAppSelector @MediaProjectionAppSelectorScope fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope = - CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("MediaProjectionAppSelectorScope")) + CoroutineScope( + applicationScope.coroutineContext + + SupervisorJob() + + newTracingContext("MediaProjectionAppSelectorScope") + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index c3729c0dcdfd..212da9ffb9c5 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -122,8 +122,11 @@ public class MediaProjectionPermissionActivity extends Activity { final Intent launchingIntent = getIntent(); mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra( EXTRA_USER_REVIEW_GRANTED_CONSENT, false); - - mPackageName = getCallingPackage(); + if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) { + mPackageName = getLaunchedFromPackage(); + } else { + mPackageName = getCallingPackage(); + } // This activity is launched directly by an app, or system server. System server provides // the package name through the intent if so. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt index 96386e520d5a..0166176721c3 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt @@ -16,7 +16,6 @@ package com.android.systemui.navigationbar.gestural.domain -import com.android.app.tracing.coroutines.flow.flowOn import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -35,6 +34,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index 3aa9daac4866..d0f6f7961889 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -37,7 +37,7 @@ import android.os.UserManager import android.provider.Settings import android.widget.Toast import androidx.annotation.VisibleForTesting -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 9c5231d716da..49b44cb95e46 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -23,7 +23,9 @@ import android.graphics.Rect import android.os.Bundle import android.util.IndentingPrintWriter import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View +import android.view.ViewConfiguration import android.view.ViewGroup import android.widget.FrameLayout import androidx.activity.OnBackPressedDispatcher @@ -35,6 +37,7 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -43,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -83,6 +87,7 @@ import com.android.systemui.Dumpable import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.lifecycle.setSnapshotBinding import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule.QS_PANEL @@ -145,6 +150,7 @@ constructor( private val qqsVisible = MutableStateFlow(false) private val qqsPositionOnRoot = Rect() private val composeViewPositionOnScreen = Rect() + private val scrollState = ScrollState(0) // Inside object for namespacing private val notificationScrimClippingParams = @@ -210,6 +216,9 @@ constructor( context, { notificationScrimClippingParams.isEnabled }, { notificationScrimClippingParams.params.top }, + // Only allow scrolling when we are fully expanded. That way, we don't intercept + // swipes in lockscreen (when somehow QS is receiving touches). + { scrollState.canScrollForward && viewModel.expansionState.value.progress >= 1f }, ) frame.addView( composeView, @@ -488,12 +497,8 @@ constructor( private fun setListenerCollections() { lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - // TODO - // setListenerJob( - // scrollListener, - // - // ) + this@QSFragmentCompose.view?.setSnapshotBinding { + scrollListener.value?.onQsPanelScrollChanged(scrollState.value) } launch { setListenerJob( @@ -528,6 +533,7 @@ constructor( viewModel.containerViewModel.quickQuickSettingsViewModel.squishinessViewModel .squishiness .collectAsStateWithLifecycle() + Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { Box( modifier = @@ -591,7 +597,12 @@ constructor( modifier = Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f) ) { - Column { + DisposableEffect(Unit) { + lifecycleScope.launch { scrollState.scrollTo(0) } + onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } } + } + + Column(modifier = Modifier.verticalScroll(scrollState)) { Spacer( modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() } ) @@ -601,15 +612,14 @@ constructor( ) } } - QuickSettingsTheme { - FooterActions( - viewModel = viewModel.footerActionsViewModel, - qsVisibilityLifecycleOwner = this@QSFragmentCompose, - modifier = - Modifier.sysuiResTag("qs_footer_actions") - .element(ElementKeys.FooterActions), - ) - } + } + QuickSettingsTheme { + FooterActions( + viewModel = viewModel.footerActionsViewModel, + qsVisibilityLifecycleOwner = this@QSFragmentCompose, + modifier = + Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions), + ) } } } @@ -791,13 +801,17 @@ private class ExpansionTransition(currentProgress: Float) : private const val EDIT_MODE_TIME_MILLIS = 500 /** - * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as - * per [clippingEnabledProvider]. + * Performs different touch handling based on the state of the ComposeView: + * * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as + * per [clippingEnabledProvider]. + * * Intercept touches that would overscroll QS forward and instead allow them to be used to close + * the shade. */ private class FrameLayoutTouchPassthrough( context: Context, private val clippingEnabledProvider: () -> Boolean, private val clippingTopProvider: () -> Int, + private val canScrollForwardQs: () -> Boolean, ) : FrameLayout(context) { override fun isTransformedTouchPointInView( x: Float, @@ -811,4 +825,32 @@ private class FrameLayoutTouchPassthrough( super.isTransformedTouchPointInView(x, y, child, outLocalPoint) } } + + val touchSlop = ViewConfiguration.get(context).scaledTouchSlop + var downY = 0f + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + // If there's a touch on this view and we can scroll down, we don't want to be intercepted + val action = ev.actionMasked + + when (action) { + MotionEvent.ACTION_DOWN -> { + // If we can scroll down, make sure none of our parents intercepts us. + if (canScrollForwardQs()) { + parent?.requestDisallowInterceptTouchEvent(true) + } + downY = ev.y + } + + MotionEvent.ACTION_MOVE -> { + val y = ev.y.toInt() + val yDiff: Float = y - downY + if (yDiff < -touchSlop && !canScrollForwardQs()) { + // Intercept touches that are overscrolling. + return true + } + } + } + return super.onInterceptTouchEvent(ev) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt new file mode 100644 index 000000000000..b9994d7bb821 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.compose + +import com.android.compose.animation.Bounceable +import com.android.systemui.qs.panels.shared.model.SizedTile +import com.android.systemui.qs.panels.ui.model.GridCell +import com.android.systemui.qs.panels.ui.model.TileGridCell +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel +import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel + +data class BounceableInfo( + val bounceable: BounceableTileViewModel, + val previousTile: Bounceable?, + val nextTile: Bounceable?, + val bounceEnd: Boolean, +) + +fun List<Pair<GridCell, BounceableTileViewModel>>.bounceableInfo( + index: Int, + columns: Int, +): BounceableInfo { + val cell = this[index].first as TileGridCell + // Only look for neighbor bounceables if they are on the same row + val onLastColumn = cell.onLastColumn(cell.column, columns) + val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 } + val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn } + return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn) +} + +fun List<BounceableTileViewModel>.bounceableInfo( + sizedTile: SizedTile<TileViewModel>, + index: Int, + column: Int, + columns: Int, +): BounceableInfo { + // Only look for neighbor bounceables if they are on the same row + val onLastColumn = sizedTile.onLastColumn(column, columns) + val previousTile = getOrNull(index - 1)?.takeIf { column != 0 } + val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn } + return BounceableInfo(this[index], previousTile, nextTile, !onLastColumn) +} + +private fun <T> SizedTile<T>.onLastColumn(column: Int, columns: Int): Boolean { + return column == columns - width +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt index 770fd785723a..74fa0fef21d7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt @@ -116,9 +116,9 @@ class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val c regenerateGrid() _tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell) } else { - // Add the tile with a temporary row which will get reassigned when + // Add the tile with a temporary row/col which will get reassigned when // regenerating spacers - _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0)) + _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0, 0)) } regenerateGrid() diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt index e749475479d8..d55763aaeddb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt @@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.ui.compose import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons @@ -97,7 +97,9 @@ constructor( TileGrid(tiles = page, modifier = Modifier, editModeStart = {}) } } - Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) { + // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is + // expected to be inside a scrollable container, this should not be an issue. + Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) { PagerDots( pagerState = pagerState, activeColor = MaterialTheme.colorScheme.primary, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt index a645b51404e7..1c7a334d3ef2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt @@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.util.fastMap @@ -29,6 +31,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.grid.ui.compose.VerticalSpannedGrid import com.android.systemui.qs.composefragment.ui.GridAnchor import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey import com.android.systemui.res.R @@ -38,10 +41,11 @@ fun SceneScope.QuickQuickSettings( viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier, ) { - val sizedTiles by - viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList()) + val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle() val tiles = sizedTiles.fastMap { it.tile } + val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() DisposableEffect(tiles) { val token = Any() @@ -49,6 +53,7 @@ fun SceneScope.QuickQuickSettings( onDispose { tiles.forEach { it.stopListening(token) } } } val columns by viewModel.columns.collectAsStateWithLifecycle() + var cellIndex = 0 Box(modifier = modifier) { GridAnchor() VerticalSpannedGrid( @@ -59,11 +64,15 @@ fun SceneScope.QuickQuickSettings( modifier = Modifier.sysuiResTag("qqs_tile_layout"), ) { spanIndex -> val it = sizedTiles[spanIndex] + val column = cellIndex % columns + cellIndex += it.width Tile( tile = it.tile, iconOnly = it.isIcon, modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)), squishiness = { squishiness }, + coroutineScope = scope, + bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt index 9ec5a82a18d7..71fa0ac30fb7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -54,6 +55,7 @@ import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.toggleableState +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.android.compose.modifiers.background import com.android.compose.modifiers.thenIf @@ -64,7 +66,6 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState import com.android.systemui.res.R -import kotlinx.coroutines.delay private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target" @@ -138,13 +139,20 @@ fun LargeTileLabels( accessibilityUiState: AccessibilityUiState? = null, ) { Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) { - Text(label, color = colors.label, modifier = Modifier.tileMarquee()) + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = colors.label, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) if (!TextUtils.isEmpty(secondaryLabel)) { Text( secondaryLabel ?: "", color = colors.secondaryLabel, + style = MaterialTheme.typography.bodyMedium, modifier = - Modifier.tileMarquee().thenIf( + Modifier.thenIf( accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") == true ) { @@ -182,10 +190,7 @@ fun SmallTileContent( rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true) } else { var atEnd by remember(icon.res) { mutableStateOf(false) } - LaunchedEffect(key1 = icon.res) { - delay(350) - atEnd = true - } + LaunchedEffect(key1 = icon.res) { atEnd = true } rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index 45c1e48840ee..5c2a2bd2b78c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -22,13 +22,13 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.animateIntAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.LocalOverscrollConfiguration import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -46,7 +46,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyGridItemScope import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.rememberScrollState @@ -66,19 +65,17 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.BiasAlignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInRoot @@ -98,23 +95,26 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastMap -import androidx.compose.ui.zIndex +import com.android.compose.animation.bounceable import com.android.compose.modifiers.background import com.android.compose.modifiers.height import com.android.systemui.common.ui.compose.load import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl +import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.DragAndDropState import com.android.systemui.qs.panels.ui.compose.EditTileListState +import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState -import com.android.systemui.qs.panels.ui.compose.selection.ResizingHandle +import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer import com.android.systemui.qs.panels.ui.compose.selection.TileWidths import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState @@ -122,11 +122,14 @@ import com.android.systemui.qs.panels.ui.compose.selection.selectableTile import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.SpacerGridCell import com.android.systemui.qs.panels.ui.model.TileGridCell +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.groupAndSort import com.android.systemui.res.R +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.launch object TileType @@ -241,15 +244,20 @@ private fun CurrentTilesGrid( onSetTiles: (List<TileSpec>) -> Unit, ) { val currentListState by rememberUpdatedState(listState) - val tileHeight = CommonTileDefaults.TileHeight val totalRows = listState.tiles.lastOrNull()?.row ?: 0 val totalHeight by animateDpAsState( - gridHeight(totalRows + 1, tileHeight, TileArrangementPadding, CurrentTilesGridPadding), + gridHeight(totalRows + 1, TileHeight, TileArrangementPadding, CurrentTilesGridPadding), label = "QSEditCurrentTilesGridHeight", ) val gridState = rememberLazyGridState() var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) } + val coroutineScope = rememberCoroutineScope() + + val cells = + remember(listState.tiles) { + listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) } + } TileLazyGrid( state = gridState, @@ -272,7 +280,7 @@ private fun CurrentTilesGrid( } .testTag(CURRENT_TILES_GRID_TEST_TAG), ) { - EditTiles(listState.tiles, listState, selectionState) { spec -> + EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec -> // Toggle the current size of the tile currentListState.isIcon(spec)?.let { onResize(spec, !it) } } @@ -286,10 +294,10 @@ private fun AvailableTileGrid( columns: Int, dragAndDropState: DragAndDropState, ) { - // Available tiles aren't visible during drag and drop, so the row isn't needed + // Available tiles aren't visible during drag and drop, so the row/col isn't needed val groupedTiles = remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) { - groupAndSort(tiles.fastMap { TileGridCell(it, 0) }) + groupAndSort(tiles.fastMap { TileGridCell(it, 0, 0) }) } val labelColors = EditModeTileDefaults.editTileColors() @@ -349,24 +357,26 @@ private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any { /** * Adds a list of [GridCell] to the lazy grid * - * @param cells the list of [GridCell] + * @param cells the pairs of [GridCell] to [BounceableTileViewModel] * @param dragAndDropState the [DragAndDropState] for this grid * @param selectionState the [MutableSelectionState] for this grid * @param onToggleSize the callback when a tile's size is toggled */ fun LazyGridScope.EditTiles( - cells: List<GridCell>, + cells: List<Pair<GridCell, BounceableTileViewModel>>, + columns: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, + coroutineScope: CoroutineScope, onToggleSize: (spec: TileSpec) -> Unit, ) { items( count = cells.size, - key = { cells[it].key(it, dragAndDropState) }, - span = { cells[it].span }, + key = { cells[it].first.key(it, dragAndDropState) }, + span = { cells[it].first.span }, contentType = { TileType }, ) { index -> - when (val cell = cells[index]) { + when (val cell = cells[index].first) { is TileGridCell -> if (dragAndDropState.isMoving(cell.tile.tileSpec)) { // If the tile is being moved, replace it with a visible spacer @@ -385,6 +395,9 @@ fun LazyGridScope.EditTiles( dragAndDropState = dragAndDropState, selectionState = selectionState, onToggleSize = onToggleSize, + coroutineScope = coroutineScope, + bounceableInfo = cells.bounceableInfo(index, columns), + modifier = Modifier.animateItem(), ) } is SpacerGridCell -> SpacerGridCell() @@ -393,12 +406,15 @@ fun LazyGridScope.EditTiles( } @Composable -private fun LazyGridItemScope.TileGridCell( +private fun TileGridCell( cell: TileGridCell, index: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, onToggleSize: (spec: TileSpec) -> Unit, + coroutineScope: CoroutineScope, + bounceableInfo: BounceableInfo, + modifier: Modifier = Modifier, ) { val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1) var selected by remember { mutableStateOf(false) } @@ -407,6 +423,9 @@ private fun LazyGridItemScope.TileGridCell( targetValue = if (selected) 1f else 0f, label = "QSEditTileSelectionAlpha", ) + val selectionColor = MaterialTheme.colorScheme.primary + val colors = EditModeTileDefaults.editTileColors() + val currentBounceableInfo by rememberUpdatedState(bounceableInfo) LaunchedEffect(selectionState.selection?.tileSpec) { selectionState.selection?.let { @@ -420,152 +439,61 @@ private fun LazyGridItemScope.TileGridCell( selected = selectionState.selection?.tileSpec == cell.tile.tileSpec } - val modifier = - Modifier.animateItem() - .semantics(mergeDescendants = true) { - this.stateDescription = stateDescription - contentDescription = cell.tile.label.text - customActions = - listOf( - // TODO(b/367748260): Add final accessibility actions - CustomAccessibilityAction("Toggle size") { - onToggleSize(cell.tile.tileSpec) - true - } - ) - } - .height(CommonTileDefaults.TileHeight) - .fillMaxWidth() - - val content = - @Composable { - EditTile( - tileViewModel = cell.tile, - iconOnly = cell.isIcon, - selectionAlpha = { selectionAlpha }, - modifier = - Modifier.fillMaxSize() - .selectableTile(cell.tile.tileSpec, selectionState) - .dragAndDropTileSource( - SizedTileImpl(cell.tile, cell.width), - dragAndDropState, - selectionState::unSelect, - ), - ) - } - - if (selected) { - SelectedTile( - isIcon = cell.isIcon, - selectionAlpha = { selectionAlpha }, - selectionState = selectionState, - modifier = modifier.zIndex(2f), // 2f to display this tile over neighbors when dragged - content = content, - ) - } else { - UnselectedTile( - selectionAlpha = { selectionAlpha }, - selectionState = selectionState, - modifier = modifier, - content = content, - ) - } -} - -@Composable -private fun SelectedTile( - isIcon: Boolean, - selectionAlpha: () -> Float, - selectionState: MutableSelectionState, - modifier: Modifier = Modifier, - content: @Composable () -> Unit, -) { // Current base, min and max width of this tile var tileWidths: TileWidths? by remember { mutableStateOf(null) } - - // Animated diff between the current width and the resized width of the tile. We can't use - // animateContentSize here as the tile is sometimes unbounded. - val remainingOffset by - animateIntAsState( - selectionState.resizingState?.let { tileWidths?.base?.minus(it.width) ?: 0 } ?: 0, - label = "QSEditTileWidthOffset", - ) - val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() } - Box( - modifier.onSizeChanged { - val min = if (isIcon) it.width else (it.width - padding) / 2 - val max = if (isIcon) (it.width * 2) + padding else it.width - tileWidths = TileWidths(it.width, min, max) - } + + ResizableTileContainer( + selected = selected, + selectionState = selectionState, + selectionAlpha = { selectionAlpha }, + selectionColor = selectionColor, + tileWidths = { tileWidths }, + modifier = + modifier + .height(TileHeight) + .fillMaxWidth() + .onSizeChanged { + // Grab the size before the bounceable to get the idle width + val min = if (cell.isIcon) it.width else (it.width - padding) / 2 + val max = if (cell.isIcon) (it.width * 2) + padding else it.width + tileWidths = TileWidths(it.width, min, max) + } + .bounceable( + bounceable = currentBounceableInfo.bounceable, + previousBounceable = currentBounceableInfo.previousTile, + nextBounceable = currentBounceableInfo.nextTile, + orientation = Orientation.Horizontal, + bounceEnd = currentBounceableInfo.bounceEnd, + ), ) { - val handle = - @Composable { - ResizingHandle( - enabled = true, - selectionState = selectionState, - transition = selectionAlpha, - tileWidths = { tileWidths }, + Box( + modifier + .fillMaxSize() + .semantics(mergeDescendants = true) { + this.stateDescription = stateDescription + contentDescription = cell.tile.label.text + customActions = + listOf( + // TODO(b/367748260): Add final accessibility actions + CustomAccessibilityAction("Toggle size") { + onToggleSize(cell.tile.tileSpec) + true + } + ) + } + .selectableTile(cell.tile.tileSpec, selectionState) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } + .dragAndDropTileSource( + SizedTileImpl(cell.tile, cell.width), + dragAndDropState, + selectionState::unSelect, ) - } - - Layout(contents = listOf(content, handle)) { - (contentMeasurables, handleMeasurables), - constraints -> - // Grab the width from the resizing state if a resize is in progress, otherwise fill the - // max width - val width = - selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset) - val contentPlaceable = - contentMeasurables.first().measure(constraints.copy(maxWidth = width)) - val handlePlaceable = handleMeasurables.first().measure(constraints) - - // Place the dot vertically centered on the right edge - val handleX = contentPlaceable.width - (handlePlaceable.width / 2) - val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2) - - layout(constraints.maxWidth, constraints.maxHeight) { - contentPlaceable.place(0, 0) - handlePlaceable.place(handleX, handleY) - } - } - } -} - -@Composable -private fun UnselectedTile( - selectionAlpha: () -> Float, - selectionState: MutableSelectionState, - modifier: Modifier = Modifier, - content: @Composable () -> Unit, -) { - val handle = - @Composable { - ResizingHandle( - enabled = false, - selectionState = selectionState, - transition = selectionAlpha, - ) - } - - Box(modifier) { - Layout(contents = listOf(content, handle)) { - (contentMeasurables, handleMeasurables), - constraints -> - val contentPlaceable = - contentMeasurables - .first() - .measure(constraints.copy(maxWidth = constraints.maxWidth)) - val handlePlaceable = handleMeasurables.first().measure(constraints) - - // Place the dot vertically centered on the right edge - val handleX = contentPlaceable.width - (handlePlaceable.width / 2) - val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2) - - layout(constraints.maxWidth, constraints.maxHeight) { - contentPlaceable.place(0, 0) - handlePlaceable.place(handleX, handleY) - } + .tileBackground(colors.background) + .tilePadding() + ) { + EditTile(tile = cell.tile, iconOnly = cell.isIcon) } } } @@ -588,19 +516,19 @@ private fun AvailableTileGridCell( verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top), modifier = modifier, ) { - EditTileContainer( - colors = colors, - modifier = - Modifier.fillMaxWidth() - .height(CommonTileDefaults.TileHeight) - .clearSelectionTile(selectionState) - .semantics(mergeDescendants = true) { - onClick(onClickActionName) { false } - this.stateDescription = stateDescription - } - .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) { - selectionState.unSelect() - }, + Box( + Modifier.fillMaxWidth() + .height(TileHeight) + .clearSelectionTile(selectionState) + .semantics(mergeDescendants = true) { + onClick(onClickActionName) { false } + this.stateDescription = stateDescription + } + .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) { + selectionState.unSelect() + } + .tileBackground(colors.background) + .tilePadding() ) { // Icon SmallTileContent( @@ -626,16 +554,14 @@ private fun AvailableTileGridCell( @Composable private fun SpacerGridCell(modifier: Modifier = Modifier) { // By default, spacers are invisible and exist purely to catch drag movements - Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth()) + Box(modifier.height(TileHeight).fillMaxWidth()) } @Composable -fun EditTile( - tileViewModel: EditTileViewModel, +fun BoxScope.EditTile( + tile: EditTileViewModel, iconOnly: Boolean, - modifier: Modifier = Modifier, colors: TileColors = EditModeTileDefaults.editTileColors(), - selectionAlpha: () -> Float = { 1f }, ) { // Animated horizontal alignment from center (0f) to start (-1f) val alignmentValue by @@ -646,68 +572,36 @@ fun EditTile( val alignment by remember { derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) } } + // Icon + Box(Modifier.size(ToggleTargetSize).align(alignment)) { + SmallTileContent( + icon = tile.icon, + color = colors.icon, + animateToEnd = true, + modifier = Modifier.align(Alignment.Center), + ) + } - EditTileContainer(colors = colors, selectionAlpha = selectionAlpha, modifier = modifier) { - // Icon - Box(Modifier.size(ToggleTargetSize).align(alignment)) { - SmallTileContent( - icon = tileViewModel.icon, - color = colors.icon, - animateToEnd = true, - modifier = Modifier.align(Alignment.Center), - ) - } - - // Labels, positioned after the icon - AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) { - LargeTileLabels( - label = tileViewModel.label.text, - secondaryLabel = tileViewModel.appName?.text, - colors = colors, - modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding), - ) - } + // Labels, positioned after the icon + AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) { + LargeTileLabels( + label = tile.label.text, + secondaryLabel = tile.appName?.text, + colors = colors, + modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding), + ) } } -@Composable -private fun EditTileContainer( - colors: TileColors, - modifier: Modifier = Modifier, - selectionAlpha: () -> Float = { 0f }, - selectionColor: Color = MaterialTheme.colorScheme.primary, - content: @Composable BoxScope.() -> Unit = {}, -) { - Box( - Modifier.wrapContentSize().drawWithContent { - drawContent() - drawRoundRect( - SolidColor(selectionColor), - cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), - style = Stroke(EditModeTileDefaults.SelectedBorderWidth.toPx()), - alpha = selectionAlpha(), - ) - } - ) { - Box( - modifier = - modifier - .drawBehind { - drawRoundRect( - SolidColor(colors.background), - cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), - ) - } - .tilePadding(), - content = content, - ) +private fun Modifier.tileBackground(color: Color): Modifier { + return drawBehind { + drawRoundRect(SolidColor(color), cornerRadius = CornerRadius(InactiveCornerRadius.toPx())) } } private object EditModeTileDefaults { const val PLACEHOLDER_ALPHA = .3f val EditGridHeaderHeight = 60.dp - val SelectedBorderWidth = 2.dp val CurrentTilesGridPadding = 8.dp @Composable diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index 6920e498bdde..e5c213519415 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.util.fastMap @@ -29,7 +30,9 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.grid.ui.compose.VerticalSpannedGrid import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout +import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.rememberEditListState +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel @@ -62,7 +65,11 @@ constructor( } val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle() val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) } + val bounceables = + remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by squishinessViewModel.squishiness.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() + var cellIndex = 0 VerticalSpannedGrid( columns = columns, @@ -71,11 +78,15 @@ constructor( spans = sizedTiles.fastMap { it.width }, ) { spanIndex -> val it = sizedTiles[spanIndex] + val column = cellIndex % columns + cellIndex += it.width Tile( tile = it.tile, iconOnly = iconTilesViewModel.isIconTile(it.tile.spec), modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)), squishiness = { squishiness }, + coroutineScope = scope, + bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt index 4bd5b2d68c4c..52d526123430 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt @@ -23,8 +23,8 @@ import android.service.quicksettings.Tile.STATE_ACTIVE import android.service.quicksettings.Tile.STATE_INACTIVE import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box @@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -61,11 +62,13 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable +import com.android.compose.animation.bounceable import com.android.compose.modifiers.thenIf import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState @@ -74,6 +77,8 @@ import com.android.systemui.qs.panels.ui.viewmodel.toUiState import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R import java.util.function.Supplier +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch private const val TEST_TAG_SMALL = "qs_tile_small" private const val TEST_TAG_LARGE = "qs_tile_large" @@ -102,9 +107,12 @@ fun Tile( tile: TileViewModel, iconOnly: Boolean, squishiness: () -> Float, + coroutineScope: CoroutineScope, + bounceableInfo: BounceableInfo, modifier: Modifier = Modifier, ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) + val currentBounceableInfo by rememberUpdatedState(bounceableInfo) val resources = resources() val uiState = remember(state, resources) { state.toUiState(resources) } val colors = TileDefaults.getColorForState(uiState) @@ -112,7 +120,7 @@ fun Tile( // TODO(b/361789146): Draw the shapes instead of clipping val tileShape = TileDefaults.animateTileShape(uiState.state) - TileContainer( + TileExpandable( color = if (iconOnly || !uiState.handlesSecondaryClick) { colors.iconBackground @@ -120,93 +128,101 @@ fun Tile( colors.background }, shape = tileShape, - iconOnly = iconOnly, - onClick = tile::onClick, - onLongClick = tile::onLongClick, - uiState = uiState, squishiness = squishiness, - modifier = modifier, + modifier = + modifier + .fillMaxWidth() + .bounceable( + bounceable = currentBounceableInfo.bounceable, + previousBounceable = currentBounceableInfo.previousTile, + nextBounceable = currentBounceableInfo.nextTile, + orientation = Orientation.Horizontal, + bounceEnd = currentBounceableInfo.bounceEnd, + ), ) { expandable -> - val icon = getTileIcon(icon = uiState.icon) - if (iconOnly) { - SmallTileContent( - icon = icon, - color = colors.icon, - modifier = Modifier.align(Alignment.Center), - ) - } else { - val iconShape = TileDefaults.animateIconShape(uiState.state) - LargeTileContent( - label = uiState.label, - secondaryLabel = uiState.secondaryLabel, - icon = icon, - colors = colors, - iconShape = iconShape, - toggleClickSupported = state.handlesSecondaryClick, - onClick = { - if (state.handlesSecondaryClick) { - tile.onSecondaryClick() - } - }, - onLongClick = { tile.onLongClick(expandable) }, - accessibilityUiState = uiState.accessibilityUiState, - squishiness = squishiness, - ) + TileContainer( + onClick = { + tile.onClick(expandable) + if (uiState.accessibilityUiState.toggleableState != null) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } + }, + onLongClick = { tile.onLongClick(expandable) }, + uiState = uiState, + iconOnly = iconOnly, + ) { + val icon = getTileIcon(icon = uiState.icon) + if (iconOnly) { + SmallTileContent( + icon = icon, + color = colors.icon, + modifier = Modifier.align(Alignment.Center), + ) + } else { + val iconShape = TileDefaults.animateIconShape(uiState.state) + LargeTileContent( + label = uiState.label, + secondaryLabel = uiState.secondaryLabel, + icon = icon, + colors = colors, + iconShape = iconShape, + toggleClickSupported = state.handlesSecondaryClick, + onClick = { + if (state.handlesSecondaryClick) { + tile.onSecondaryClick() + } + }, + onLongClick = { tile.onLongClick(expandable) }, + accessibilityUiState = uiState.accessibilityUiState, + squishiness = squishiness, + ) + } } } } @Composable -private fun TileContainer( +private fun TileExpandable( color: Color, shape: Shape, - iconOnly: Boolean, - uiState: TileUiState, squishiness: () -> Float, modifier: Modifier = Modifier, - onClick: (Expandable) -> Unit = {}, - onLongClick: (Expandable) -> Unit = {}, - content: @Composable BoxScope.(Expandable) -> Unit, + content: @Composable (Expandable) -> Unit, ) { Expandable( color = color, shape = shape, modifier = modifier.clip(shape).verticalSquish(squishiness), ) { - val longPressLabel = longPressLabel() - Box( - modifier = - Modifier.height(CommonTileDefaults.TileHeight) - .fillMaxWidth() - .combinedClickable( - onClick = { onClick(it) }, - onLongClick = { onLongClick(it) }, - onClickLabel = uiState.accessibilityUiState.clickLabel, - onLongClickLabel = longPressLabel, - ) - .semantics { - role = uiState.accessibilityUiState.accessibilityRole - if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) { - uiState.accessibilityUiState.toggleableState?.let { - toggleableState = it - } - } - stateDescription = uiState.accessibilityUiState.stateDescription - } - .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE) - .thenIf(iconOnly) { - Modifier.semantics { - contentDescription = uiState.accessibilityUiState.contentDescription - } - } - .tilePadding() - ) { - content(it) - } + content(it) } } @Composable +fun TileContainer( + onClick: () -> Unit, + onLongClick: () -> Unit, + uiState: TileUiState, + iconOnly: Boolean, + content: @Composable BoxScope.() -> Unit, +) { + Box( + modifier = + Modifier.height(CommonTileDefaults.TileHeight) + .fillMaxWidth() + .tileCombinedClickable( + onClick = onClick, + onLongClick = onLongClick, + uiState = uiState, + iconOnly = iconOnly, + ) + .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE) + .tilePadding(), + content = content, + ) +} + +@Composable private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon { val context = LocalContext.current return icon.get()?.let { @@ -222,14 +238,38 @@ fun tileHorizontalArrangement(): Arrangement.Horizontal { return spacedBy(space = CommonTileDefaults.TileArrangementPadding, alignment = Alignment.Start) } -fun Modifier.tileMarquee(): Modifier { - return basicMarquee(iterations = 1, initialDelayMillis = 200) -} - fun Modifier.tilePadding(): Modifier { return padding(CommonTileDefaults.TilePadding) } +@Composable +fun Modifier.tileCombinedClickable( + onClick: () -> Unit, + onLongClick: () -> Unit, + uiState: TileUiState, + iconOnly: Boolean, +): Modifier { + val longPressLabel = longPressLabel() + return combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + onClickLabel = uiState.accessibilityUiState.clickLabel, + onLongClickLabel = longPressLabel, + ) + .semantics { + role = uiState.accessibilityUiState.accessibilityRole + if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) { + uiState.accessibilityUiState.toggleableState?.let { toggleableState = it } + } + stateDescription = uiState.accessibilityUiState.stateDescription + } + .thenIf(iconOnly) { + Modifier.semantics { + contentDescription = uiState.accessibilityUiState.contentDescription + } + } +} + data class TileColors( val background: Color, val iconBackground: Color, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt index 441d96289d86..1d36aee4eb85 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt @@ -89,8 +89,11 @@ class MutableSelectionState( * Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as * they can be selected. */ -@Composable -fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier { +fun Modifier.selectableTile( + tileSpec: TileSpec, + selectionState: MutableSelectionState, + onClick: () -> Unit = {}, +): Modifier { return pointerInput(Unit) { detectTapGestures( onTap = { @@ -99,6 +102,7 @@ fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelection } else { selectionState.select(tileSpec, manual = true) } + onClick() } ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt index e0f0b6aa8919..9f13a3788f53 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt @@ -16,63 +16,117 @@ package com.android.systemui.qs.panels.ui.compose.selection +import androidx.compose.animation.core.animateIntAsState import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.detectHorizontalDragGestures import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.size import androidx.compose.foundation.systemGestureExclusion import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toSize +import androidx.compose.ui.zIndex +import com.android.compose.modifiers.thenIf +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth /** - * Dot handling resizing drag events. Use this on the selected tile to resize it + * Places a dot to handle resizing drag events. Use this on tiles to resize. * - * @param enabled whether resizing drag events should be handled + * The dot is placed vertically centered on the right border. The [content] will have a border when + * selected. + * + * @param selected whether resizing drag events should be handled * @param selectionState the [MutableSelectionState] on the grid - * @param transition the animated value for the dot, used for its alpha and scale + * @param selectionAlpha the animated value for the dot and border alpha + * @param selectionColor the [Color] of the dot and border * @param tileWidths the [TileWidths] of the selected tile - * @param onResize the callback when the drag passes the resizing threshold */ @Composable -fun ResizingHandle( +fun ResizableTileContainer( + selected: Boolean, + selectionState: MutableSelectionState, + selectionAlpha: () -> Float, + selectionColor: Color, + tileWidths: () -> TileWidths?, + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit = {}, +) { + Box( + modifier + .resizable(selected, selectionState, tileWidths) + .selectionBorder(selectionColor, selectionAlpha) + ) { + content() + ResizingHandle( + enabled = selected, + selectionState = selectionState, + transition = selectionAlpha, + tileWidths = tileWidths, + modifier = + // Higher zIndex to make sure the handle is drawn above the content + Modifier.zIndex(2f), + ) + } +} + +@Composable +private fun ResizingHandle( enabled: Boolean, selectionState: MutableSelectionState, transition: () -> Float, - tileWidths: () -> TileWidths? = { null }, + tileWidths: () -> TileWidths?, + modifier: Modifier = Modifier, ) { - if (enabled) { - // Manually creating the touch target around the resizing dot to ensure that the next tile - // does - // not receive the touch input accidentally. - val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current - Box( - Modifier.size(minTouchTargetSize) - .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } - .pointerInput(Unit) { - detectHorizontalDragGestures( - onHorizontalDrag = { _, offset -> selectionState.onResizingDrag(offset) }, - onDragStart = { - tileWidths()?.let { selectionState.onResizingDragStart(it) } - }, - onDragEnd = selectionState::onResizingDragEnd, - onDragCancel = selectionState::onResizingDragEnd, + // Manually creating the touch target around the resizing dot to ensure that the next tile + // does not receive the touch input accidentally. + val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current + Box( + modifier + .layout { measurable, constraints -> + val size = minTouchTargetSize.roundToPx() + val placeable = measurable.measure(Constraints(size, size, size, size)) + layout(placeable.width, placeable.height) { + placeable.place( + x = constraints.maxWidth - placeable.width / 2, + y = constraints.maxHeight / 2 - placeable.height / 2, ) } - ) { - ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center)) - } - } else { - ResizingDot(transition = transition) + } + .thenIf(enabled) { + Modifier.systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } + .pointerInput(Unit) { + detectHorizontalDragGestures( + onHorizontalDrag = { _, offset -> + selectionState.onResizingDrag(offset) + }, + onDragStart = { + tileWidths()?.let { selectionState.onResizingDragStart(it) } + }, + onDragEnd = selectionState::onResizingDragEnd, + onDragCancel = selectionState::onResizingDragEnd, + ) + } + } + ) { + ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center)) } } @@ -88,6 +142,45 @@ private fun ResizingDot( } } +private fun Modifier.selectionBorder( + selectionColor: Color, + selectionAlpha: () -> Float = { 0f }, +): Modifier { + return drawWithContent { + drawContent() + drawRoundRect( + SolidColor(selectionColor), + cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), + style = Stroke(SelectedBorderWidth.toPx()), + alpha = selectionAlpha(), + ) + } +} + +@Composable +private fun Modifier.resizable( + selected: Boolean, + selectionState: MutableSelectionState, + tileWidths: () -> TileWidths?, +): Modifier { + if (!selected) return zIndex(1f) + + // Animated diff between the current width and the resized width of the tile. We can't use + // animateContentSize here as the tile is sometimes unbounded. + val remainingOffset by + animateIntAsState( + selectionState.resizingState?.let { tileWidths()?.base?.minus(it.width) ?: 0 } ?: 0, + label = "QSEditTileWidthOffset", + ) + return zIndex(2f).layout { measurable, constraints -> + // Grab the width from the resizing state if a resize is in progress + val width = selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset) + val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width)) + layout(constraints.maxWidth, placeable.height) { placeable.place(0, 0) } + } +} + private object SelectionDefaults { val ResizingDotSize = 16.dp + val SelectedBorderWidth = 2.dp } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt index b1841c4c5ffa..c0441f8a38a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt @@ -31,8 +31,8 @@ sealed interface GridCell { } /** - * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's - * positioned at + * Represents a [EditTileViewModel] from a grid associated with a tile format and the row and column + * it's positioned at */ @Immutable data class TileGridCell( @@ -41,13 +41,15 @@ data class TileGridCell( override val width: Int, override val span: GridItemSpan = GridItemSpan(width), override val s: String = "${tile.tileSpec.spec}-$row-$width", + val column: Int, ) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile { val key: String = "${tile.tileSpec.spec}-$row" constructor( sizedTile: SizedTile<EditTileViewModel>, row: Int, - ) : this(tile = sizedTile.tile, row = row, width = sizedTile.width) + column: Int, + ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width) } /** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */ @@ -73,7 +75,13 @@ fun List<SizedTile<EditTileViewModel>>.toGridCells( return splitInRowsSequence(this, columns) .flatMapIndexed { rowIndex, sizedTiles -> val correctedRowIndex = rowIndex + startingRow - val row: List<GridCell> = sizedTiles.map { TileGridCell(it, correctedRowIndex) } + var column = 0 + val row: List<GridCell> = + sizedTiles.map { + TileGridCell(it, correctedRowIndex, column).also { cell -> + column += cell.width + } + } // Fill the incomplete rows with spacers val numSpacers = columns - sizedTiles.sumOf { it.width } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt new file mode 100644 index 000000000000..506b05256880 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.viewmodel + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.VectorConverter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.android.compose.animation.Bounceable + +class BounceableTileViewModel : Bounceable { + private val animatableBounce = Animatable(0.dp, Dp.VectorConverter) + override val bounce: Dp + get() = animatableBounce.value + + suspend fun animateBounce() { + animatableBounce.animateTo(BounceSize) + animatableBounce.animateTo(0.dp) + } + + private companion object { + val BounceSize = 8.dp + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt index ee12736f6db4..be6ce5c5b4f4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt @@ -43,13 +43,13 @@ data class UnloadedEditTileViewModel( ) { fun load(context: Context): EditTileViewModel { return EditTileViewModel( - tileSpec, - icon, - label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec), - appName?.toAnnotatedString(context), - isCurrent, - availableEditActions, - category, + tileSpec = tileSpec, + icon = icon, + label = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec), + appName = appName?.toAnnotatedString(context), + isCurrent = isCurrent, + availableEditActions = availableEditActions, + category = category, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt index b8f4ab40bb1d..dde36289f139 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt @@ -16,7 +16,7 @@ package com.android.systemui.qs.tiles.base.viewmodel -import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -28,5 +28,7 @@ class QSTileCoroutineScopeFactory constructor(@Application private val applicationScope: CoroutineScope) { fun create(): CoroutineScope = - CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("QSTileScope")) + CoroutineScope( + applicationScope.coroutineContext + SupervisorJob() + newTracingContext("QSTileScope") + ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt index ae56c2aad4e9..f6749715e1fd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt @@ -15,12 +15,12 @@ */ package com.android.systemui.qs.tiles.dialog -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.util.Log import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.animation.Expandable +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.phone.SystemUIDialog @@ -63,7 +63,7 @@ constructor( } return } else { - coroutineScope = CoroutineScope(bgDispatcher + createCoroutineTracingContext("InternetDialogScope")) + coroutineScope = CoroutineScope(bgDispatcher + newTracingContext("InternetDialogScope")) dialog = dialogFactory .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt new file mode 100644 index 000000000000..8dd611f9911a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain + +import android.content.res.Resources +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [HearingDevicesTileModel] to [QSTileState]. */ +class HearingDevicesTileMapper +@Inject +constructor(@Main private val resources: Resources, private val theme: Resources.Theme) : + QSTileDataToStateMapper<HearingDevicesTileModel> { + + override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + label = resources.getString(R.string.quick_settings_hearing_devices_label) + iconRes = R.drawable.qs_hearing_devices_icon + val loadedIcon = + Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null) + icon = { loadedIcon } + sideViewIcon = QSTileState.SideViewIcon.Chevron + contentDescription = label + if (data.isAnyActiveHearingDevice) { + activationState = QSTileState.ActivationState.ACTIVE + secondaryLabel = + resources.getString(R.string.quick_settings_hearing_devices_connected) + } else if (data.isAnyPairedHearingDevice) { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = + resources.getString(R.string.quick_settings_hearing_devices_disconnected) + } else { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = "" + } + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt new file mode 100644 index 000000000000..ec0a4e9db896 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.os.UserHandle +import com.android.systemui.Flags +import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.statusbar.policy.BluetoothController +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn + +/** Observes hearing devices state changes providing the [HearingDevicesTileModel]. */ +class HearingDevicesTileDataInteractor +@Inject +constructor( + @Background private val backgroundContext: CoroutineContext, + private val bluetoothController: BluetoothController, + private val hearingDevicesChecker: HearingDevicesChecker, +) : QSTileDataInteractor<HearingDevicesTileModel> { + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger>, + ): Flow<HearingDevicesTileModel> = + conflatedCallbackFlow { + val callback = + object : BluetoothController.Callback { + override fun onBluetoothStateChange(enabled: Boolean) { + trySend(getModel()) + } + + override fun onBluetoothDevicesChanged() { + trySend(getModel()) + } + } + bluetoothController.addCallback(callback) + awaitClose { bluetoothController.removeCallback(callback) } + } + .flowOn(backgroundContext) + .distinctUntilChanged() + + override fun availability(user: UserHandle): Flow<Boolean> = + flowOf(Flags.hearingAidsQsTileDialog()) + + private fun getModel() = + HearingDevicesTileModel( + hearingDevicesChecker.isAnyActiveHearingDevice, + hearingDevicesChecker.isAnyPairedHearingDevice, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt new file mode 100644 index 000000000000..5e7172ee3ba7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager +import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.withContext + +/** Handles hearing devices tile clicks. */ +class HearingDevicesTileUserActionInteractor +@Inject +constructor( + @Main private val mainContext: CoroutineContext, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, + private val hearingDevicesDialogManager: HearingDevicesDialogManager, +) : QSTileUserActionInteractor<HearingDevicesTileModel> { + + override suspend fun handleInput(input: QSTileInput<HearingDevicesTileModel>) = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + withContext(mainContext) { + hearingDevicesDialogManager.showDialog( + action.expandable, + LAUNCH_SOURCE_QS_TILE, + ) + } + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.expandable, + Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS), + ) + } + is QSTileUserAction.ToggleClick -> {} + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt new file mode 100644 index 000000000000..4e37b771c49b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.hearingdevices.domain.model + +/** Hearing devices tile model */ +data class HearingDevicesTileModel( + val isAnyActiveHearingDevice: Boolean, + val isAnyPairedHearingDevice: Boolean, +) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt index ac75932b8fee..14115444fe49 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt @@ -16,9 +16,9 @@ package com.android.systemui.qs.tiles.impl.location.domain.interactor -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.content.Intent import android.provider.Settings +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.plugins.ActivityStarter @@ -53,9 +53,11 @@ constructor( val wasEnabled: Boolean = input.data.isEnabled if (keyguardController.isMethodSecure() && keyguardController.isShowing()) { activityStarter.postQSRunnableDismissingKeyguard { - CoroutineScope(applicationScope.coroutineContext + createCoroutineTracingContext("LocationTileScope")).launch { - locationController.setLocationEnabled(!wasEnabled) - } + CoroutineScope( + applicationScope.coroutineContext + + newTracingContext("LocationTileScope") + ) + .launch { locationController.setLocationEnabled(!wasEnabled) } } } else { withContext(coroutineContext) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt index 40591bf56e0a..cc14e71986f5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor import android.content.Context import android.os.UserHandle -import com.android.app.tracing.coroutines.flow.map +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Background @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map class ModesTileDataInteractor @Inject @@ -59,6 +60,7 @@ constructor( fun tileData() = zenModeInteractor.activeModes .map { activeModes -> buildTileData(activeModes) } + .flowName("tileData") .flowOn(bgDispatcher) .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt index 468e180a6e41..f03c7521931c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt @@ -16,12 +16,12 @@ package com.android.systemui.qs.tiles.impl.saver.domain -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.content.Context import android.content.DialogInterface import android.content.SharedPreferences import android.os.Bundle import com.android.internal.R +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.DataSaverController @@ -45,9 +45,8 @@ class DataSaverDialogDelegate( setTitle(R.string.data_saver_enable_title) setMessage(R.string.data_saver_description) setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ -> - CoroutineScope(backgroundContext + createCoroutineTracingContext("DataSaverDialogScope")).launch { - dataSaverController.setDataSaverEnabled(true) - } + CoroutineScope(backgroundContext + newTracingContext("DataSaverDialogScope")) + .launch { dataSaverController.setDataSaverEnabled(true) } sharedPreferences .edit() diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt index 7b6b0f614cc2..6097ef53f8df 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt @@ -20,7 +20,6 @@ package com.android.systemui.scene.shared.flag import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.Flags.sceneContainer -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils import com.android.systemui.keyguard.KeyguardBottomAreaRefactor @@ -42,8 +41,7 @@ object SceneContainerFlag { KeyguardWmStateRefactor.isEnabled && MigrateClocksToBlueprint.isEnabled && NotificationThrottleHun.isEnabled && - PredictiveBackSysUiFlag.isEnabled && - DeviceEntryUdfpsRefactor.isEnabled + PredictiveBackSysUiFlag.isEnabled // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer @@ -58,7 +56,6 @@ object SceneContainerFlag { MigrateClocksToBlueprint.token, NotificationThrottleHun.token, PredictiveBackSysUiFlag.token, - DeviceEntryUdfpsRefactor.token, // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index ce942fefa393..47254775618c 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.scene.ui.viewmodel import android.view.MotionEvent import android.view.View import androidx.compose.runtime.getValue -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.DefaultEdgeDetector import com.android.compose.animation.scene.ObservableTransitionState diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt index 54e0319da58f..17b1b6d91229 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt @@ -26,7 +26,7 @@ import android.os.UserHandle import android.util.Log import android.util.Pair import android.view.Window -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.app.ChooserActivity import com.android.systemui.dagger.qualifiers.Application import dagger.assisted.Assisted diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt index 9e622801a01b..7b01c36489fb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt @@ -31,7 +31,7 @@ import android.view.RemoteAnimationAdapter import android.view.RemoteAnimationTarget import android.view.WindowManager import android.view.WindowManagerGlobal -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.infra.ServiceConnector import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java index acfcd13738f0..2259b55dc268 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java @@ -313,7 +313,9 @@ public class LegacyScreenshotController implements InteractiveScreenshotHandler setWindowFocusable(true); mViewProxy.requestFocus(); - enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle()); + if (screenshot.getType() != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) { + enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle()); + } attachWindow(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt index f5c605211520..08214c456897 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt @@ -35,6 +35,7 @@ import android.util.Log import android.view.Display import android.view.ScrollCaptureResponse import android.view.ViewRootImpl.ActivityConfigCallback +import android.view.WindowManager import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE import android.widget.Toast import android.window.WindowContext @@ -217,7 +218,9 @@ internal constructor( window.setFocusable(true) viewProxy.requestFocus() - enqueueScrollCaptureRequest(requestId, screenshot.userHandle) + if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) { + enqueueScrollCaptureRequest(requestId, screenshot.userHandle) + } window.attachWindow() diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt index c8067df114fc..6df22f036273 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt @@ -21,7 +21,7 @@ import android.os.RemoteException import android.util.Log import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.shade.ShadeExpansionStateManager diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt index d3a7fc4a3e4a..7aeec47241cb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt @@ -18,7 +18,7 @@ package com.android.systemui.screenshot import android.media.MediaPlayer import android.util.Log -import com.android.app.tracing.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index cae141d014a8..5699557c14e7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -35,7 +35,6 @@ import androidx.core.view.ViewKt; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardUnfoldTransition; -import com.android.keyguard.LockIconViewController; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -44,7 +43,6 @@ import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags; import com.android.systemui.bouncer.ui.binder.BouncerViewBinder; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; @@ -75,7 +73,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeScrimController; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; @@ -101,9 +98,7 @@ public class NotificationShadeWindowViewController implements Dumpable { private final NotificationShadeDepthController mDepthController; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; - private final LockIconViewController mLockIconViewController; private final ShadeLogger mShadeLogger; - private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final StatusBarWindowStateController mStatusBarWindowStateController; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final AmbientState mAmbientState; @@ -174,9 +169,7 @@ public class NotificationShadeWindowViewController implements Dumpable { PanelExpansionInteractor panelExpansionInteractor, ShadeExpansionStateManager shadeExpansionStateManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, - StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowStateController statusBarWindowStateController, - LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, DozeServiceHost dozeServiceHost, DozeScrimController dozeScrimController, @@ -210,9 +203,7 @@ public class NotificationShadeWindowViewController implements Dumpable { mShadeExpansionStateManager = shadeExpansionStateManager; mDepthController = depthController; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; - mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarWindowStateController = statusBarWindowStateController; - mLockIconViewController = lockIconViewController; mShadeLogger = shadeLogger; mService = centralSurfaces; mDozeServiceHost = dozeServiceHost; @@ -259,7 +250,6 @@ public class NotificationShadeWindowViewController implements Dumpable { mDisableSubpixelTextTransitionListener)); } - lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view)); dumpManager.registerDumpable(this); } @@ -392,9 +382,6 @@ public class NotificationShadeWindowViewController implements Dumpable { } if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { - // If the user was sliding their finger across the lock screen, - // we may have been intercepting the touch and forwarding it to the - // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below). // If this touch ended up unlocking the device, we want to cancel the touch // immediately, so we don't cause swipe or expand animations afterwards. cancelCurrentTouch(); @@ -419,9 +406,6 @@ public class NotificationShadeWindowViewController implements Dumpable { && mDreamingWakeupGestureHandler.onTouchEvent(ev)) { return logDownDispatch(ev, "dream wakeup gesture handled", true); } - if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) { - return logDownDispatch(ev, "dispatched to Keyguard", true); - } if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == View.VISIBLE) { // Disallow new pointers while the brightness mirror is visible. This is so that @@ -512,7 +496,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing() && !mDockManager.isDocked() - && !mLockIconViewController.willHandleTouchWhileDozing(ev) ) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mShadeLogger.d("NSWVC: capture all touch events in always-on"); @@ -520,22 +503,8 @@ public class NotificationShadeWindowViewController implements Dumpable { return true; } - if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) { - // Don't allow touches to proceed to underlying views if alternate - // bouncer is showing - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mShadeLogger.d("NSWVC: alt bouncer showing"); - } - return true; - } - - boolean bouncerShowing; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() + boolean bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() || mAlternateBouncerInteractor.isVisibleState(); - } else { - bouncerShowing = mService.isBouncerShowing(); - } if (mPanelExpansionInteractor.isFullyExpanded() && !bouncerShowing && !mStatusBarStateController.isDozing()) { @@ -603,9 +572,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing()) { handled = !mDozeServiceHost.isPulsing(); } - if (mStatusBarKeyguardViewManager.onTouch(ev)) { - return true; - } if (MigrateClocksToBlueprint.isEnabled()) { if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) { // we still want to finish our drag down gesture when locking the screen diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt index 949d2aa36bf3..460bfbbcb3ab 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.domain.interactor +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -62,17 +63,20 @@ constructor( override val isShadeEnabled: StateFlow<Boolean> = disableFlagsRepository.disableFlags .map { it.isShadeEnabled() } + .flowName("isShadeEnabled") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isQsEnabled: StateFlow<Boolean> = disableFlagsRepository.disableFlags .map { it.isQuickSettingsEnabled() } + .flowName("isQsEnabled") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isAnyFullyExpanded: StateFlow<Boolean> = anyExpansion .map { it >= 1f } .distinctUntilChanged() + .flowName("isAnyFullyExpanded") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isShadeFullyExpanded: Flow<Boolean> = @@ -81,6 +85,7 @@ constructor( override val isShadeAnyExpanded: StateFlow<Boolean> = baseShadeInteractor.shadeExpansion .map { it > 0 } + .flowName("isShadeAnyExpanded") .stateIn(scope, SharingStarted.Eagerly, false) override val isShadeFullyCollapsed: Flow<Boolean> = @@ -88,6 +93,7 @@ constructor( override val isUserInteracting: StateFlow<Boolean> = combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs } + .flowName("isUserInteracting") .stateIn(scope, SharingStarted.Eagerly, false) override val isShadeTouchable: Flow<Boolean> = diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt index 3113dc462e6a..4bdd36773655 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.shade.ui.viewmodel -import com.android.app.tracing.coroutines.flow.map import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction @@ -33,6 +32,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map /** * Models the UI state for the user actions that the user can perform to navigate to other scenes. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 1481b734ff61..1ec5357d0d30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -15,7 +15,6 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.ExpandHelper import com.android.systemui.Gefingerpoken -import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton @@ -90,6 +89,7 @@ constructor( @get:VisibleForTesting var fractionToShade: Float = 0f private set + private var useSplitShade: Boolean = false private lateinit var nsslController: NotificationStackScrollLayoutController lateinit var centralSurfaces: CentralSurfaces @@ -161,9 +161,6 @@ constructor( val distanceUntilShowingPulsingNotifications get() = fullTransitionDistance - /** The udfpsKeyguardViewController if it exists. */ - var mUdfpsKeyguardViewControllerLegacy: UdfpsKeyguardViewControllerLegacy? = null - /** The touch helper responsible for the drag down animation. */ val touchHelper = DragDownHelper( @@ -171,7 +168,7 @@ constructor( this, naturalScrollingSettingObserver, shadeRepository, - context + context, ) private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy { @@ -448,7 +445,6 @@ constructor( val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance) shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress) - mUdfpsKeyguardViewControllerLegacy?.setTransitionToFullShadeProgress(udfpsProgress) val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance) centralSurfaces.setTransitionToFullShadeProgress(statusBarProgress) @@ -457,7 +453,7 @@ constructor( private fun setDragDownAmountAnimated( target: Float, delay: Long = 0, - endlistener: (() -> Unit)? = null + endlistener: (() -> Unit)? = null, ) { logger.logDragDownAnimation(target) val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target) @@ -553,7 +549,7 @@ constructor( private fun goToLockedShadeInternal( expandView: View?, animationHandler: ((Long) -> Unit)? = null, - cancelAction: Runnable? = null + cancelAction: Runnable? = null, ) { if (!shadeInteractor.isShadeEnabled.value) { cancelAction?.run() @@ -564,10 +560,7 @@ constructor( var entry: NotificationEntry? = null if (expandView is ExpandableNotificationRow) { entry = expandView.entry - entry.setUserExpanded( - /* userExpanded= */ true, - /* allowChildExpansion= */ true, - ) + entry.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true) // Indicate that the group expansion is changing at this time -- this way the group // and children backgrounds / divider animations will look correct. entry.setGroupExpansionChanging(true) @@ -594,9 +587,7 @@ constructor( statusBarStateController.setLeaveOpenOnKeyguardHide(false) draggedDownEntry?.apply { setUserLocked(false) - notifyHeightChanged( - /* needsAnimation= */ false, - ) + notifyHeightChanged(/* needsAnimation= */ false) draggedDownEntry = null } cancelAction?.run() @@ -614,9 +605,7 @@ constructor( // This call needs to be after updating the shade state since otherwise // the scrimstate resets too early if (animationHandler != null) { - animationHandler.invoke( - /* delay= */ 0, - ) + animationHandler.invoke(/* delay= */ 0) } else { performDefaultGoToFullShadeAnimation(0) } @@ -757,7 +746,7 @@ class DragDownHelper( private val dragDownCallback: LockscreenShadeTransitionController, private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver, private val shadeRepository: ShadeRepository, - context: Context + context: Context, ) : Gefingerpoken { private var dragDownAmountOnStart = 0.0f @@ -932,7 +921,7 @@ class DragDownHelper( @VisibleForTesting fun cancelChildExpansion( child: ExpandableView, - animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS + animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS, ) { if (child.actualHeight == child.collapsedHeight) { expandCallback.setUserLockedChild(child, false) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS index 32d37aef7707..c019f308d3e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS @@ -22,3 +22,7 @@ per-file *RemoteInput* = set noparent per-file *RemoteInput* = file:notification/OWNERS per-file *EmptyShadeView* = set noparent per-file *EmptyShadeView* = file:notification/OWNERS +per-file *Lockscreen* = set noparent +per-file *Lockscreen* = file:../keyguard/OWNERS +per-file *Scrim* = set noparent +per-file *Scrim* = file:../keyguard/OWNERS diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt index be733d4e1e8e..8ce0dbf8e171 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt @@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel import dagger.Binds import dagger.Module import dagger.Provides @@ -31,8 +31,8 @@ import dagger.multibindings.IntoMap abstract class StatusBarChipsModule { @Binds @IntoMap - @ClassKey(DemoRonChipViewModel::class) - abstract fun binds(impl: DemoRonChipViewModel): CoreStartable + @ClassKey(DemoNotifChipViewModel::class) + abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt index cce9a1624d51..5fa19ddef1be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException @@ -22,7 +22,7 @@ import android.graphics.drawable.Drawable import com.android.systemui.CoreStartable import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel @@ -37,25 +37,25 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** - * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on - * adb commands sent by the user. + * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb + * commands sent by the user. * * Example adb commands: * * To show a chip with the SysUI icon and custom text and color: * ``` - * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343" + * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343" * ``` * * To hide the chip: * ``` - * adb shell cmd statusbar demo-ron --hide + * adb shell cmd statusbar demo-notif --hide * ``` * - * See [DemoRonCommand] for more information on the adb command spec. + * See [DemoNotifCommand] for more information on the adb command spec. */ @SysUISingleton -class DemoRonChipViewModel +class DemoNotifChipViewModel @Inject constructor( private val commandRegistry: CommandRegistry, @@ -63,19 +63,19 @@ constructor( private val systemClock: SystemClock, ) : OngoingActivityChipViewModel, CoreStartable { override fun start() { - commandRegistry.registerCommand("demo-ron") { DemoRonCommand() } + commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() } } private val _chip = MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden()) override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow() - private inner class DemoRonCommand : ParseableCommand("demo-ron") { + private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) { private val packageName: String? by param( longName = "packageName", shortName = "p", - description = "The package name for the demo RON app", + description = "The package name for app \"posting\" the demo notification", valueParser = Type.String, ) @@ -99,15 +99,12 @@ constructor( ) private val hide by - flag( - longName = "hide", - description = "Hides any existing demo RON chip", - ) + flag(longName = "hide", description = "Hides any existing demo notification chip") override fun execute(pw: PrintWriter) { - if (!StatusBarRonChips.isEnabled) { + if (!StatusBarNotifChips.isEnabled) { pw.println( - "Error: com.android.systemui.status_bar_ron_chips must be enabled " + + "Error: com.android.systemui.status_bar_notification_chips must be enabled " + "before using this demo feature" ) return @@ -167,8 +164,12 @@ constructor( return null } return OngoingActivityChipModel.ChipIcon.FullColorAppIcon( - Icon.Loaded(drawable = iconDrawable, contentDescription = null), + Icon.Loaded(drawable = iconDrawable, contentDescription = null) ) } } + + companion object { + private const val DEMO_COMMAND_NAME = "demo-notif" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt index 4ef190991f19..47ffbafe3735 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.shared +package com.android.systemui.statusbar.chips.notification.shared import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils -/** Helper for reading or using the status bar RON chips flag state. */ +/** Helper for reading or using the status bar promoted notification chips flag state. */ @Suppress("NOTHING_TO_INLINE") -object StatusBarRonChips { +object StatusBarNotifChips { /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS /** A token used for dependency declaration */ val token: FlagToken @@ -33,7 +33,7 @@ object StatusBarRonChips { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.statusBarRonChips() + get() = Flags.statusBarNotificationChips() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt new file mode 100644 index 000000000000..6ae92637bde7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -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.systemui.statusbar.chips.notification.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.chips.ui.model.ColorsModel +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** A view model for status bar chips for promoted ongoing notifications. */ +@SysUISingleton +class NotifChipsViewModel +@Inject +constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) { + /** + * A flow modeling the notification chips that should be shown. Emits an empty list if there are + * no notifications that should show a status bar chip. + */ + val chips: Flow<List<OngoingActivityChipModel.Shown>> = + activeNotificationsInteractor.promotedOngoingNotifications.map { notifications -> + notifications.mapNotNull { it.toChipModel() } + } + + /** + * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the + * notification has invalid data such that it can't be displayed as a chip. + */ + private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? { + // TODO(b/364653005): Log error if there's no icon view. + val rawIcon = this.statusBarChipIconView ?: return null + val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon) + // TODO(b/364653005): Use the notification color if applicable. + val colors = ColorsModel.Themed + // TODO(b/364653005): When the chip is clicked, show the HUN. + val onClickListener = null + return OngoingActivityChipModel.Shown.ShortTimeDelta( + icon, + colors, + time = this.whenTime, + onClickListener, + ) + // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta. + // TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text. + // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`. + // TODO(b/364653005): If the app that posted the notification is in the foreground, don't + // show that app's chip. + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt index 3b1e565e122b..f4462a434477 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt @@ -21,13 +21,14 @@ import android.content.res.ColorStateList import android.graphics.drawable.GradientDrawable import android.view.View import android.view.ViewGroup +import android.widget.DateTimeView import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.view.ChipChronometer @@ -42,6 +43,8 @@ object OngoingActivityChipBinder { val chipTimeView: ChipChronometer = chipView.requireViewById(R.id.ongoing_activity_chip_time) val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text) + val chipShortTimeDeltaView: DateTimeView = + chipView.requireViewById(R.id.ongoing_activity_chip_short_time_delta) val chipBackgroundView: ChipBackgroundContainer = chipView.requireViewById(R.id.ongoing_activity_chip_background) @@ -49,13 +52,14 @@ object OngoingActivityChipBinder { is OngoingActivityChipModel.Shown -> { // Data setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView) - setChipMainContent(chipModel, chipTextView, chipTimeView) + setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView) chipView.setOnClickListener(chipModel.onClickListener) updateChipPadding( chipModel, chipBackgroundView, chipTextView, chipTimeView, + chipShortTimeDeltaView, ) // Accessibility @@ -65,6 +69,7 @@ object OngoingActivityChipBinder { val textColor = chipModel.colors.text(chipContext) chipTimeView.setTextColor(textColor) chipTextView.setTextColor(textColor) + chipShortTimeDeltaView.setTextColor(textColor) (chipBackgroundView.background as GradientDrawable).color = chipModel.colors.background(chipContext) } @@ -97,7 +102,7 @@ object OngoingActivityChipBinder { defaultIconView.tintView(iconTint) } is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> { - StatusBarRonChips.assertInNewMode() + StatusBarNotifChips.assertInNewMode() IconViewBinder.bind(icon.impl, defaultIconView) defaultIconView.visibility = View.VISIBLE defaultIconView.untintView() @@ -161,6 +166,7 @@ object OngoingActivityChipBinder { chipModel: OngoingActivityChipModel.Shown, chipTextView: TextView, chipTimeView: ChipChronometer, + chipShortTimeDeltaView: DateTimeView, ) { when (chipModel) { is OngoingActivityChipModel.Shown.Countdown -> { @@ -168,21 +174,35 @@ object OngoingActivityChipBinder { chipTextView.visibility = View.VISIBLE chipTimeView.hide() + chipShortTimeDeltaView.visibility = View.GONE } is OngoingActivityChipModel.Shown.Text -> { chipTextView.text = chipModel.text chipTextView.visibility = View.VISIBLE chipTimeView.hide() + chipShortTimeDeltaView.visibility = View.GONE } is OngoingActivityChipModel.Shown.Timer -> { ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) chipTimeView.visibility = View.VISIBLE chipTextView.visibility = View.GONE + chipShortTimeDeltaView.visibility = View.GONE + } + is OngoingActivityChipModel.Shown.ShortTimeDelta -> { + chipShortTimeDeltaView.setTime(chipModel.time) + // TODO(b/364653005): DateTimeView's relative time doesn't quite match the format we + // want in the status bar chips. + chipShortTimeDeltaView.isShowRelativeTime = true + chipShortTimeDeltaView.visibility = View.VISIBLE + + chipTextView.visibility = View.GONE + chipTimeView.hide() } is OngoingActivityChipModel.Shown.IconOnly -> { chipTextView.visibility = View.GONE + chipShortTimeDeltaView.visibility = View.GONE chipTimeView.hide() } } @@ -200,6 +220,7 @@ object OngoingActivityChipBinder { backgroundView: View, chipTextView: TextView, chipTimeView: ChipChronometer, + chipShortTimeDeltaView: DateTimeView, ) { if (chipModel.icon != null) { if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) { @@ -209,15 +230,18 @@ object OngoingActivityChipBinder { backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon() chipTextView.setTextPaddingForEmbeddedPaddingIcon() chipTimeView.setTextPaddingForEmbeddedPaddingIcon() + chipShortTimeDeltaView.setTextPaddingForEmbeddedPaddingIcon() } else { backgroundView.setBackgroundPaddingForNormalIcon() chipTextView.setTextPaddingForNormalIcon() chipTimeView.setTextPaddingForNormalIcon() + chipShortTimeDeltaView.setTextPaddingForNormalIcon() } } else { backgroundView.setBackgroundPaddingForNoIcon() chipTextView.setTextPaddingForNoIcon() chipTimeView.setTextPaddingForNoIcon() + chipShortTimeDeltaView.setTextPaddingForNoIcon() } } @@ -258,23 +282,13 @@ object OngoingActivityChipBinder { context.resources.getDimensionPixelSize( R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon ) - setPaddingRelative( - sidePadding, - paddingTop, - sidePadding, - paddingBottom, - ) + setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom) } private fun View.setBackgroundPaddingForNormalIcon() { val sidePadding = context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding) - setPaddingRelative( - sidePadding, - paddingTop, - sidePadding, - paddingBottom, - ) + setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom) } private fun View.setBackgroundPaddingForNoIcon() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt index 62622a893ec5..cf07af1fc5dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt @@ -20,6 +20,7 @@ import android.view.View import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips /** Model representing the display of an ongoing activity as a chip in the status bar. */ sealed class OngoingActivityChipModel { @@ -79,6 +80,24 @@ sealed class OngoingActivityChipModel { } /** + * The chip shows the time delta between now and [time] in a short format, e.g. "15min" or + * "1hr ago". + */ + data class ShortTimeDelta( + override val icon: ChipIcon, + override val colors: ColorsModel, + /** The time of the event that this chip represents. */ + val time: Long, + override val onClickListener: View.OnClickListener?, + ) : Shown(icon, colors, onClickListener) { + init { + StatusBarNotifChips.assertInNewMode() + } + + override val logName = "Shown.ShortTimeDelta" + } + + /** * This chip shows a countdown using [secondsUntilStarted]. Used to inform users that an * event is about to start. Typically, a [Countdown] chip will turn into a [Timer] chip. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt index 24c1b879f429..ed325970ebb2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.chips.ui.viewmodel -import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer @@ -24,19 +23,20 @@ import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.chips.StatusBarChipsLog import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.util.kotlin.combine import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -55,7 +55,8 @@ constructor( shareToAppChipViewModel: ShareToAppChipViewModel, castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, - demoRonChipViewModel: DemoRonChipViewModel, + notifChipsViewModel: NotifChipsViewModel, + demoNotifChipViewModel: DemoNotifChipViewModel, @StatusBarChipsLog private val logger: LogBuffer, ) { private enum class ChipType { @@ -63,8 +64,9 @@ constructor( ShareToApp, CastToOtherDevice, Call, - /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */ - DemoRon, + Notification, + /** A demo of a notification chip, used just for testing. */ + DemoNotification, } /** Model that helps us internally track the various chip states from each of the types. */ @@ -85,7 +87,8 @@ constructor( val shareToApp: OngoingActivityChipModel.Hidden, val castToOtherDevice: OngoingActivityChipModel.Hidden, val call: OngoingActivityChipModel.Hidden, - val demoRon: OngoingActivityChipModel.Hidden, + val notifs: OngoingActivityChipModel.Hidden, + val demoNotif: OngoingActivityChipModel.Hidden, ) : InternalChipModel } @@ -94,7 +97,8 @@ constructor( val shareToApp: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), - val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), + val notifs: List<OngoingActivityChipModel.Shown> = emptyList(), + val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), ) /** Bundles all the incoming chips into one object to easily pass to various flows. */ @@ -104,8 +108,9 @@ constructor( shareToAppChipViewModel.chip, castToOtherDeviceChipViewModel.chip, callChipViewModel.chip, - demoRonChipViewModel.chip, - ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon -> + notifChipsViewModel.chips, + demoNotifChipViewModel.chip, + ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif -> logger.log( TAG, LogLevel.INFO, @@ -121,16 +126,19 @@ constructor( LogLevel.INFO, { str1 = call.logName - str2 = demoRon.logName + // TODO(b/364653005): Log other information for notification chips. + str2 = notifs.map { it.logName }.toString() + str3 = demoNotif.logName }, - { "... > Call=$str1 > DemoRon=$str2" } + { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" }, ) ChipBundle( screenRecord = screenRecord, shareToApp = shareToApp, castToOtherDevice = castToOtherDevice, call = call, - demoRon = demoRon, + notifs = notifs, + demoNotif = demoNotif, ) } // Some of the chips could have timers in them and we don't want the start time @@ -189,9 +197,9 @@ constructor( * actually displaying the chip. */ val chips: StateFlow<MultipleOngoingActivityChipsModel> = - if (!Flags.statusBarRonChips()) { - // Multiple chips are only allowed with RONs. If the flag isn't on, use just the - // primary chip. + if (!StatusBarNotifChips.isEnabled) { + // Multiple chips are only allowed with notification chips. If the flag isn't on, use + // just the primary chip. primaryChip .map { MultipleOngoingActivityChipsModel( @@ -199,11 +207,7 @@ constructor( secondary = OngoingActivityChipModel.Hidden(), ) } - .stateIn( - scope, - SharingStarted.Lazily, - MultipleOngoingActivityChipsModel(), - ) + .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel()) } else { internalChips .pairwise(initialValue = DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL) @@ -212,11 +216,7 @@ constructor( val correctSecondary = createOutputModel(old.secondary, new.secondary) MultipleOngoingActivityChipsModel(correctPrimary, correctSecondary) } - .stateIn( - scope, - SharingStarted.Lazily, - MultipleOngoingActivityChipsModel(), - ) + .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel()) } /** A data class representing the return result of [pickMostImportantChip]. */ @@ -251,7 +251,7 @@ constructor( // suppress the share-to-app chip to make sure they don't both show. // See b/296461748. shareToApp = OngoingActivityChipModel.Hidden(), - ) + ), ) bundle.shareToApp is OngoingActivityChipModel.Shown -> MostImportantChipResult( @@ -274,11 +274,19 @@ constructor( mostImportantChip = InternalChipModel.Shown(ChipType.Call, bundle.call), remainingChips = bundle.copy(call = OngoingActivityChipModel.Hidden()), ) - bundle.demoRon is OngoingActivityChipModel.Shown -> { - StatusBarRonChips.assertInNewMode() + bundle.notifs.isNotEmpty() -> + MostImportantChipResult( + mostImportantChip = + InternalChipModel.Shown(ChipType.Notification, bundle.notifs.first()), + remainingChips = + bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)), + ) + bundle.demoNotif is OngoingActivityChipModel.Shown -> { + StatusBarNotifChips.assertInNewMode() MostImportantChipResult( - mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon), - remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()), + mostImportantChip = + InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif), + remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()), ) } else -> { @@ -287,7 +295,8 @@ constructor( check(bundle.shareToApp is OngoingActivityChipModel.Hidden) check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden) check(bundle.call is OngoingActivityChipModel.Hidden) - check(bundle.demoRon is OngoingActivityChipModel.Hidden) + check(bundle.notifs.isEmpty()) + check(bundle.demoNotif is OngoingActivityChipModel.Hidden) MostImportantChipResult( mostImportantChip = InternalChipModel.Hidden( @@ -295,7 +304,8 @@ constructor( shareToApp = bundle.shareToApp, castToOtherDevice = bundle.castToOtherDevice, call = bundle.call, - demoRon = bundle.demoRon, + notifs = OngoingActivityChipModel.Hidden(), + demoNotif = bundle.demoNotif, ), // All the chips are already hidden, so no need to filter anything out of the // bundle. @@ -323,7 +333,8 @@ constructor( ChipType.ShareToApp -> new.shareToApp ChipType.CastToOtherDevice -> new.castToOtherDevice ChipType.Call -> new.call - ChipType.DemoRon -> new.demoRon + ChipType.Notification -> new.notifs + ChipType.DemoNotification -> new.demoNotif } } else if (new is InternalChipModel.Shown) { // If we have a chip to show, always show it. @@ -344,7 +355,8 @@ constructor( shareToApp = OngoingActivityChipModel.Hidden(), castToOtherDevice = OngoingActivityChipModel.Hidden(), call = OngoingActivityChipModel.Hidden(), - demoRon = OngoingActivityChipModel.Hidden(), + notifs = OngoingActivityChipModel.Hidden(), + demoNotif = OngoingActivityChipModel.Hidden(), ) private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt new file mode 100644 index 000000000000..b64a577ef77e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import android.view.Display +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.display.data.repository.DisplayScopeRepository +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore +import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore +import com.android.systemui.util.kotlin.pairwiseBy +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch + +/** + * Responsible for creating and starting the status bar components for each display. Also does it + * for newly added displays. + */ +@SysUISingleton +class MultiDisplayStatusBarStarter +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val displayScopeRepository: DisplayScopeRepository, + private val statusBarOrchestratorFactory: StatusBarOrchestrator.Factory, + private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore, + private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + private val displayRepository: DisplayRepository, + private val initializerStore: StatusBarInitializerStore, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, + private val statusBarInitializerStore: StatusBarInitializerStore, +) : CoreStartable { + + init { + StatusBarConnectedDisplays.assertInNewMode() + } + + override fun start() { + applicationScope.launch { + displayRepository.displays + .pairwiseBy { previousDisplays, currentDisplays -> + currentDisplays - previousDisplays + } + .onStart { emit(displayRepository.displays.value) } + .collect { newDisplays -> + newDisplays.forEach { createAndStartComponentsForDisplay(it) } + } + } + } + + private fun createAndStartComponentsForDisplay(display: Display) { + val displayId = display.displayId + createAndStartOrchestratorForDisplay(displayId) + createAndStartInitializerForDisplay(displayId) + } + + private fun createAndStartOrchestratorForDisplay(displayId: Int) { + statusBarOrchestratorFactory + .create( + displayId, + displayScopeRepository.scopeForDisplay(displayId), + statusBarWindowStateRepositoryStore.forDisplay(displayId), + statusBarModeRepositoryStore.forDisplay(displayId), + initializerStore.forDisplay(displayId), + statusBarWindowControllerStore.forDisplay(displayId), + ) + .start() + } + + private fun createAndStartInitializerForDisplay(displayId: Int) { + statusBarInitializerStore.forDisplay(displayId).start() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt index 7eff8124368b..2c94632abcda 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt @@ -26,7 +26,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent -import com.android.systemui.statusbar.window.StatusBarWindowControllerStore +import com.android.systemui.statusbar.window.StatusBarWindowController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -37,7 +37,7 @@ import javax.inject.Provider * Responsible for creating the status bar window and initializing the root components of that * window (see [CollapsedStatusBarFragment]) */ -interface StatusBarInitializer { +interface StatusBarInitializer : CoreStartable { var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? @@ -68,18 +68,17 @@ interface StatusBarInitializer { } interface Factory { - fun create(displayId: Int): StatusBarInitializer + fun create(statusBarWindowController: StatusBarWindowController): StatusBarInitializer } } class StatusBarInitializerImpl @AssistedInject constructor( - @Assisted private val displayId: Int, - private val statusBarWindowControllerStore: StatusBarWindowControllerStore, + @Assisted private val statusBarWindowController: StatusBarWindowController, private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>, private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>, -) : CoreStartable, StatusBarInitializer { +) : StatusBarInitializer { private var component: StatusBarFragmentComponent? = null @get:VisibleForTesting @@ -111,7 +110,7 @@ constructor( private fun doStart() { initialized = true - statusBarWindowControllerStore.defaultDisplay.fragmentHostManager + statusBarWindowController.fragmentHostManager .addTagListener( CollapsedStatusBarFragment.TAG, object : FragmentHostManager.FragmentListener { @@ -145,6 +144,8 @@ constructor( @AssistedFactory interface Factory : StatusBarInitializer.Factory { - override fun create(displayId: Int): StatusBarInitializerImpl + override fun create( + statusBarWindowController: StatusBarWindowController + ): StatusBarInitializerImpl } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt index 8d044bb9ce87..6c3802676f26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt @@ -21,6 +21,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import kotlinx.coroutines.CoroutineName @@ -53,6 +54,7 @@ constructor( @Background private val backgroundApplicationScope: CoroutineScope, private val factory: StatusBarInitializer.Factory, private val displayRepository: DisplayRepository, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, ) : StatusBarInitializerStore, CoreStartable { init { @@ -68,7 +70,11 @@ constructor( if (displayRepository.getDisplay(displayId) == null) { throw IllegalArgumentException("Display with id $displayId doesn't exist.") } - return perDisplayInitializers.computeIfAbsent(displayId) { factory.create(displayId) } + return perDisplayInitializers.computeIfAbsent(displayId) { + factory.create( + statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId) + ) + } } override fun start() { @@ -85,15 +91,13 @@ constructor( @SysUISingleton class SingleDisplayStatusBarInitializerStore @Inject -constructor(factory: StatusBarInitializerImpl.Factory) : StatusBarInitializerStore { +constructor(private val defaultInstance: StatusBarInitializer) : StatusBarInitializerStore { init { StatusBarConnectedDisplays.assertInLegacyMode() } - private val defaultInstance = factory.create(Display.DEFAULT_DISPLAY) - override val defaultDisplay: StatusBarInitializer = defaultInstance - override fun forDisplay(displayId: Int): StatusBarInitializer = defaultDisplay + override fun forDisplay(displayId: Int): StatusBarInitializer = defaultInstance } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt index d372eb29b27b..47e6c57a5ca7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt @@ -16,12 +16,12 @@ package com.android.systemui.statusbar.core +import android.view.Display import android.view.View import com.android.systemui.CoreStartable import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.demomode.DemoModeController +import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.plugins.PluginDependencyProvider import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -31,19 +31,21 @@ import com.android.systemui.shade.ShadeSurface import com.android.systemui.statusbar.AutoHideUiElement import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.data.model.StatusBarMode -import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository import com.android.systemui.statusbar.phone.AutoHideController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarViewController -import com.android.systemui.statusbar.window.StatusBarWindowControllerStore +import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.data.model.StatusBarWindowState -import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore +import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository import com.android.wm.shell.bubbles.Bubbles import dagger.Lazy +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.io.PrintWriter import java.util.Optional -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -57,27 +59,35 @@ import kotlinx.coroutines.launch * It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The * plan is break it out into individual classes. */ -@SysUISingleton class StatusBarOrchestrator -@Inject +@AssistedInject constructor( - @Application private val applicationScope: CoroutineScope, - private val statusBarInitializer: StatusBarInitializer, - private val statusBarModeRepository: StatusBarModeRepositoryStore, - private val statusBarWindowControllerStore: StatusBarWindowControllerStore, + @Assisted private val displayId: Int, + @Assisted private val coroutineScope: CoroutineScope, + @Assisted private val statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository, + @Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository, + @Assisted private val statusBarInitializer: StatusBarInitializer, + @Assisted private val statusBarWindowController: StatusBarWindowController, private val demoModeController: DemoModeController, private val pluginDependencyProvider: PluginDependencyProvider, private val autoHideController: AutoHideController, private val remoteInputManager: NotificationRemoteInputManager, private val notificationShadeWindowViewControllerLazy: - Lazy<NotificationShadeWindowViewController>, + Lazy<NotificationShadeWindowViewController>, private val shadeSurface: ShadeSurface, private val bubblesOptional: Optional<Bubbles>, - private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore, + private val dumpManager: DumpManager, powerInteractor: PowerInteractor, primaryBouncerInteractor: PrimaryBouncerInteractor, ) : CoreStartable { + private val dumpableName: String = + if (displayId == Display.DEFAULT_DISPLAY) { + javaClass.simpleName + } else { + "${javaClass.simpleName}$displayId" + } + private val phoneStatusBarViewController = MutableStateFlow<PhoneStatusBarViewController?>(value = null) @@ -86,9 +96,9 @@ constructor( private val shouldAnimateNextBarModeChange = combine( - statusBarModeRepository.defaultDisplay.isTransientShown, + statusBarModeRepository.isTransientShown, powerInteractor.isAwake, - statusBarWindowStateRepositoryStore.defaultDisplay.windowState, + statusBarWindowStateRepository.windowState, ) { isTransientShown, isDeviceAwake, statusBarWindowState -> !isTransientShown && isDeviceAwake && @@ -107,8 +117,8 @@ constructor( private val statusBarVisible = combine( - statusBarModeRepository.defaultDisplay.statusBarMode, - statusBarWindowStateRepositoryStore.defaultDisplay.windowState, + statusBarModeRepository.statusBarMode, + statusBarWindowStateRepository.windowState, ) { mode, statusBarWindowState -> mode != StatusBarMode.LIGHTS_OUT && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT && @@ -119,7 +129,7 @@ constructor( combine( shouldAnimateNextBarModeChange, phoneStatusBarTransitions.filterNotNull(), - statusBarModeRepository.defaultDisplay.statusBarMode, + statusBarModeRepository.statusBarMode, ::Triple, ) .distinctUntilChangedBy { (_, barTransitions, statusBarMode) -> @@ -130,26 +140,29 @@ constructor( override fun start() { StatusBarSimpleFragment.assertInNewMode() - applicationScope.launch { - launch { - controllerAndBouncerShowing.collect { (controller, bouncerShowing) -> - setBouncerShowingForStatusBarComponents(controller, bouncerShowing) + coroutineScope + .launch { + dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator) + launch { + controllerAndBouncerShowing.collect { (controller, bouncerShowing) -> + setBouncerShowingForStatusBarComponents(controller, bouncerShowing) + } } - } - launch { - barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) -> - if (deviceAsleep) { - barTransitions.finishAnimations() + launch { + barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) -> + if (deviceAsleep) { + barTransitions.finishAnimations() + } } } - } - launch { statusBarVisible.collect { updateBubblesVisibility(it) } } - launch { - barModeUpdate.collect { (animate, barTransitions, statusBarMode) -> - updateBarMode(animate, barTransitions, statusBarMode) + launch { statusBarVisible.collect { updateBubblesVisibility(it) } } + launch { + barModeUpdate.collect { (animate, barTransitions, statusBarMode) -> + updateBarMode(animate, barTransitions, statusBarMode) + } } } - } + .invokeOnCompletion { dumpManager.unregisterDumpable(dumpableName) } createAndAddWindow() setupPluginDependencies() setUpAutoHide() @@ -157,7 +170,7 @@ constructor( private fun createAndAddWindow() { initializeStatusBarFragment() - statusBarWindowControllerStore.defaultDisplay.attach() + statusBarWindowController.attach() } private fun initializeStatusBarFragment() { @@ -170,6 +183,10 @@ constructor( phoneStatusBarViewController.value = statusBarViewController phoneStatusBarTransitions.value = statusBarTransitions + if (displayId != Display.DEFAULT_DISPLAY) { + return + } + // TODO(b/373310629): shade should be display id aware notificationShadeWindowViewControllerLazy .get() .setStatusBarViewController(statusBarViewController) @@ -189,6 +206,10 @@ constructor( } private fun setUpAutoHide() { + if (displayId != Display.DEFAULT_DISPLAY) { + return + } + // TODO(b/373309973): per display implementation of auto hide controller autoHideController.setStatusBar( object : AutoHideUiElement { override fun synchronizeState() {} @@ -198,13 +219,14 @@ constructor( } override fun isVisible(): Boolean { - return statusBarModeRepository.defaultDisplay.isTransientShown.value + return statusBarModeRepository.isTransientShown.value } override fun hide() { - statusBarModeRepository.defaultDisplay.clearTransient() + statusBarModeRepository.clearTransient() } - }) + } + ) } private fun updateBarMode( @@ -215,11 +237,18 @@ constructor( if (!demoModeController.isInDemoMode) { barTransitions.transitionTo(barMode.toTransitionModeInt(), animate) } - autoHideController.touchAutoHide() + if (displayId == Display.DEFAULT_DISPLAY) { + // TODO(b/373309973): per display implementation of auto hide controller + autoHideController.touchAutoHide() + } } private fun updateBubblesVisibility(statusBarVisible: Boolean) { + if (displayId != Display.DEFAULT_DISPLAY) { + return + } bubblesOptional.ifPresent { bubbles: Bubbles -> + // TODO(b/373311537): per display implementation of Bubbles bubbles.onStatusBarVisibilityChanged(statusBarVisible) } } @@ -238,11 +267,23 @@ constructor( } override fun dump(pw: PrintWriter, args: Array<out String>) { - pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value) + pw.println(statusBarWindowStateRepository.windowState.value) CentralSurfaces.dumpBarTransitions( pw, "PhoneStatusBarTransitions", phoneStatusBarTransitions.value, ) } + + @AssistedFactory + interface Factory { + fun create( + displayId: Int, + displayScope: CoroutineScope, + statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository, + statusBarModeRepository: StatusBarModePerDisplayRepository, + statusBarInitializer: StatusBarInitializer, + statusBarWindowController: StatusBarWindowController, + ): StatusBarOrchestrator + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt index 962cb0953f97..154be1f96e8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt @@ -31,6 +31,7 @@ import javax.inject.Inject interface StatusBarModeRepositoryStore { val defaultDisplay: StatusBarModePerDisplayRepository + fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository } @@ -39,7 +40,7 @@ class StatusBarModeRepositoryImpl @Inject constructor( @DisplayId private val displayId: Int, - factory: StatusBarModePerDisplayRepositoryFactory + factory: StatusBarModePerDisplayRepositoryFactory, ) : StatusBarModeRepositoryStore, CoreStartable, @@ -47,11 +48,9 @@ constructor( override val defaultDisplay = factory.create(displayId) override fun forDisplay(displayId: Int) = - if (this.displayId == displayId) { - defaultDisplay - } else { - TODO("b/127878649 implement multi-display state management") - } + // TODO(b/369337087): implement per display status bar modes. + // For now just use default display instance. + defaultDisplay override fun start() { defaultDisplay.start() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java index 0927a728783f..a61463823613 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java @@ -115,23 +115,23 @@ public class HeadsUpTouchHelper implements Gefingerpoken { final float h = y - mInitialTouchY; if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) { - setTrackingHeadsUp(true); - mCollapseSnoozes = h < 0; - mInitialTouchX = x; - mInitialTouchY = y; - int startHeight = (int) (mPickedChild.getActualHeight() - + mPickedChild.getTranslationY()); - mPanel.setHeadsUpDraggingStartingHeight(startHeight); - mPanel.startExpand(x, y, true /* startTracking */, startHeight); - if (!SceneContainerFlag.isEnabled()) { + setTrackingHeadsUp(true); + mCollapseSnoozes = h < 0; + mInitialTouchX = x; + mInitialTouchY = y; + int startHeight = (int) (mPickedChild.getActualHeight() + + mPickedChild.getTranslationY()); + mPanel.setHeadsUpDraggingStartingHeight(startHeight); + mPanel.startExpand(x, y, true /* startTracking */, startHeight); + // This call needs to be after the expansion start otherwise we will get a // flicker of one frame as it's not expanded yet. mHeadsUpManager.unpinAll(true); - } - clearNotificationEffects(); - endMotion(); + clearNotificationEffects(); + endMotion(); + } return true; } break; @@ -167,17 +167,70 @@ public class HeadsUpTouchHelper implements Gefingerpoken { @Override public boolean onTouchEvent(MotionEvent event) { - if (!mTrackingHeadsUp) { + if (SceneContainerFlag.isEnabled()) { + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float x = event.getX(pointerIndex); + final float y = event.getY(pointerIndex); + switch (event.getActionMasked()) { + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + mTrackingPointer = event.getPointerId(newIndex); + mInitialTouchX = event.getX(newIndex); + mInitialTouchY = event.getY(newIndex); + } + break; + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop + && Math.abs(h) > Math.abs(x - mInitialTouchX)) { + setTrackingHeadsUp(true); + mCollapseSnoozes = h < 0; + mInitialTouchX = x; + mInitialTouchY = y; + int startHeight = (int) (mPickedChild.getActualHeight() + + mPickedChild.getTranslationY()); + mPanel.setHeadsUpDraggingStartingHeight(startHeight); + mPanel.startExpand(x, y, true /* startTracking */, startHeight); + + clearNotificationEffects(); + endMotion(); + return true; + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mPickedChild != null && mTouchingHeadsUpView) { + // We may swallow this click if the heads up just came in. + if (mHeadsUpManager.shouldSwallowClick( + mPickedChild.getEntry().getSbn().getKey())) { + endMotion(); + return true; + } + } + endMotion(); + return false; + } return false; + } else { + if (!mTrackingHeadsUp) { + return false; + } + switch (event.getActionMasked()) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + endMotion(); + setTrackingHeadsUp(false); + break; + } + return true; } - switch (event.getActionMasked()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - endMotion(); - setTrackingHeadsUp(false); - break; - } - return true; } private void endMotion() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt index 9b382e61b2e3..697a6ce52ba9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel @@ -26,6 +27,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -74,6 +76,17 @@ constructor( val allNotificationsCountValue: Int get() = repository.activeNotifications.value.individuals.size + /** The notifications that are promoted and ongoing. Sorted by priority order. */ + val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> = + if (StatusBarNotifChips.isEnabled) { + // TODO(b/364653005): Filter all the notifications down to just the promoted ones. + // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow + // instead of being separate. + topLevelRepresentativeNotifications + } else { + flowOf(emptyList()) + } + /** * The priority ongoing call notification, or null if there is no ongoing call. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt index 695e088b5f8b..fbec6406e9d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel import android.content.Context import android.icu.text.MessageFormat -import com.android.app.tracing.coroutines.flow.flowOn import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.modes.shared.ModesUi @@ -40,6 +39,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 560028cb5640..d538f52fd637 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -449,7 +449,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } // We need to reset the View state, even if the animation was cancelled - onAppearAnimationFinished(isAppearing); + onAppearAnimationFinished(isAppearing, /* cancelled = */ !mRunWithoutInterruptions); if (mRunWithoutInterruptions) { InteractionJankMonitor.getInstance().end(getCujType(isAppearing)); @@ -463,6 +463,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (onStartedRunnable != null) { onStartedRunnable.run(); } + onAppearAnimationStarted(isAppearing); mRunWithoutInterruptions = true; Configuration.Builder builder = Configuration.Builder .withView(getCujType(isAppearing), ActivatableNotificationView.this); @@ -486,6 +487,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView frameTimeNanos -> { if (mAppearAnimator == cachedAnimator) { mAppearAnimator.start(); + } else { + onAppearAnimationSkipped(isAppearing); } }, delay); } @@ -502,7 +505,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } - protected void onAppearAnimationFinished(boolean wasAppearing) { + protected void onAppearAnimationStarted(boolean isAppear) { + } + + protected void onAppearAnimationSkipped(boolean isAppear) { + } + + protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) { } private void cancelAppearAnimation() { @@ -830,4 +839,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView }); } } + + protected void dumpAppearAnimationProperties(IndentingPrintWriter pw, String[] args) { + pw.print("AppearAnimation: "); + pw.print("mDrawingAppearAnimation", mDrawingAppearAnimation); + pw.print("mAppearAnimationFraction", mAppearAnimationFraction); + pw.print("mIsHeadsUpAnimation", mIsHeadsUpAnimation); + pw.print("mTargetPoint", mTargetPoint); + pw.println(); + } } 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 49153d1d8eef..38e66099022c 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 @@ -1835,6 +1835,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView void logSkipResetAllContentAlphas( NotificationEntry entry ); + + /** Called when we start an appear animation. */ + void logStartAppearAnimation(NotificationEntry entry, boolean isAppear); + + /** Called when we cancel the running appear animation. */ + void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing); + + /** Called when the animator of the appear animation is started. */ + void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear); + + /** Called when we prepared an appear animation, but the animator was never started. */ + void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear); + + /** Called when the animator of the appear animation is finished. */ + void logAppearAnimationFinished( + NotificationEntry entry, + boolean isAppear, + boolean cancelled + ); } /** @@ -3165,6 +3184,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear, + Runnable onFinishRunnable) { + mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ true); + super.performAddAnimation(delay, duration, isHeadsUpAppear, onFinishRunnable); + } + + @Override public long performRemoveAnimation( long duration, long delay, @@ -3173,6 +3199,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView Runnable onStartedRunnable, Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener, ClipSide clipSide) { + mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ false); if (mMenuRow != null && mMenuRow.isMenuVisible()) { Animator anim = getTranslateViewAnimator(0f, null /* listener */); if (anim != null) { @@ -3201,8 +3228,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - protected void onAppearAnimationFinished(boolean wasAppearing) { - super.onAppearAnimationFinished(wasAppearing); + protected void onAppearAnimationStarted(boolean isAppear) { + mLogger.logAppearAnimationStarted(getEntry(), /* isAppear = */ isAppear); + super.onAppearAnimationStarted(isAppear); + } + + @Override + protected void onAppearAnimationSkipped(boolean isAppear) { + mLogger.logAppearAnimationSkipped(getEntry(), /* isAppear = */ isAppear); + super.onAppearAnimationSkipped(isAppear); + } + + @Override + protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) { + mLogger.logAppearAnimationFinished( + /* entry = */ getEntry(), + /* isAppear = */ wasAppearing, + /* cancelled = */ cancelled + ); + super.onAppearAnimationFinished(wasAppearing, cancelled); if (wasAppearing) { // During the animation the visible view might have changed, so let's make sure all // alphas are reset @@ -3218,6 +3262,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public void cancelAppearDrawing() { + mLogger.logCancelAppearDrawing(getEntry(), isDrawingAppearAnimation()); + super.cancelAppearDrawing(); + } + + @Override public void resetAllContentAlphas() { mLogger.logResetAllContentAlphas(getEntry()); mPrivateLayout.setAlpha(1f); @@ -3883,6 +3933,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView dumpHeights(pw); } showingLayout.dump(pw, args); + dumpAppearAnimationProperties(pw, args); dumpCustomOutline(pw, args); dumpClipping(pw, args); if (getViewState() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index c31a2cb8908b..23a2facf4c5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -209,6 +209,32 @@ public class ExpandableNotificationRowController implements NotifViewController ) { mLogBufferLogger.logSkipResetAllContentAlphas(entry); } + + @Override + public void logStartAppearAnimation(NotificationEntry entry, boolean isAppear) { + mLogBufferLogger.logStartAppearAnimation(entry, isAppear); + } + + @Override + public void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing) { + mLogBufferLogger.logCancelAppearDrawing(entry, wasDrawing); + } + + @Override + public void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear) { + mLogBufferLogger.logAppearAnimationStarted(entry, isAppear); + } + + @Override + public void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear) { + mLogBufferLogger.logAppearAnimationSkipped(entry, isAppear); + } + + @Override + public void logAppearAnimationFinished(NotificationEntry entry, boolean isAppear, + boolean cancelled) { + mLogBufferLogger.logAppearAnimationFinished(entry, isAppear, cancelled); + } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt index b90aa107d617..71f9c07ba2d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row import android.widget.flags.Flags.notifLinearlayoutOptimized import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing import javax.inject.Inject import javax.inject.Provider @@ -35,6 +36,7 @@ constructor( bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory, optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory, notificationViewFlipperFactory: Provider<NotificationViewFlipperFactory>, + notificationRowIconViewInflaterFactory: NotificationRowIconViewInflaterFactory, ) : NotifRemoteViewsFactoryContainer { override val factories: Set<NotifRemoteViewsFactory> = buildSet { add(precomputedTextViewFactory) @@ -47,5 +49,8 @@ constructor( if (NotificationViewFlipperPausing.isEnabled) { add(notificationViewFlipperFactory.get()) } + if (android.app.Flags.notificationsRedesignAppIcons()) { + add(notificationRowIconViewInflaterFactory) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index e10fd8f3b3fb..41abac1d47f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -342,7 +342,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder * Cancel any pending content view frees from {@link #freeNotificationView} for the provided * content views. * - * @param row top level notification row containing the content views + * @param row top level notification row containing the content views * @param contentViews content views to cancel pending frees on */ private void cancelContentViewFrees( @@ -478,6 +478,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP)); setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC)); + if (android.app.Flags.notificationsRedesignAppIcons()) { + setRemoteViewsInflaterFactory(result.mNewGroupHeaderView, + notifLayoutInflaterFactoryProvider.provide(row, FLAG_GROUP_SUMMARY_HEADER)); + setRemoteViewsInflaterFactory(result.mNewMinimizedGroupHeaderView, + notifLayoutInflaterFactoryProvider.provide(row, + FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)); + } } private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews, @@ -516,6 +523,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder logger.logAsyncTaskProgress(entry, "contracted view applied"); result.inflatedContentView = v; } + @Override public RemoteViews getRemoteView() { return result.newContentView; @@ -1406,6 +1414,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder @VisibleForTesting abstract static class ApplyCallback { public abstract void setResultView(View v); + public abstract RemoteViews getRemoteView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt index b1e90329e01a..6e78f561cfc1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt @@ -30,7 +30,7 @@ class NotificationRowLogger @Inject constructor( @NotificationLog private val buffer: LogBuffer, - @NotificationRenderLog private val notificationRenderBuffer: LogBuffer + @NotificationRenderLog private val notificationRenderBuffer: LogBuffer, ) { fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) { buffer.log( @@ -40,7 +40,7 @@ constructor( str1 = child.logKey str2 = oldParent.logKey }, - { "Detach child $str1 kept in parent $str2" } + { "Detach child $str1 kept in parent $str2" }, ) } @@ -52,13 +52,13 @@ constructor( str1 = child.logKey str2 = newParent.logKey }, - { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" } + { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }, ) } fun logRemoveTransientFromContainer( childEntry: NotificationEntry, - containerEntry: NotificationEntry + containerEntry: NotificationEntry, ) { notificationRenderBuffer.log( TAG, @@ -67,25 +67,20 @@ constructor( str1 = childEntry.logKey str2 = containerEntry.logKey }, - { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" } + { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" }, ) } - fun logRemoveTransientFromNssl( - childEntry: NotificationEntry, - ) { + fun logRemoveTransientFromNssl(childEntry: NotificationEntry) { notificationRenderBuffer.log( TAG, LogLevel.INFO, { str1 = childEntry.logKey }, - { "RemoveTransientRow from Nssl: childKey: $str1" } + { "RemoveTransientRow from Nssl: childKey: $str1" }, ) } - fun logRemoveTransientFromViewGroup( - childEntry: NotificationEntry, - containerView: ViewGroup, - ) { + fun logRemoveTransientFromViewGroup(childEntry: NotificationEntry, containerView: ViewGroup) { notificationRenderBuffer.log( TAG, LogLevel.WARNING, @@ -93,14 +88,14 @@ constructor( str1 = childEntry.logKey str2 = containerView.toString() }, - { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" } + { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" }, ) } fun logAddTransientRow( childEntry: NotificationEntry, containerEntry: NotificationEntry, - index: Int + index: Int, ) { notificationRenderBuffer.log( TAG, @@ -110,14 +105,11 @@ constructor( str2 = containerEntry.logKey int1 = index }, - { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" } + { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" }, ) } - fun logRemoveTransientRow( - childEntry: NotificationEntry, - containerEntry: NotificationEntry, - ) { + fun logRemoveTransientRow(childEntry: NotificationEntry, containerEntry: NotificationEntry) { notificationRenderBuffer.log( TAG, LogLevel.ERROR, @@ -125,7 +117,7 @@ constructor( str1 = childEntry.logKey str2 = containerEntry.logKey }, - { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" } + { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }, ) } @@ -134,7 +126,7 @@ constructor( TAG, LogLevel.INFO, { str1 = entry.logKey }, - { "resetAllContentAlphas: $str1" } + { "resetAllContentAlphas: $str1" }, ) } @@ -143,7 +135,72 @@ constructor( TAG, LogLevel.INFO, { str1 = entry.logKey }, - { "Skip resetAllContentAlphas: $str1" } + { "Skip resetAllContentAlphas: $str1" }, + ) + } + + fun logStartAppearAnimation(entry: NotificationEntry, isAppear: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + bool1 = isAppear + }, + { "startAppearAnimation childKey: $str1 isAppear:$bool1" }, + ) + } + + fun logCancelAppearDrawing(entry: NotificationEntry, wasDrawing: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.WARNING, + { + str1 = entry.logKey + bool1 = wasDrawing + }, + { "cancelAppearDrawing childKey: $str1 wasDrawing:$bool1" }, + ) + } + + fun logAppearAnimationStarted(entry: NotificationEntry, isAppear: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + bool1 = isAppear + }, + { "onAppearAnimationStarted childKey: $str1 isAppear:$bool1" }, + ) + } + + fun logAppearAnimationSkipped(entry: NotificationEntry, isAppear: Boolean) { + notificationRenderBuffer.log( + TAG, + LogLevel.WARNING, + { + str1 = entry.logKey + bool1 = isAppear + }, + { "Skipped an appear animation childKey: $str1 isAppear:$bool1" }, + ) + } + + fun logAppearAnimationFinished( + entry: NotificationEntry, + isAppear: Boolean, + cancelled: Boolean, + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = entry.logKey + bool1 = isAppear + bool2 = cancelled + }, + { "onAppearAnimationFinished childKey: $str1 isAppear:$bool1 cancelled:$bool2" }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java index 84f2f6670839..4feb78a46d60 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.notification.row; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.notification.row.icon.AppIconProviderModule; +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderModule; import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import dagger.Binds; @@ -28,7 +30,7 @@ import javax.inject.Provider; /** * Dagger Module containing notification row and view inflation implementations. */ -@Module +@Module(includes = {AppIconProviderModule.class, NotificationIconStyleProviderModule.class}) public abstract class NotificationRowModule { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt new file mode 100644 index 000000000000..24b5cf1aa33b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt @@ -0,0 +1,86 @@ +/* + * 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.icon + +import android.app.ActivityManager +import android.app.Flags +import android.content.Context +import android.content.pm.PackageManager.NameNotFoundException +import android.graphics.Color +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.util.Log +import com.android.internal.R +import com.android.launcher3.icons.BaseIconFactory +import com.android.systemui.dagger.SysUISingleton +import dagger.Module +import dagger.Provides +import javax.inject.Inject +import javax.inject.Provider + +/** A provider used to cache and fetch app icons used by notifications. */ +interface AppIconProvider { + @Throws(NameNotFoundException::class) + fun getOrFetchAppIcon(packageName: String, context: Context): Drawable +} + +@SysUISingleton +class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider { + private val iconFactory: BaseIconFactory + get() { + val isLowRam = ActivityManager.isLowRamDeviceStatic() + val res = sysuiContext.resources + val iconSize: Int = + res.getDimensionPixelSize( + if (isLowRam) R.dimen.notification_small_icon_size_low_ram + else R.dimen.notification_small_icon_size + ) + return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize) + } + + override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + val icon = context.packageManager.getApplicationIcon(packageName) + return BitmapDrawable( + context.resources, + iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE), + ) + } +} + +class NoOpIconProvider : AppIconProvider { + companion object { + const val TAG = "NoOpIconProvider" + } + + override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.") + return ColorDrawable(Color.WHITE) + } +} + +@Module +class AppIconProviderModule { + @Provides + @SysUISingleton + fun provideImpl(realImpl: Provider<AppIconProviderImpl>): AppIconProvider = + if (Flags.notificationsRedesignAppIcons()) { + realImpl.get() + } else { + NoOpIconProvider() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt new file mode 100644 index 000000000000..165c1a7803a9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import android.annotation.WorkerThread +import android.app.Flags +import android.content.Context +import android.content.pm.ApplicationInfo +import android.service.notification.StatusBarNotification +import android.util.Log +import com.android.systemui.dagger.SysUISingleton +import dagger.Module +import dagger.Provides +import javax.inject.Inject +import javax.inject.Provider + +/** + * A provider used to cache and fetch information about which icon should be displayed by + * notifications. + */ +interface NotificationIconStyleProvider { + @WorkerThread + fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean +} + +@SysUISingleton +class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIconStyleProvider { + override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean { + val packageContext = notification.getPackageContext(context) + return !belongsToHeadlessSystemApp(packageContext) + } + + @WorkerThread + private fun belongsToHeadlessSystemApp(context: Context): Boolean { + val info = context.applicationInfo + if (info != null) { + if ((info.flags and ApplicationInfo.FLAG_SYSTEM) == 0) { + // It's not a system app at all. + return false + } else { + // If there's no launch intent, it's probably a headless app. + val pm = context.packageManager + return (pm.getLaunchIntentForPackage(info.packageName) == null) + } + } else { + // If for some reason we don't have the app info, we don't know; best assume it's + // not a system app. + return false + } + } +} + +class NoOpIconStyleProvider : NotificationIconStyleProvider { + companion object { + const val TAG = "NoOpIconStyleProvider" + } + + override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean { + Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.") + return true + } +} + +@Module +class NotificationIconStyleProviderModule { + @Provides + @SysUISingleton + fun provideImpl( + realImpl: Provider<NotificationIconStyleProviderImpl> + ): NotificationIconStyleProvider = + if (Flags.notificationsRedesignAppIcons()) { + realImpl.get() + } else { + NoOpIconStyleProvider() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt new file mode 100644 index 000000000000..79defd255de0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt @@ -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.systemui.statusbar.notification.row.icon + +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.View +import com.android.internal.widget.NotificationRowIconView +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder +import javax.inject.Inject + +/** + * A factory which owns the construction of any NotificationRowIconView inside of Notifications in + * SystemUI. This allows overriding the small icon with the app icon in notifications. + */ +class NotificationRowIconViewInflaterFactory +@Inject +constructor( + private val appIconProvider: AppIconProvider, + private val iconStyleProvider: NotificationIconStyleProvider, +) : NotifRemoteViewsFactory { + override fun instantiate( + row: ExpandableNotificationRow, + @NotificationRowContentBinder.InflationFlag layoutType: Int, + parent: View?, + name: String, + context: Context, + attrs: AttributeSet, + ): View? { + return when (name) { + NotificationRowIconView::class.java.name -> + NotificationRowIconView(context, attrs).also { view -> + val sbn = row.entry.sbn + view.setIconProvider( + object : NotificationRowIconView.NotificationIconProvider { + override fun shouldShowAppIcon(): Boolean { + return iconStyleProvider.shouldShowAppIcon(row.entry.sbn, context) + } + + override fun getAppIcon(): Drawable { + return appIconProvider.getOrFetchAppIcon(sbn.packageName, context) + } + } + ) + } + else -> null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 00c5c40fc8ac..379a67ed3942 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -2122,9 +2122,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { boolean hunWantsIt = false; if (shouldHeadsUpHandleTouch()) { hunWantsIt = mHeadsUpTouchHelper.onInterceptTouchEvent(ev); - if (hunWantsIt) { - mView.startDraggingOnHun(); - } } boolean swipeWantsIt = false; if (mLongPressedView == null && !mView.isBeingDragged() @@ -2210,6 +2207,9 @@ public class NotificationStackScrollLayoutController implements Dumpable { boolean hunWantsIt = false; if (shouldHeadsUpHandleTouch()) { hunWantsIt = mHeadsUpTouchHelper.onTouchEvent(ev); + if (hunWantsIt) { + mView.startDraggingOnHun(); + } } // Check if we need to clear any snooze leavebehinds diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt index 87d70ba12012..fb42ee7908b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt @@ -17,7 +17,8 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder import android.util.Log -import com.android.app.tracing.coroutines.flow.filter +import com.android.app.tracing.coroutines.flow.collectTraced +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.dagger.SysUISingleton @@ -37,7 +38,6 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch /** Binds the [NotificationScrollView]. */ @SysUISingleton @@ -79,39 +79,39 @@ constructor( launch { viewModel .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset) - .collect { view.setScrimClippingShape(it) } + .collectTraced { view.setScrimClippingShape(it) } } - launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } } - launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } } + launch { viewModel.maxAlpha.collectTraced { view.setMaxAlpha(it) } } + launch { viewModel.scrolledToTop.collectTraced { view.setScrolledToTop(it) } } launch { - viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } + viewModel.expandFraction.collectTraced { view.setExpandFraction(it.coerceIn(0f, 1f)) } } - launch { viewModel.qsExpandFraction.collect { view.setQsExpandFraction(it) } } + launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } } launch { - viewModel.isShowingStackOnLockscreen.collect { + viewModel.isShowingStackOnLockscreen.collectTraced { view.setShowingStackOnLockscreen(it) } } launch { - viewModel.alphaForLockscreenFadeIn.collect { view.setAlphaForLockscreenFadeIn(it) } + viewModel.alphaForLockscreenFadeIn.collectTraced { view.setAlphaForLockscreenFadeIn(it) } } - launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } } - launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } } + launch { viewModel.isScrollable.collectTraced { view.setScrollingEnabled(it) } } + launch { viewModel.isDozing.collectTraced { isDozing -> view.setDozing(isDozing) } } launch { - viewModel.isPulsing.collect { isPulsing -> + viewModel.isPulsing.collectTraced { isPulsing -> view.setPulsing(isPulsing, viewModel.shouldAnimatePulse.value) } } launch { viewModel.shouldResetStackTop .filter { it } - .collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) } + .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) } } launch { - viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() } + viewModel.shouldCloseGuts.filter { it }.collectTraced { view.closeGutsOnSceneTouch() } } - launch { viewModel.suppressHeightUpdates.collect { view.suppressHeightUpdates(it) } } + launch { viewModel.suppressHeightUpdates.collectTraced { view.suppressHeightUpdates(it) } } launchAndDispose { view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer) 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 f39af18afcea..878ae91391e9 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 @@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton @@ -158,6 +159,7 @@ constructor( ) { shadeExpansion, qsExpansion -> shadeExpansion || qsExpansion } + .flowName("isAnyExpanded") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -175,6 +177,7 @@ constructor( isAnyExpanded -> isShadeLocked && isAnyExpanded } + .flowName("isShadeLocked") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -227,6 +230,7 @@ constructor( ), keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f }, ) + .flowName("isOnLockscreen") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -239,6 +243,7 @@ constructor( combine(isOnLockscreen, isAnyExpanded) { isKeyguard, isAnyExpanded -> isKeyguard && !isAnyExpanded } + .flowName("isOnLockscreenWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -274,6 +279,7 @@ constructor( combine(isOnGlanceableHub, isAnyExpanded) { isGlanceableHub, isAnyExpanded -> isGlanceableHub && !isAnyExpanded } + .flowName("isOnGlanceableHubWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -288,6 +294,7 @@ constructor( isAnyExpanded -> isDreaming && !isAnyExpanded } + .flowName("isDreamingWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -345,6 +352,7 @@ constructor( } } } + .flowName("shadeCollapseFadeIn") .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), @@ -382,6 +390,7 @@ constructor( bounds.copy(top = top, isAnimated = animate) } } + .flowName("bounds") .stateIn( scope = applicationScope, started = SharingStarted.Lazily, @@ -495,6 +504,7 @@ constructor( // flatMapLatest below, the last value gets emitted, to avoid the randomness of `merge`. val alphaForTransitionsAndShade = merge(alphaForTransitions(viewState), alphaForShadeAndQsExpansion) + .flowName("alphaForTransitionsAndShade") .stateIn( // Use view-level scope instead of ApplicationScope, to prevent collection that // never stops diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 316e1f13bc2b..a34ac2e11c2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -22,7 +22,7 @@ import android.content.res.Resources import android.hardware.biometrics.BiometricSourceType import android.provider.Settings import com.android.app.tracing.ListenersTracing.forEachTraced -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application 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 069c6241491c..40e52935daf1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -1715,26 +1715,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump updateScrims(); } - public void setHasBackdrop(boolean hasBackdrop) { - for (ScrimState state : ScrimState.values()) { - state.setHasBackdrop(hasBackdrop); - } - - // Backdrop event may arrive after state was already applied, - // in this case, back-scrim needs to be re-evaluated - if (mState == ScrimState.AOD || mState == ScrimState.PULSING) { - float newBehindAlpha = mState.getBehindAlpha(); - if (isNaN(newBehindAlpha)) { - throw new IllegalStateException("Scrim opacity is NaN for state: " + mState - + ", back: " + mBehindAlpha); - } - if (mBehindAlpha != newBehindAlpha) { - mBehindAlpha = newBehindAlpha; - updateScrims(); - } - } - } - private void setKeyguardFadingAway(boolean fadingAway, long duration) { for (ScrimState state : ScrimState.values()) { state.setKeyguardFadingAway(fadingAway, duration); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index fbba3dc107f1..a0ab6120f372 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -210,7 +210,7 @@ public enum ScrimState { @Override public float getMaxLightRevealScrimAlpha() { - return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f; + return mWallpaperSupportsAmbientMode ? 0f : 1f; } @Override @@ -369,7 +369,6 @@ public enum ScrimState { DockManager mDockManager; boolean mDisplayRequiresBlanking; boolean mWallpaperSupportsAmbientMode; - boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; boolean mOccludeAnimationPlaying; boolean mWakeLockScreenSensorActive; @@ -489,10 +488,6 @@ public enum ScrimState { return false; } - public void setHasBackdrop(boolean hasBackdrop) { - mHasBackdrop = hasBackdrop; - } - public void setWakeLockScreenSensorActive(boolean active) { mWakeLockScreenSensorActive = active; } 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 17bd53869ee5..74c6e72d3400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -47,7 +47,6 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; @@ -68,7 +67,6 @@ import com.android.systemui.bouncer.util.BouncerTestUtilsKt; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.keyguard.DismissCallbackRegistry; @@ -77,9 +75,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInte import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.DismissAction; -import com.android.systemui.keyguard.shared.model.Edge; import com.android.systemui.keyguard.shared.model.KeyguardDone; -import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; @@ -165,7 +161,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DreamOverlayStateController mDreamOverlayStateController; @Nullable private final FoldAodAnimationController mFoldAodAnimationController; - KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController; private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; private final AlternateBouncerInteractor mAlternateBouncerInteractor; @@ -463,11 +458,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb onPanelExpansionChanged(currentState); } mNotificationContainer = notificationContainer; - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create( - centralSurfaces.getKeyguardMessageArea()); - } - mCentralSurfacesRegistered = true; registerListeners(); @@ -518,24 +508,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mListenForCanShowAlternateBouncer.cancel(null); } mListenForCanShowAlternateBouncer = null; - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow( - mKeyguardTransitionInteractor - .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)), - this::consumeFromAlternateBouncerTransitionSteps - ); - - mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow( - mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(), - this::consumeKeyguardAuthenticatedBiometricsHandled - ); - } else { - // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. - mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( - mAlternateBouncerInteractor.getCanShowAlternateBouncer(), - this::consumeCanShowAlternateBouncer - ); - } + // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. + mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( + mAlternateBouncerInteractor.getCanShowAlternateBouncer(), + this::consumeCanShowAlternateBouncer + ); if (KeyguardWmStateRefactor.isEnabled()) { // Show the keyguard views whenever we've told WM that the lockscreen is visible. @@ -792,21 +769,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { - Log.d(TAG, "showBouncer:alternateBouncer.forceShow()"); - mAlternateBouncerInteractor.forceShow(); - updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); - } else { - showPrimaryBouncer(scrimmed); - } - return; - } - - if (!mAlternateBouncerInteractor.show()) { - showPrimaryBouncer(scrimmed); - } else { + if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { + Log.d(TAG, "showBouncer:alternateBouncer.forceShow()"); + mAlternateBouncerInteractor.forceShow(); updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); + } else { + showPrimaryBouncer(scrimmed); } } @@ -921,13 +889,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardGoneCancelAction = null; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()"); - mAlternateBouncerInteractor.forceShow(); - updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); - } else { - updateAlternateBouncerShowing(mAlternateBouncerInteractor.show()); - } + Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()"); + mAlternateBouncerInteractor.forceShow(); + updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); setKeyguardMessage(message, null, null); return; } @@ -1033,11 +997,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState(); - if (mKeyguardMessageAreaController != null) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer); - mKeyguardMessageAreaController.setMessage(""); - } if (!SceneContainerFlag.isEnabled()) { mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer); } @@ -1646,12 +1605,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /** Display security message to relevant KeyguardMessageArea. */ public void setKeyguardMessage(String message, ColorStateList colorState, BiometricSourceType biometricSourceType) { - if (mAlternateBouncerInteractor.isVisibleState()) { - if (mKeyguardMessageAreaController != null) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - mKeyguardMessageAreaController.setMessage(message, biometricSourceType); - } - } else { + if (!mAlternateBouncerInteractor.isVisibleState()) { mPrimaryBouncerInteractor.showMessage(message, colorState); } } @@ -1778,66 +1732,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } - /** - * An opportunity for the AlternateBouncer to handle the touch instead of sending - * the touch to NPVC child views. - * @return true if the alternate bouncer should consime the touch and prevent it from - * going to its child views - */ - public boolean dispatchTouchEvent(MotionEvent event) { - if (shouldInterceptTouchEvent(event) - && !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) { - onTouch(event); - } - return shouldInterceptTouchEvent(event); - } - - /** - * Whether the touch should be intercepted by the AlternateBouncer before going to the - * notification shade's child views. - */ - public boolean shouldInterceptTouchEvent(MotionEvent event) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - return false; - } - return mAlternateBouncerInteractor.isVisibleState(); - } - - /** - * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently - * showing. - */ - public boolean onTouch(MotionEvent event) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - return false; - } - - boolean handleTouch = shouldInterceptTouchEvent(event); - if (handleTouch) { - final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN; - final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch() - && event.getActionMasked() == MotionEvent.ACTION_UP; - final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade = - mKeyguardUpdateManager.isUdfpsEnrolled(); - final boolean actionOutsideShouldDismissAlternateBouncer = - event.getActionMasked() == MotionEvent.ACTION_OUTSIDE - && !udfpsOverlayWillForwardEventsOutsideNotificationShade; - if (actionDown) { - mAlternateBouncerInteractor.setReceivedDownTouch(true); - } else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer) - && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) { - showPrimaryBouncer(true); - } - } - - // Forward NPVC touches to callbacks in case they want to respond to touches - for (KeyguardViewManagerCallback callback: mCallbacks) { - callback.onTouch(event); - } - - return handleTouch; - } - /** Update keyguard position based on a tapped X coordinate. */ public void updateKeyguardPosition(float x) { mPrimaryBouncerInteractor.setKeyguardPosition(x); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 5f864e5dc53a..09e191dd1911 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -18,10 +18,12 @@ package com.android.systemui.statusbar.phone.dagger import android.view.Display import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Default import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.core.CommandQueueInitializer import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore +import com.android.systemui.statusbar.core.MultiDisplayStatusBarStarter import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.core.StatusBarInitializer @@ -29,7 +31,9 @@ import com.android.systemui.statusbar.core.StatusBarInitializerImpl import com.android.systemui.statusbar.core.StatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarOrchestrator import com.android.systemui.statusbar.core.StatusBarSimpleFragment +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl import dagger.Binds @@ -38,6 +42,7 @@ import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap +import kotlinx.coroutines.CoroutineScope /** Similar in purpose to [StatusBarModule], but scoped only to phones */ @Module @@ -58,24 +63,56 @@ interface StatusBarPhoneModule { implFactory: StatusBarInitializerImpl.Factory ): StatusBarInitializer.Factory - /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */ - @Binds - @IntoMap - @ClassKey(StatusBarInitializerImpl::class) - fun bindStatusBarInitializer(@Default impl: StatusBarInitializerImpl): CoreStartable - @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer companion object { + /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */ + @Provides + @SysUISingleton + @IntoMap + @ClassKey(StatusBarInitializer::class) + fun bindStatusBarInitializer( + @Default defaultInitializerLazy: Lazy<StatusBarInitializerImpl> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + // Will be started through MultiDisplayStatusBarStarter + CoreStartable.NOP + } else { + defaultInitializerLazy.get() + } + } + // Dagger doesn't support providing AssistedInject types, without a qualifier. Using the // Default qualifier for this reason. @Default @Provides @SysUISingleton fun statusBarInitializerImpl( - implFactory: StatusBarInitializerImpl.Factory + implFactory: StatusBarInitializerImpl.Factory, + statusBarWindowControllerStore: StatusBarWindowControllerStore, ): StatusBarInitializerImpl { - return implFactory.create(displayId = Display.DEFAULT_DISPLAY) + return implFactory.create(statusBarWindowControllerStore.defaultDisplay) + } + + @Provides + @SysUISingleton + @Default // Dagger does not support providing @AssistedInject types without a qualifier + fun orchestrator( + @Background backgroundApplicationScope: CoroutineScope, + statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore, + statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + initializerStore: StatusBarInitializerStore, + statusBarWindowControllerStore: StatusBarWindowControllerStore, + statusBarOrchestratorFactory: StatusBarOrchestrator.Factory, + ): StatusBarOrchestrator { + return statusBarOrchestratorFactory.create( + Display.DEFAULT_DISPLAY, + backgroundApplicationScope, + statusBarWindowStateRepositoryStore.defaultDisplay, + statusBarModeRepositoryStore.defaultDisplay, + initializerStore.defaultDisplay, + statusBarWindowControllerStore.defaultDisplay, + ) } @Provides @@ -83,11 +120,29 @@ interface StatusBarPhoneModule { @IntoMap @ClassKey(StatusBarOrchestrator::class) fun orchestratorCoreStartable( - orchestratorLazy: Lazy<StatusBarOrchestrator> + @Default orchestratorLazy: Lazy<StatusBarOrchestrator> ): CoreStartable { - return if (StatusBarSimpleFragment.isEnabled) { + return if (StatusBarConnectedDisplays.isEnabled) { + // Will be started through MultiDisplayStatusBarStarter + CoreStartable.NOP + } else if (StatusBarSimpleFragment.isEnabled) { orchestratorLazy.get() } else { + // Will be started through CentralSurfacesImpl + CoreStartable.NOP + } + } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(MultiDisplayStatusBarStarter::class) + fun multiDisplayStarter( + multiDisplayStatusBarStarterLazy: Lazy<MultiDisplayStatusBarStarter> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayStatusBarStarterLazy.get() + } else { CoreStartable.NOP } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index c258095510c6..d868519ee9e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -29,6 +29,7 @@ import android.telephony.SubscriptionManager; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.SparseArray; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -55,7 +56,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameView; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarSimpleFragment; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; @@ -333,7 +334,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - mDumpManager.registerDumpable(getClass().getSimpleName(), this); + mDumpManager.registerDumpable(getDumpableName(), this); mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create( (PhoneStatusBarView) getView()); mStatusBarFragmentComponent.init(); @@ -374,6 +375,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener); } + private String getDumpableName() { + if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) { + return getClass().getSimpleName(); + } else { + return getClass().getSimpleName() + getContext().getDisplayId(); + } + } + @Override public void onCameraLaunchGestureDetected(int source) { mWaitingForWindowStateChangeAfterCameraLaunch = true; @@ -470,7 +479,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue startable.stop(); mStartableStates.put(startable, Startable.State.STOPPED); } - mDumpManager.unregisterDumpable(getClass().getSimpleName()); + mDumpManager.unregisterDumpable(getDumpableName()); if (mNicBindingDisposable != null) { mNicBindingDisposable.dispose(); mNicBindingDisposable = null; @@ -486,7 +495,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue NotificationIconContainer notificationIcons = notificationIconArea.requireViewById(R.id.notificationIcons); mNotificationIconAreaInner = notificationIcons; - mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons); + if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) { + //TODO(b/369337701): implement notification icons for all displays. + // Currently if we try to bind for all displays, there is a crash, because the same + // notification icon view can't have multiple parents. + mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons); + } if (!StatusBarSimpleFragment.isEnabled()) { updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false); @@ -642,7 +656,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } boolean showSecondaryOngoingActivityChip = Flags.statusBarScreenSharingChips() - && StatusBarRonChips.isEnabled() + && StatusBarNotifChips.isEnabled() && mHasSecondaryOngoingActivity; return new StatusBarVisibilityModel( @@ -684,7 +698,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue boolean showSecondaryOngoingActivityChip = // Secondary chips are only supported when RONs are enabled. - StatusBarRonChips.isEnabled() + StatusBarNotifChips.isEnabled() && visibilityModel.getShowSecondaryOngoingActivityChip() && !disableNotifications; if (showSecondaryOngoingActivityChip) { @@ -811,7 +825,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void showSecondaryOngoingActivityChip(boolean animate) { - StatusBarRonChips.assertInNewMode(); + StatusBarNotifChips.assertInNewMode(); StatusBarSimpleFragment.assertInLegacyMode(); animateShow(mSecondaryOngoingActivityChip, animate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt index bad6f80c3735..694a5e529ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel -import com.android.app.tracing.coroutines.createCoroutineTracingContext import androidx.annotation.VisibleForTesting +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlagsClassic @@ -29,8 +29,8 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.cancel @@ -116,7 +116,7 @@ constructor( private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> { // Create a child scope so we can cancel it - val vmScope = scope.createChildScope(createCoroutineTracingContext("MobileIconViewModel")) + val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel")) val vm = MobileIconViewModel( subId, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index 9c168be0693f..3a07d9b6beaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.core.StatusBarSimpleFragment @@ -83,7 +84,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } - if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) { + if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) launch { @@ -119,7 +120,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } - if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) { + if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) val secondaryChipView: View = diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java index 213581787f73..d0817d736f07 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -61,7 +61,6 @@ public class TunerActivity extends Activity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight); getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); requestWindowFeature(Window.FEATURE_NO_TITLE); diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt index de9231856e2c..d87607dec8a1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt +++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt @@ -11,6 +11,7 @@ import android.graphics.drawable.AnimatedStateListDrawable import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable import android.util.Log import androidx.annotation.Px import com.android.app.tracing.traceSection @@ -22,36 +23,35 @@ class DrawableSize { const val TAG = "SysUiDrawableSize" /** - * Downscales passed Drawable to set maximum width and height. This will only - * be done for Drawables that can be downscaled non-destructively - e.g. animated - * and stateful drawables will no be downscaled. + * Downscales passed Drawable to set maximum width and height. This will only be done for + * Drawables that can be downscaled non-destructively - e.g. animated drawables, stateful + * drawables, and drawables with mixed-type layers will not be downscaled. * - * Downscaling will keep the aspect ratio. - * This method will not touch drawables that already fit into size specification. + * Downscaling will keep the aspect ratio. This method will not touch drawables that already + * fit into size specification. * * @param resources Resources on which to base the density of resized drawable. * @param drawable Drawable to downscale. * @param maxWidth Maximum width of the downscaled drawable. * @param maxHeight Maximum height of the downscaled drawable. - * * @return returns downscaled drawable if it's possible to downscale it or original if it's - * not. + * not. */ @JvmStatic fun downscaleToSize( res: Resources, drawable: Drawable, @Px maxWidth: Int, - @Px maxHeight: Int + @Px maxHeight: Int, ): Drawable { traceSection("DrawableSize#downscaleToSize") { // Bitmap drawables can contain big bitmaps as their content while sneaking it past // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap // size for those. - val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width - ?: drawable.intrinsicWidth - val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height - ?: drawable.intrinsicHeight + val originalWidth = + (drawable as? BitmapDrawable)?.bitmap?.width ?: drawable.intrinsicWidth + val originalHeight = + (drawable as? BitmapDrawable)?.bitmap?.height ?: drawable.intrinsicHeight // Don't touch drawable if we can't resolve sizes for whatever reason. if (originalWidth <= 0 || originalHeight <= 0) { @@ -61,14 +61,18 @@ class DrawableSize { // Do not touch drawables that are already within bounds. if (originalWidth < maxWidth && originalHeight < maxHeight) { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " + - "to $maxWidth x $maxHeight") + Log.d( + TAG, + "Not resizing $originalWidth x $originalHeight" + + " " + + "to $maxWidth x $maxHeight", + ) } return drawable } - if (!isSimpleBitmap(drawable)) { + if (isComplicatedBitmap(drawable)) { return drawable } @@ -80,19 +84,25 @@ class DrawableSize { val height = (originalHeight * scale).toInt() if (width <= 0 || height <= 0) { - Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " + - "from $originalWidth x $originalHeight to invalid $width x $height.") + Log.w( + TAG, + "Attempted to resize ${drawable.javaClass.simpleName} " + + "from $originalWidth x $originalHeight to invalid $width x $height.", + ) return drawable } if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " + - "from $originalWidth x $originalHeight to $width x $height") + Log.d( + TAG, + "Resizing large drawable (${drawable.javaClass.simpleName}) " + + "from $originalWidth x $originalHeight to $width x $height", + ) } // We want to keep existing config if it's more efficient than 32-bit RGB. - val config = (drawable as? BitmapDrawable)?.bitmap?.config - ?: Bitmap.Config.ARGB_8888 + val config = + (drawable as? BitmapDrawable)?.bitmap?.config ?: Bitmap.Config.ARGB_8888 val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config) val canvas = Canvas(scaledDrawableBitmap) @@ -105,8 +115,8 @@ class DrawableSize { } } - private fun isSimpleBitmap(drawable: Drawable): Boolean { - return !(drawable.isStateful || isAnimated(drawable)) + private fun isComplicatedBitmap(drawable: Drawable): Boolean { + return drawable.isStateful || isAnimated(drawable) || hasComplicatedLayers(drawable) } private fun isAnimated(drawable: Drawable): Boolean { @@ -119,5 +129,30 @@ class DrawableSize { drawable is AnimatedStateListDrawable || drawable is AnimatedVectorDrawable } + + private fun hasComplicatedLayers(drawable: Drawable): Boolean { + if (drawable !is LayerDrawable) { + return false + } + if (drawable.numberOfLayers == 1) { + return false + } + + val firstLayerType = drawable.getDrawable(0).javaClass + for (i in 1..<drawable.numberOfLayers) { + val layer = drawable.getDrawable(i) + if (layer.javaClass != firstLayerType) { + // If different layers have different drawable types, we shouldn't scale it down + // because we may lose the level information if one of the layers is a bitmap + // and another layer is a level-list. See b/244282477. + return true + } + if (isComplicatedBitmap(layer)) { + return true + } + } + + return false + } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt index 2af84c7e46f0..579af73236f3 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt @@ -16,10 +16,9 @@ package com.android.systemui.util.kotlin -import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dagger.qualifiers.Tracing import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -27,7 +26,6 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi /** Providers for various application-wide coroutines-related constructs. */ @Module @@ -35,16 +33,15 @@ class GlobalCoroutinesModule { @Provides @Singleton @Application - fun applicationScope( - @Main dispatcherContext: CoroutineContext, - ): CoroutineScope = CoroutineScope(dispatcherContext + createCoroutineTracingContext("ApplicationScope")) + fun applicationScope(@Main dispatcherContext: CoroutineContext): CoroutineScope = + CoroutineScope(dispatcherContext + newTracingContext("ApplicationScope")) @Provides @Singleton @Main @Deprecated( "Use @Main CoroutineContext instead", - ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext"), ) fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt index 4d9aaa6dc6b0..ea8709f7d65c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.util.settings -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.annotation.UserIdInt import android.content.ContentResolver import android.database.ContentObserver @@ -24,6 +23,7 @@ import android.provider.Settings.SettingNotFoundException import androidx.annotation.AnyThread import androidx.annotation.WorkerThread import com.android.app.tracing.TraceUtils.trace +import com.android.systemui.coroutines.newTracingContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -94,7 +94,7 @@ interface SettingsProxy { */ @AnyThread fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-A")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-A")).launch { registerContentObserverSync(getUriFor(name), settingsObserver) } @@ -109,9 +109,9 @@ interface SettingsProxy { fun registerContentObserverAsync( name: String, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-B")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-B")).launch { registerContentObserverSync(getUriFor(name), settingsObserver) registered.run() } @@ -144,7 +144,7 @@ interface SettingsProxy { */ @AnyThread fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-C")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-C")).launch { registerContentObserverSync(uri, settingsObserver) } @@ -159,9 +159,9 @@ interface SettingsProxy { fun registerContentObserverAsync( uri: Uri, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-D")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-D")).launch { registerContentObserverSync(uri, settingsObserver) registered.run() } @@ -175,7 +175,7 @@ interface SettingsProxy { fun registerContentObserverSync( name: String, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) /** @@ -204,9 +204,9 @@ interface SettingsProxy { fun registerContentObserverAsync( name: String, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-E")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-E")).launch { registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) } @@ -222,9 +222,9 @@ interface SettingsProxy { name: String, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-F")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-F")).launch { registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) registered.run() } @@ -273,9 +273,9 @@ interface SettingsProxy { fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-G")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-G")).launch { registerContentObserverSync(uri, notifyForDescendants, settingsObserver) } @@ -291,9 +291,9 @@ interface SettingsProxy { uri: Uri, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-H")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-H")).launch { registerContentObserverSync(uri, notifyForDescendants, settingsObserver) registered.run() } @@ -330,7 +330,9 @@ interface SettingsProxy { */ @AnyThread fun unregisterContentObserverAsync(settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-I")).launch { unregisterContentObserver(settingsObserver) } + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-I")).launch { + unregisterContentObserver(settingsObserver) + } /** * Look up a name in the database. diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt index c820c07b61b1..c5deca214e28 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.util.settings -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.annotation.UserIdInt import android.annotation.WorkerThread import android.content.ContentResolver @@ -24,6 +23,7 @@ import android.net.Uri import android.os.UserHandle import android.provider.Settings.SettingNotFoundException import com.android.app.tracing.TraceUtils.trace +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow @@ -79,7 +79,7 @@ interface UserSettingsProxy : SettingsProxy { } override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-A")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-A")).launch { registerContentObserverForUserSync(uri, settingsObserver, userId) } @@ -88,7 +88,7 @@ interface UserSettingsProxy : SettingsProxy { override fun registerContentObserverSync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) { registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId) } @@ -111,9 +111,9 @@ interface UserSettingsProxy : SettingsProxy { override fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-B")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-B")).launch { registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId) } @@ -156,9 +156,9 @@ interface UserSettingsProxy : SettingsProxy { fun registerContentObserverForUserAsync( name: String, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-C")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-C")).launch { registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle) } @@ -197,9 +197,9 @@ interface UserSettingsProxy : SettingsProxy { fun registerContentObserverForUserAsync( uri: Uri, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-D")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-D")).launch { registerContentObserverForUserSync(uri, settingsObserver, userHandle) } @@ -214,9 +214,9 @@ interface UserSettingsProxy : SettingsProxy { uri: Uri, settingsObserver: ContentObserver, userHandle: Int, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-E")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-E")).launch { registerContentObserverForUserSync(uri, settingsObserver, userHandle) registered.run() } @@ -273,14 +273,14 @@ interface UserSettingsProxy : SettingsProxy { name: String, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) { - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-F")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-F")).launch { registerContentObserverForUserSync( getUriFor(name), notifyForDescendants, settingsObserver, - userHandle + userHandle, ) } } @@ -337,14 +337,14 @@ interface UserSettingsProxy : SettingsProxy { uri: Uri, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-G")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-G")).launch { registerContentObserverForUserSync( uri, notifyForDescendants, settingsObserver, - userHandle + userHandle, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt index 2dd0bdab93d1..5e0af634c786 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.volume.dialog.settings.domain import android.app.ActivityManager -import com.android.app.tracing.coroutines.flow.map +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.volume.Events import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog @@ -30,6 +30,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @VolumeDialogScope @@ -49,6 +50,7 @@ constructor( deviceProvisionedController.isCurrentUserSetup() && model.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE } + .flowName("VDSBI#isVisible") .stateIn(coroutineScope, SharingStarted.Eagerly, false) fun onButtonClicked() { diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml new file mode 100644 index 000000000000..d9ea0b91abdf --- /dev/null +++ b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml @@ -0,0 +1,19 @@ +<?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. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <item android:drawable="@drawable/ic_brightness"/> + <item android:drawable="@drawable/ic_brightness"/> +</layer-list> diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml new file mode 100644 index 000000000000..796de8f15f47 --- /dev/null +++ b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml @@ -0,0 +1,20 @@ +<?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. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <!-- dessert_flan is a PNG while ic_brightness is a level-list. --> + <item android:drawable="@drawable/dessert_flan"/> + <item android:drawable="@drawable/ic_brightness"/> +</layer-list> diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java deleted file mode 100644 index c51aa04fc7b6..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; -import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; -import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.AnimatedStateListDrawable; -import android.util.Pair; -import android.view.View; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; -import android.widget.ImageView; - -import com.android.systemui.Flags; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.biometrics.AuthRippleController; -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; -import com.android.systemui.doze.util.BurnInHelperKt; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; -import com.android.systemui.kosmos.KosmosJavaAdapter; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.res.R; -import com.android.systemui.scene.shared.flag.SceneContainerFlag; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; - -import org.junit.After; -import org.junit.Before; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -public class LegacyLockIconViewControllerBaseTest extends SysuiTestCase { - protected static final String UNLOCKED_LABEL = "unlocked"; - protected static final String LOCKED_LABEL = "locked"; - protected static final int PADDING = 10; - - protected MockitoSession mStaticMockSession; - - protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); - protected @Mock DeviceEntryInteractor mDeviceEntryInteractor; - protected @Mock LockIconView mLockIconView; - protected @Mock ImageView mLockIcon; - protected @Mock AnimatedStateListDrawable mIconDrawable; - protected @Mock Context mContext; - protected @Mock Resources mResources; - protected @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager; - protected @Mock StatusBarStateController mStatusBarStateController; - protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - protected @Mock KeyguardViewController mKeyguardViewController; - protected @Mock KeyguardStateController mKeyguardStateController; - protected @Mock FalsingManager mFalsingManager; - protected @Mock AuthController mAuthController; - protected @Mock DumpManager mDumpManager; - protected @Mock AccessibilityManager mAccessibilityManager; - protected @Mock ConfigurationController mConfigurationController; - protected @Mock VibratorHelper mVibrator; - protected @Mock AuthRippleController mAuthRippleController; - protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); - protected FakeFeatureFlags mFeatureFlags; - - protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; - - protected LegacyLockIconViewController mUnderTest; - - // Capture listeners so that they can be used to send events - @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = - ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); - - @Captor protected ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor = - ArgumentCaptor.forClass(KeyguardStateController.Callback.class); - protected KeyguardStateController.Callback mKeyguardStateCallback; - - @Captor protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor = - ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); - protected StatusBarStateController.StateListener mStatusBarStateListener; - - @Captor protected ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor; - protected AuthController.Callback mAuthControllerCallback; - - @Captor protected ArgumentCaptor<KeyguardUpdateMonitorCallback> - mKeyguardUpdateMonitorCallbackCaptor = - ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); - protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; - - @Captor protected ArgumentCaptor<Point> mPointCaptor; - - @Before - public void setUp() throws Exception { - mStaticMockSession = mockitoSession() - .mockStatic(BurnInHelperKt.class) - .strictness(Strictness.LENIENT) - .startMocking(); - MockitoAnnotations.initMocks(this); - - setupLockIconViewMocks(); - when(mContext.getResources()).thenReturn(mResources); - when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager); - Rect windowBounds = new Rect(0, 0, 800, 1200); - when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds); - when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL); - when(mResources.getString(R.string.accessibility_lock_icon)).thenReturn(LOCKED_LABEL); - when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable); - when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING); - when(mAuthController.getScaleFactor()).thenReturn(1f); - - when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); - - if (!SceneContainerFlag.isEnabled()) { - mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR); - //TODO move this to use @DisableFlags annotation if needed - mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT); - } - - mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); - mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false); - - mUnderTest = new LegacyLockIconViewController( - mStatusBarStateController, - mKeyguardUpdateMonitor, - mKeyguardViewController, - mKeyguardStateController, - mFalsingManager, - mAuthController, - mDumpManager, - mAccessibilityManager, - mConfigurationController, - mDelayableExecutor, - mVibrator, - mAuthRippleController, - mResources, - mKosmos.getKeyguardTransitionInteractor(), - KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(), - mFeatureFlags, - mPrimaryBouncerInteractor, - mContext, - () -> mDeviceEntryInteractor - ); - } - - @After - public void tearDown() { - mStaticMockSession.finishMocking(); - } - - protected Pair<Float, Point> setupUdfps() { - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); - final Point udfpsLocation = new Point(50, 75); - final float radius = 33f; - when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation); - when(mAuthController.getUdfpsRadius()).thenReturn(radius); - - return new Pair(radius, udfpsLocation); - } - - protected void setupShowLockIcon() { - when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarStateController.getDozeAmount()).thenReturn(0f); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false); - } - - protected void captureAuthControllerCallback() { - verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture()); - mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue(); - } - - protected void captureKeyguardStateCallback() { - verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture()); - mKeyguardStateCallback = mKeyguardStateCaptor.getValue(); - } - - protected void captureStatusBarStateListener() { - verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture()); - mStatusBarStateListener = mStatusBarStateCaptor.getValue(); - } - - protected void captureKeyguardUpdateMonitorCallback() { - verify(mKeyguardUpdateMonitor).registerCallback( - mKeyguardUpdateMonitorCallbackCaptor.capture()); - mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue(); - } - - protected void setupLockIconViewMocks() { - when(mLockIconView.getResources()).thenReturn(mResources); - when(mLockIconView.getContext()).thenReturn(mContext); - when(mLockIconView.getLockIcon()).thenReturn(mLockIcon); - } - - protected void resetLockIconView() { - reset(mLockIconView); - setupLockIconViewMocks(); - } - - protected void init(boolean useDozeMigrationFlag) { - mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag); - mUnderTest.setLockIconView(mLockIconView); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java deleted file mode 100644 index c1ba39e89cf9..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - -import static com.android.keyguard.LockIconView.ICON_LOCK; -import static com.android.keyguard.LockIconView.ICON_UNLOCK; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.graphics.Point; -import android.hardware.biometrics.BiometricSourceType; -import android.testing.TestableLooper; -import android.util.Pair; -import android.view.HapticFeedbackConstants; -import android.view.View; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.systemui.biometrics.UdfpsController; -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.doze.util.BurnInHelperKt; -import com.android.systemui.flags.EnableSceneContainer; -import com.android.systemui.statusbar.StatusBarState; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -@TestableLooper.RunWithLooper -public class LegacyLockIconViewControllerTest extends LegacyLockIconViewControllerBaseTest { - - @Override - public void setUp() throws Exception { - super.setUp(); - when(mLockIconView.isAttachedToWindow()).thenReturn(true); - } - - @Test - public void testUpdateFingerprintLocationOnInit() { - // GIVEN fp sensor location is available pre-attached - Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location - - // WHEN lock icon view controller is initialized and attached - init(/* useMigrationFlag= */false); - - // THEN lock icon view location is updated to the udfps location with UDFPS radius - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testUpdatePaddingBasedOnResolutionScale() { - // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5 - Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location - when(mAuthController.getScaleFactor()).thenReturn(5f); - - // WHEN lock icon view controller is initialized and attached - init(/* useMigrationFlag= */false); - - // THEN lock icon view location is updated with the scaled radius - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING * 5)); - } - - @Test - public void testUpdateLockIconLocationOnAuthenticatorsRegistered() { - // GIVEN fp sensor location is not available pre-init - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - init(/* useMigrationFlag= */false); - resetLockIconView(); // reset any method call counts for when we verify method calls later - - // GIVEN fp sensor location is available post-attached - captureAuthControllerCallback(); - Pair<Float, Point> udfps = setupUdfps(); - - // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); - mDelayableExecutor.runAllReady(); - - // THEN lock icon view location is updated with the same coordinates as auth controller vals - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testUpdateLockIconLocationOnUdfpsLocationChanged() { - // GIVEN fp sensor location is not available pre-init - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - init(/* useMigrationFlag= */false); - resetLockIconView(); // reset any method call counts for when we verify method calls later - - // GIVEN fp sensor location is available post-attached - captureAuthControllerCallback(); - Pair<Float, Point> udfps = setupUdfps(); - - // WHEN udfps location changes - mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams()); - mDelayableExecutor.runAllReady(); - - // THEN lock icon view location is updated with the same coordinates as auth controller vals - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() { - // GIVEN Udpfs sensor location is available - setupUdfps(); - - // WHEN the view is attached - init(/* useMigrationFlag= */false); - - // THEN the lock icon view background should be enabled - verify(mLockIconView).setUseBackground(true); - } - - @Test - public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() { - // GIVEN Udfps sensor location is not supported - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - - // WHEN the view is attached - init(/* useMigrationFlag= */false); - - // THEN the lock icon view background should be disabled - verify(mLockIconView).setUseBackground(false); - } - - @Test - public void testLockIconStartState() { - // GIVEN lock icon state - setupShowLockIcon(); - - // WHEN lock icon controller is initialized - init(/* useMigrationFlag= */false); - - // THEN the lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, false); - } - - @Test - public void testLockIcon_updateToUnlock() { - // GIVEN starting state for the lock icon - setupShowLockIcon(); - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - reset(mLockIconView); - - // WHEN the unlocked state changes to canDismissLockScreen=true - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the unlock should show - verify(mLockIconView).updateIcon(ICON_UNLOCK, false); - } - - @Test - public void testLockIcon_clearsIconWhenUnlocked() { - // GIVEN udfps not enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false); - - // GIVEN starting state for the lock icon - setupShowLockIcon(); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN the dozing state changes - mStatusBarStateListener.onDozingChanged(false /* isDozing */); - - // THEN the icon is cleared - verify(mLockIconView).clearIcon(); - } - - @Test - public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() { - // GIVEN udfps enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); - - // GIVEN starting state for the lock icon - setupShowLockIcon(); - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN the dozing state changes - mStatusBarStateListener.onDozingChanged(true /* isDozing */); - - // THEN the AOD lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, true); - } - - @Test - public void testBurnInOffsetsUpdated_onDozeAmountChanged() { - // GIVEN udfps enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); - - // GIVEN burn-in offset = 5 - int burnInOffset = 5; - when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset); - - // GIVEN starting state for the lock icon (keyguard) - setupShowLockIcon(); - init(/* useMigrationFlag= */false); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN dozing updates - mStatusBarStateListener.onDozingChanged(true /* isDozing */); - mStatusBarStateListener.onDozeAmountChanged(1f, 1f); - - // THEN the view's translation is updated to use the AoD burn-in offsets - verify(mLockIconView).setTranslationY(burnInOffset); - verify(mLockIconView).setTranslationX(burnInOffset); - reset(mLockIconView); - - // WHEN the device is no longer dozing - mStatusBarStateListener.onDozingChanged(false /* isDozing */); - mStatusBarStateListener.onDozeAmountChanged(0f, 0f); - - // THEN the view is updated to NO translation (no burn-in offsets anymore) - verify(mLockIconView).setTranslationY(0); - verify(mLockIconView).setTranslationX(0); - } - - @Test - public void lockIconShows_afterUnlockStateChanges() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setContentDescription(LOCKED_LABEL); - } - - @Test - public void lockIconAccessibility_notVisibleToUser() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - when(mLockIconView.isVisibleToUser()).thenReturn(false); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } - - @Test - public void lockIconAccessibility_bouncerAnimatingAway() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - when(mLockIconView.isVisibleToUser()).thenReturn(true); - when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } - - @Test - public void lockIconAccessibility_bouncerNotAnimatingAway_viewVisible() { - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */false); - captureKeyguardStateCallback(); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - // and biometric running state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - reset(mLockIconView); - when(mLockIconView.isVisibleToUser()).thenReturn(true); - when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(false); - - // WHEN the unlocked state changes - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the lock icon is shown - verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - - @Test - public void playHaptic_onTouchExploration_performHapticFeedback() { - // WHEN request to vibrate on touch exploration - mUnderTest.vibrateOnTouchExploration(); - - // THEN performHapticFeedback is used - verify(mVibrator).performHapticFeedback(any(), eq(HapticFeedbackConstants.CONTEXT_CLICK)); - } - - @Test - public void playHaptic_onLongPress_performHapticFeedback() { - // WHEN request to vibrate on long press - mUnderTest.vibrateOnLongPress(); - - // THEN uses perform haptic feedback - verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); - } - - @Test - public void longPress_showBouncer_sceneContainerNotEnabled() { - init(/* useMigrationFlag= */ false); - when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); - - // WHEN longPress - mUnderTest.onLongPress(); - - // THEN show primary bouncer via keyguard view controller, not scene container - verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean()); - verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); - } - - @Test - @EnableSceneContainer - public void longPress_showBouncer() { - init(/* useMigrationFlag= */ false); - when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false); - - // WHEN longPress - mUnderTest.onLongPress(); - - // THEN show primary bouncer - verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); - verify(mDeviceEntryInteractor).attemptDeviceEntry(); - } - - @Test - @EnableSceneContainer - public void longPress_falsingTriggered_doesNotShowBouncer() { - init(/* useMigrationFlag= */ false); - when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true); - - // WHEN longPress - mUnderTest.onLongPress(); - - // THEN don't show primary bouncer - verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); - verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt deleted file mode 100644 index 2fd3cb0f0592..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard - -import android.view.View -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.keyguard.LockIconView.ICON_LOCK -import com.android.systemui.doze.util.getBurnInOffset -import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.util.mockito.whenever -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.anyBoolean -import org.mockito.Mockito.anyInt -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify - -@RunWith(AndroidJUnit4::class) -@SmallTest -class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControllerBaseTest() { - - /** After migration, replaces LockIconViewControllerTest version */ - @Test - fun testLockIcon_clearsIconWhenUnlocked() = - runBlocking(IMMEDIATE) { - // GIVEN udfps not enrolled - setupUdfps() - whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false) - - // GIVEN starting state for the lock icon - setupShowLockIcon() - whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE) - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN the dozing state changes - mUnderTest.mIsDozingCallback.accept(false) - // THEN the icon is cleared - verify(mLockIconView).clearIcon() - } - - /** After migration, replaces LockIconViewControllerTest version */ - @Test - fun testLockIcon_updateToAodLock_whenUdfpsEnrolled() = - runBlocking(IMMEDIATE) { - // GIVEN udfps enrolled - setupUdfps() - whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true) - - // GIVEN starting state for the lock icon - setupShowLockIcon() - - // GIVEN lock icon controller is initialized and view is attached - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN the dozing state changes - mUnderTest.mIsDozingCallback.accept(true) - - // THEN the AOD lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, true) - } - - /** After migration, replaces LockIconViewControllerTest version */ - @Test - fun testBurnInOffsetsUpdated_onDozeAmountChanged() = - runBlocking(IMMEDIATE) { - // GIVEN udfps enrolled - setupUdfps() - whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true) - - // GIVEN burn-in offset = 5 - val burnInOffset = 5 - whenever(getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset) - - // GIVEN starting state for the lock icon (keyguard) - setupShowLockIcon() - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN dozing updates - mUnderTest.mIsDozingCallback.accept(true) - mUnderTest.mDozeTransitionCallback.accept(1f) - - // THEN the view's translation is updated to use the AoD burn-in offsets - verify(mLockIconView).setTranslationY(burnInOffset.toFloat()) - verify(mLockIconView).setTranslationX(burnInOffset.toFloat()) - reset(mLockIconView) - - // WHEN the device is no longer dozing - mUnderTest.mIsDozingCallback.accept(false) - mUnderTest.mDozeTransitionCallback.accept(0f) - - // THEN the view is updated to NO translation (no burn-in offsets anymore) - verify(mLockIconView).setTranslationY(0f) - verify(mLockIconView).setTranslationX(0f) - } - - @Test - fun testHideLockIconView_onLockscreenHostedDreamStateChanged() = - runBlocking(IMMEDIATE) { - // GIVEN starting state for the lock icon (keyguard) and wallpaper dream enabled - mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true) - setupShowLockIcon() - init(/* useMigrationFlag= */ true) - reset(mLockIconView) - - // WHEN dream starts - mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept( - true /* isActiveDreamLockscreenHosted */ - ) - - // THEN the lock icon is hidden - verify(mLockIconView).visibility = View.INVISIBLE - reset(mLockIconView) - - // WHEN the device is no longer dreaming - mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept( - false /* isActiveDreamLockscreenHosted */ - ) - - // THEN lock icon is visible - verify(mLockIconView).visibility = View.VISIBLE - } - - companion object { - private val IMMEDIATE = Dispatchers.Main.immediate - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt deleted file mode 100644 index 6dc4b10a57da..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.graphics.Point -import android.hardware.biometrics.BiometricSourceType -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal -import android.testing.TestableLooper.RunWithLooper -import android.util.DisplayMetrics -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.keyguard.logging.KeyguardLogger -import com.android.systemui.Flags -import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository -import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor -import com.android.systemui.keyguard.WakefulnessLifecycle -import com.android.systemui.log.logcatLogBuffer -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.LightRevealScrim -import com.android.systemui.statusbar.NotificationShadeWindowController -import com.android.systemui.statusbar.commandline.CommandRegistry -import com.android.systemui.statusbar.phone.BiometricUnlockController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.leak.RotationUtils -import com.android.systemui.util.mockito.any -import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.junit.After -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers -import org.mockito.ArgumentMatchers.eq -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations -import org.mockito.MockitoSession -import org.mockito.quality.Strictness -import javax.inject.Provider - - -@ExperimentalCoroutinesApi -@SmallTest -@RunWith(AndroidJUnit4::class) -class AuthRippleControllerTest : SysuiTestCase() { - private lateinit var staticMockSession: MockitoSession - - private lateinit var controller: AuthRippleController - @Mock private lateinit var rippleView: AuthRippleView - @Mock private lateinit var commandRegistry: CommandRegistry - @Mock private lateinit var configurationController: ConfigurationController - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock private lateinit var authController: AuthController - @Mock private lateinit var authRippleInteractor: AuthRippleInteractor - @Mock private lateinit var keyguardStateController: KeyguardStateController - @Mock - private lateinit var wakefulnessLifecycle: WakefulnessLifecycle - @Mock - private lateinit var notificationShadeWindowController: NotificationShadeWindowController - @Mock - private lateinit var biometricUnlockController: BiometricUnlockController - @Mock - private lateinit var udfpsControllerProvider: Provider<UdfpsController> - @Mock - private lateinit var udfpsController: UdfpsController - @Mock - private lateinit var statusBarStateController: StatusBarStateController - @Mock - private lateinit var lightRevealScrim: LightRevealScrim - @Mock - private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal - - private val facePropertyRepository = FakeFacePropertyRepository() - private val displayMetrics = DisplayMetrics() - - @Captor - private lateinit var biometricUnlockListener: - ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener> - - @Before - fun setUp() { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - MockitoAnnotations.initMocks(this) - staticMockSession = mockitoSession() - .mockStatic(RotationUtils::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - - `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE) - `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp)) - `when`(udfpsControllerProvider.get()).thenReturn(udfpsController) - - controller = AuthRippleController( - context, - authController, - configurationController, - keyguardUpdateMonitor, - keyguardStateController, - wakefulnessLifecycle, - commandRegistry, - notificationShadeWindowController, - udfpsControllerProvider, - statusBarStateController, - displayMetrics, - KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)), - biometricUnlockController, - lightRevealScrim, - authRippleInteractor, - facePropertyRepository, - rippleView, - ) - controller.init() - } - - @After - fun tearDown() { - staticMockSession.finishMocking() - } - - @Test - fun testFingerprintTrigger_KeyguardShowing_Ripple() { - // GIVEN fp exists, keyguard is showing, unlocking with fp allowed - val fpsLocation = Point(5, 5) - `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) - - // WHEN fingerprint authenticated - verify(biometricUnlockController).addListener(biometricUnlockListener.capture()) - biometricUnlockListener.value - .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT) - - // THEN update sensor location and show ripple - verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f) - verify(rippleView).startUnlockedRipple(any()) - } - - @Test - fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() { - // GIVEN fp exists & unlocking with fp allowed - val fpsLocation = Point(5, 5) - `when`(authController.udfpsLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(true) - - // WHEN keyguard is NOT showing & fingerprint authenticated - `when`(keyguardStateController.isShowing).thenReturn(false) - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FINGERPRINT /* type */, - false /* isStrongBiometric */) - - // THEN no ripple - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() { - // GIVEN fp exists & keyguard is showing - val fpsLocation = Point(5, 5) - `when`(authController.udfpsLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - - // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FINGERPRINT))).thenReturn(false) - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FINGERPRINT /* type */, - false /* isStrongBiometric */) - - // THEN no ripple - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun testNullFaceSensorLocationDoesNothing() { - facePropertyRepository.setSensorLocation(null) - controller.onViewAttached() - - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FACE /* type */, - false /* isStrongBiometric */) - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun testNullFingerprintSensorLocationDoesNothing() { - `when`(authController.fingerprintSensorLocation).thenReturn(null) - controller.onViewAttached() - - val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) - verify(keyguardUpdateMonitor).registerCallback(captor.capture()) - - captor.value.onBiometricAuthenticated( - 0 /* userId */, - BiometricSourceType.FINGERPRINT /* type */, - false /* isStrongBiometric */) - verify(rippleView, never()).startUnlockedRipple(any()) - } - - @Test - fun registersAndDeregisters() { - controller.onViewAttached() - val captor = ArgumentCaptor - .forClass(KeyguardStateController.Callback::class.java) - verify(keyguardStateController).addCallback(captor.capture()) - val captor2 = ArgumentCaptor - .forClass(WakefulnessLifecycle.Observer::class.java) - verify(wakefulnessLifecycle).addObserver(captor2.capture()) - controller.onViewDetached() - verify(keyguardStateController).removeCallback(any()) - verify(wakefulnessLifecycle).removeObserver(any()) - } - - @Test - @RunWithLooper(setAsMainLooper = true) - fun testAnimatorRunWhenWakeAndUnlock_fingerprint() { - mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION) - val fpsLocation = Point(5, 5) - `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - BiometricSourceType.FINGERPRINT)).thenReturn(true) - `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - - controller.showUnlockRipple(BiometricSourceType.FINGERPRINT) - assertTrue("reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway) - `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true) - controller.onKeyguardFadingAwayChanged() - assertFalse("reveal triggers multiple times", - controller.startLightRevealScrimOnKeyguardFadingAway) - } - - @Test - @RunWithLooper(setAsMainLooper = true) - fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() { - mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION) - val faceLocation = Point(5, 5) - facePropertyRepository.setSensorLocation(faceLocation) - controller.onViewAttached() - `when`(keyguardStateController.isShowing).thenReturn(true) - `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - `when`(authController.isUdfpsFingerDown).thenReturn(true) - `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed( - eq(BiometricSourceType.FACE))).thenReturn(true) - - controller.showUnlockRipple(BiometricSourceType.FACE) - assertTrue("reveal didn't start on keyguardFadingAway", - controller.startLightRevealScrimOnKeyguardFadingAway) - `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true) - controller.onKeyguardFadingAwayChanged() - assertFalse("reveal triggers multiple times", - controller.startLightRevealScrimOnKeyguardFadingAway) - } - - @Test - fun testUpdateRippleColor() { - controller.onViewAttached() - val captor = ArgumentCaptor - .forClass(ConfigurationController.ConfigurationListener::class.java) - verify(configurationController).addCallback(captor.capture()) - - reset(rippleView) - captor.value.onThemeChanged() - verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt()) - - reset(rippleView) - captor.value.onUiModeChanged() - verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt()) - } - - @Test - fun testUdfps_onFingerDown_runningForDeviceEntry_showDwellRipple() { - // GIVEN fingerprint detection is running on keyguard - `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true) - - // GIVEN view is already attached - controller.onViewAttached() - val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java) - verify(udfpsController).addCallback(captor.capture()) - - // GIVEN fp is updated to Point(5, 5) - val fpsLocation = Point(5, 5) - `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) - - // WHEN finger is down - captor.value.onFingerDown() - - // THEN update sensor location and show ripple - verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f) - verify(rippleView).startDwellRipple(false) - } - - @Test - fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() { - // GIVEN fingerprint detection is NOT running on keyguard - `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false) - - // GIVEN view is already attached - controller.onViewAttached() - val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java) - verify(udfpsController).addCallback(captor.capture()) - - // WHEN finger is down - captor.value.onFingerDown() - - // THEN doesn't show dwell ripple - verify(rippleView, never()).startDwellRipple(false) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index e2a6a5508992..4baca713e19f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -16,21 +16,14 @@ package com.android.systemui.biometrics -import android.graphics.Rect import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS -import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricRequestConstants.RequestReason import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater import android.view.MotionEvent -import android.view.Surface -import android.view.Surface.Rotation import android.view.View -import android.view.ViewGroup import android.view.WindowManager import android.view.accessibility.AccessibilityManager import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -38,7 +31,6 @@ import androidx.test.filters.SmallTest import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor @@ -61,9 +53,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState -import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController @@ -86,7 +76,6 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @@ -118,7 +107,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var dialogManager: SystemUIDialogManager @Mock private lateinit var dumpManager: DumpManager - @Mock private lateinit var transitionController: LockscreenShadeTransitionController @Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock @@ -126,8 +114,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback @Mock private lateinit var udfpsController: UdfpsController - @Mock private lateinit var udfpsView: UdfpsView - @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @@ -147,7 +133,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { private lateinit var powerInteractor: PowerInteractor private lateinit var testScope: TestScope - private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true } + private val onTouch = { _: View, _: MotionEvent -> true } private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() private lateinit var controllerOverlay: UdfpsControllerOverlay @@ -158,53 +144,37 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { powerInteractor = kosmos.powerInteractor keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor - whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView) - whenever(inflater.inflate(R.layout.udfps_bp_view, null)) - .thenReturn(mock(UdfpsBpView::class.java)) - whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) - .thenReturn(mUdfpsKeyguardViewLegacy) - whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null)) - .thenReturn(mock(UdfpsFpmEmptyView::class.java)) } private suspend fun withReasonSuspend( @RequestReason reason: Int, isDebuggable: Boolean = false, - enableDeviceEntryUdfpsRefactor: Boolean = false, block: suspend () -> Unit, ) { - withReason( - reason, - isDebuggable, - enableDeviceEntryUdfpsRefactor, - ) + withReason(reason, isDebuggable) block() } private fun withReason( @RequestReason reason: Int, isDebuggable: Boolean = false, - enableDeviceEntryUdfpsRefactor: Boolean = false, block: () -> Unit = {}, ) { - if (enableDeviceEntryUdfpsRefactor) { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } else { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } controllerOverlay = UdfpsControllerOverlay( context, inflater, - ViewCaptureAwareWindowManager(windowManager, lazyViewCapture, - isViewCaptureEnabled = false), + ViewCaptureAwareWindowManager( + windowManager, + lazyViewCapture, + isViewCaptureEnabled = false, + ), accessibilityManager, statusBarStateController, statusBarKeyguardViewManager, keyguardUpdateMonitor, dialogManager, dumpManager, - transitionController, configurationController, keyguardStateController, unlockedScreenOffAnimationController, @@ -230,117 +200,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { block() } - @Test fun showUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { showUdfpsOverlay() } - - @Test - fun showUdfpsOverlay_keyguard() = - withReason(REASON_AUTH_KEYGUARD) { - showUdfpsOverlay() - verify(mUdfpsKeyguardViewLegacy).updateSensorLocation(eq(overlayParams.sensorBounds)) - } - - @Test fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() } - - private fun withRotation(@Rotation rotation: Int, block: () -> Unit) { - // Sensor that's in the top left corner of the display in natural orientation. - val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT) - val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT) - overlayParams = - UdfpsOverlayParams( - sensorBounds, - overlayBounds, - DISPLAY_WIDTH, - DISPLAY_HEIGHT, - scaleFactor = 1f, - rotation - ) - block() - } - - @Test - fun showUdfpsOverlay_withRotation0() = - withRotation(Surface.ROTATION_0) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // ROTATION_0 is the native orientation. Sensor should stay in the top left corner. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) - assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) - } - } - - @Test - fun showUdfpsOverlay_withRotation180() = - withRotation(Surface.ROTATION_180) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // ROTATION_180 is not supported. Sensor should stay in the top left corner. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) - assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) - } - } - - @Test - fun showUdfpsOverlay_withRotation90() = - withRotation(Surface.ROTATION_90) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Sensor should be in the bottom left corner in ROTATION_90. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) - assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) - } - } - - @Test - fun showUdfpsOverlay_withRotation270() = - withRotation(Surface.ROTATION_270) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Sensor should be in the top right corner in ROTATION_270. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) - assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) - } - } - - @Test - fun showUdfpsOverlay_awake() = - testScope.runTest { - withReason(REASON_AUTH_KEYGUARD) { - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - verify(windowManager).addView(any(), any()) - } - } - @Test fun showUdfpsOverlay_whileGoingToSleep() = testScope.runTest { @@ -412,91 +271,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun showUdfpsOverlay_afterFinishedTransitioningToAOD() = - testScope.runTest { - withReasonSuspend(REASON_AUTH_KEYGUARD) { - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.OFF, - to = KeyguardState.GONE, - testScope = this, - ) - powerRepository.updateWakefulness( - rawState = WakefulnessState.STARTING_TO_SLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - - // WHEN a request comes to show the view - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - - // THEN the view does not get added immediately - verify(windowManager, never()).addView(any(), any()) - - // WHEN the device finishes transitioning to AOD - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - testScope = this, - ) - runCurrent() - - // THEN the view gets added - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - } - } - - private fun showUdfpsOverlay() { - val didShow = controllerOverlay.show(udfpsController, overlayParams) - - verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any()) - verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode)) - verify(udfpsView).animationViewController = any() - verify(udfpsView).addView(any()) - - assertThat(didShow).isTrue() - assertThat(controllerOverlay.isShowing).isTrue() - assertThat(controllerOverlay.isHiding).isFalse() - assertThat(controllerOverlay.getTouchOverlay()).isNotNull() - } - - @Test fun hideUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() } - - private fun hideUdfpsOverlay() { - val didShow = controllerOverlay.show(udfpsController, overlayParams) - val view = controllerOverlay.getTouchOverlay() - view?.let { whenever(view.parent).thenReturn(mock(ViewGroup::class.java)) } - val didHide = controllerOverlay.hide() - - verify(windowManager).removeView(eq(view)) - - assertThat(didShow).isTrue() - assertThat(didHide).isTrue() - assertThat(controllerOverlay.getTouchOverlay()).isNull() - assertThat(controllerOverlay.animationViewController).isNull() - assertThat(controllerOverlay.isShowing).isFalse() - assertThat(controllerOverlay.isHiding).isTrue() - } - - @Test fun canNotHide() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.hide()).isFalse() } @Test - fun canNotReshow() = - withReason(REASON_AUTH_BP) { - assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue() - assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse() - } - - @Test fun cancels() = withReason(REASON_AUTH_BP) { controllerOverlay.cancel() @@ -504,16 +281,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun unconfigureDisplayOnHide() = - withReason(REASON_AUTH_BP) { - whenever(udfpsView.isDisplayConfigured).thenReturn(true) - - controllerOverlay.show(udfpsController, overlayParams) - controllerOverlay.hide() - verify(udfpsView).unconfigureDisplay() - } - - @Test fun matchesRequestIds() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue() @@ -521,29 +288,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun smallOverlayOnEnrollmentWithA11y() = - withRotation(Surface.ROTATION_0) { - withReason(REASON_ENROLL_ENROLLING) { - // When a11y enabled during enrollment - whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true) - - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Layout params should use sensor bounds - val lp = layoutParamsCaptor.value - assertThat(lp.width).isEqualTo(overlayParams.sensorBounds.width()) - assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height()) - } - } - - @Test fun addViewPending_layoutIsNotUpdated() = testScope.runTest { withReasonSuspend(REASON_AUTH_KEYGUARD) { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - // GIVEN going to sleep keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.OFF, @@ -574,26 +321,4 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { controllerOverlay.hide() } } - - @Test - fun updateOverlayParams_viewLayoutUpdated() = - testScope.runTest { - withReasonSuspend(REASON_AUTH_KEYGUARD) { - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - verify(windowManager).addView(any(), any()) - - // WHEN updateOverlayParams gets called - controllerOverlay.updateOverlayParams(overlayParams) - - // THEN the view layout is updated - verify(windowManager).updateViewLayout(any(), any()) - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt deleted file mode 100644 index 9fbe09619ff1..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.hardware.biometrics.SensorLocationInternal -import android.testing.TestableLooper -import android.testing.ViewUtils -import android.view.LayoutInflater -import android.view.Surface -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.res.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.withArgCaptor -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.never -import org.mockito.Mockito.nullable -import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit - -private const val SENSOR_X = 50 -private const val SENSOR_Y = 250 -private const val SENSOR_RADIUS = 10 - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper -class UdfpsViewTest : SysuiTestCase() { - - @JvmField @Rule - var rule = MockitoJUnit.rule() - - @Mock - lateinit var hbmProvider: UdfpsDisplayModeProvider - @Mock - lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView> - - private lateinit var view: UdfpsView - - @Before - fun setup() { - context.setTheme(androidx.appcompat.R.style.Theme_AppCompat) - view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView - view.animationViewController = animationViewController - val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect - view.overlayParams = UdfpsOverlayParams(sensorBounds, sensorBounds, 1920, - 1080, 1f, Surface.ROTATION_0) - view.setUdfpsDisplayModeProvider(hbmProvider) - ViewUtils.attachView(view) - } - - @After - fun cleanup() { - ViewUtils.detachView(view) - } - - // TODO: Add test to verify view is size of screen - - @Test - fun startAndStopIllumination() { - val onDone: Runnable = mock() - view.configureDisplay(onDone) - - val illuminator = withArgCaptor<Runnable> { - verify(hbmProvider).enable(capture()) - } - - assertThat(view.isDisplayConfigured).isTrue() - verify(animationViewController).onDisplayConfiguring() - verify(animationViewController, never()).onDisplayUnconfigured() - verify(onDone, never()).run() - - // fake illumination event - illuminator.run() - waitForLooper() - verify(onDone).run() - verify(hbmProvider, never()).disable(any()) - - view.unconfigureDisplay() - assertThat(view.isDisplayConfigured).isFalse() - verify(animationViewController).onDisplayUnconfigured() - verify(hbmProvider).disable(nullable(Runnable::class.java)) - } - - private fun waitForLooper() = TestableLooper.get(this).processAllMessages() -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index b87647ef9839..eb72f29046aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -48,11 +48,11 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; -import android.testing.AndroidTestingRunner; -import android.testing.UiThreadTest; +import androidx.test.annotation.UiThreadTest; import android.view.Display; import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -69,7 +69,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @UiThreadTest public class DozeMachineTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt index ae2a9add4fb7..6fce108f35a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt @@ -20,10 +20,10 @@ import android.animation.AnimatorTestRule import android.content.Context import android.service.quicksettings.Tile import android.testing.AndroidTestingRunner -import android.testing.UiThreadTest import android.view.ContextThemeWrapper import android.view.View import android.widget.ImageView +import androidx.test.annotation.UiThreadTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.qs.QSTile diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java index 643debfbc810..7b24233d8603 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java @@ -17,13 +17,13 @@ import android.os.Handler; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.testing.TestableLooper; -import android.testing.UiThreadTest; import android.view.View; import android.view.Window; import android.widget.LinearLayout; import android.widget.Switch; import android.widget.TextView; +import androidx.test.annotation.UiThreadTest; import androidx.recyclerview.widget.RecyclerView; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java index 2d8e69280d30..c8ef663a4f81 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java @@ -31,12 +31,12 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.testing.AndroidTestingRunner; -import android.testing.UiThreadTest; import android.util.FloatProperty; import android.util.Property; import android.view.View; import android.view.animation.Interpolator; +import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.app.animation.Interpolators; @@ -270,4 +270,4 @@ public class PropertyAnimatorTest extends SysuiTestCase { PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties); assertTrue(PropertyAnimator.isAnimating(mView, mProperty)); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index ed7383cacd29..e3e2491e5d52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -612,6 +612,8 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { Assert.assertNull(child.getParent()); Assert.assertNull(child.getNotificationParent()); Assert.assertFalse(child.keepInParentForDismissAnimation()); + verify(mNotificationTestHelper.getMockLogger()) + .logCancelAppearDrawing(child.getEntry(), false); verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger()); } @@ -1013,7 +1015,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { assertThat(row.isHeadsUpAnimatingAway()).isTrue(); // on disappear animation ends - row.onAppearAnimationFinished(/* wasAppearing = */ false); + row.onAppearAnimationFinished(/* wasAppearing = */ false, /* cancelled = */ false); assertThat(row.isHeadsUpAnimatingAway()).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 44d81a7abfe5..8f64287ebd0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -512,10 +512,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mLightBarController, mAutoHideController, new StatusBarInitializerImpl( - mContext.getDisplayId(), - mStatusBarWindowControllerStore, + mStatusBarWindowController, mCollapsedStatusBarFragmentProvider, - emptySet()), + emptySet() + ), mStatusBarWindowControllerStore, mStatusBarWindowStateController, new FakeStatusBarModeRepository(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index e804b33db1f7..eecf36e9343b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -309,8 +309,6 @@ public class ScrimControllerTest extends SysuiTestCase { // Attach behind scrim so flows that are collecting on it start running. ViewUtils.attachView(mScrimBehind); - mScrimController.setHasBackdrop(false); - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); mTestScope.getTestScheduler().runCurrent(); @@ -483,47 +481,6 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() { - mScrimController.setHasBackdrop(true); - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - - assertScrimTinted(Map.of( - mScrimInFront, true, - mScrimBehind, true - )); - } - - @Test - public void setHasBackdrop_withAodWallpaperAndAlbumArt() { - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - mScrimController.setHasBackdrop(true); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - - assertScrimTinted(Map.of( - mScrimInFront, true, - mScrimBehind, true - )); - } - - @Test public void transitionToAod_withFrontAlphaUpdates() { // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state. mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); @@ -1356,7 +1313,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); - mScrimController.setHasBackdrop(false); mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); mTestScope.getTestScheduler().runCurrent(); mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index e57e8d108529..15ef917343ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone.fragment; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; @@ -61,6 +60,7 @@ import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; @@ -633,7 +633,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, - FLAG_STATUS_BAR_RON_CHIPS, + StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() { resumeAndGetFragment(); @@ -660,8 +660,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() { resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -673,7 +673,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() { resumeAndGetFragment(); @@ -689,8 +689,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -705,9 +705,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() { + public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -724,8 +724,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -740,9 +740,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() { + public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -759,8 +759,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() { resumeAndGetFragment(); // Ongoing activity started @@ -781,9 +781,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() { + public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() { resumeAndGetFragment(); // Ongoing activity started @@ -804,7 +804,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void secondaryOngoingActivityEnded_chipHidden() { resumeAndGetFragment(); @@ -828,8 +828,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -846,9 +846,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() { + public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -866,8 +866,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback @@ -898,9 +898,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() { + public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 5996ef13a463..1ceb20adeebd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -198,6 +198,8 @@ import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewTransitions; import com.android.wm.shell.transition.Transitions; +import kotlin.Lazy; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -216,7 +218,6 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; -import kotlin.Lazy; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -2481,7 +2482,7 @@ public class BubblesTest extends SysuiTestCase { mEntryListener.onEntryAdded(mRow); - verify(mBubbleLogger).log(argThat(b -> b.getKey().equals(mRow.getKey())), + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED)); } @@ -2498,10 +2499,25 @@ public class BubblesTest extends SysuiTestCase { NotificationEntryHelper.modifyRanking(mRow).setTextChanged(true).build(); mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true); - verify(mBubbleLogger).log(argThat(b -> b.getKey().equals(mRow.getKey())), + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED)); } + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_dragBubbleToDismiss() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L); + + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE)); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); @@ -2687,6 +2703,10 @@ public class BubblesTest extends SysuiTestCase { assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded); } + private Bubble eqBubbleWithKey(String key) { + return argThat(b -> b.getKey().equals(key)); + } + private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener { int mStateChangeCalls = 0; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt index baaf60447cf9..703e2d1db200 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt @@ -49,8 +49,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos private val _isAlternateBouncerVisible = MutableStateFlow(false) override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow() override var lastAlternateBouncerVisibleTime: Long = 0L - private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false) - override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow() override val lastShownSecurityMode: MutableStateFlow<KeyguardSecurityModel.SecurityMode> = MutableStateFlow(KeyguardSecurityModel.SecurityMode.Invalid) override var bouncerDismissActionModel: BouncerDismissActionModel? = null @@ -63,10 +61,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos _isAlternateBouncerVisible.value = isVisible } - override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { - _isAlternateBouncerUIAvailable.value = isAvailable - } - override fun setPrimaryShow(isShowing: Boolean) { _primaryBouncerShow.value = isShowing } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt index 63323b239a78..8bbb8a0d320e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt @@ -16,33 +16,23 @@ package com.android.systemui.bouncer.domain.interactor -import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope -import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.statusbar.policy.keyguardStateController -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.systemClock val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by Kosmos.Fixture { AlternateBouncerInteractor( - statusBarStateController = statusBarStateController, - keyguardStateController = keyguardStateController, bouncerRepository = keyguardBouncerRepository, fingerprintPropertyRepository = fingerprintPropertyRepository, - biometricSettingsRepository = biometricSettingsRepository, systemClock = systemClock, - keyguardUpdateMonitor = keyguardUpdateMonitor, deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor }, keyguardInteractor = { keyguardInteractor }, keyguardTransitionInteractor = { keyguardTransitionInteractor }, @@ -54,21 +44,9 @@ val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by fun Kosmos.givenCanShowAlternateBouncer() { this.givenAlternateBouncerSupported() this.keyguardBouncerRepository.setPrimaryShow(false) - this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) - this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) this.deviceEntryFaceAuthRepository.setLockedOut(false) - whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) - whenever(this.keyguardStateController.isUnlocked).thenReturn(false) } fun Kosmos.givenAlternateBouncerSupported() { - if (DeviceEntryUdfpsRefactor.isEnabled) { - this.fingerprintPropertyRepository.supportsUdfps() - } else { - this.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true) - } -} - -fun Kosmos.givenCannotShowAlternateBouncer() { - this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) + this.fingerprintPropertyRepository.supportsUdfps() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt index c0152b26d7a3..41402badf141 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt @@ -17,7 +17,6 @@ package com.android.systemui.flags import android.platform.test.annotations.EnableFlags -import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT @@ -36,7 +35,6 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN, FLAG_PREDICTIVE_BACK_SYSUI, FLAG_SCENE_CONTAINER, - FLAG_DEVICE_ENTRY_UDFPS_REFACTOR, ) @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt index 4ccee6f52fa2..2aa27444a058 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt @@ -22,18 +22,16 @@ import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor val Kosmos.deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor by Kosmos.Fixture { DeviceEntrySideFpsOverlayInteractor( - applicationScope = testScope.backgroundScope, context = applicationContext, deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository, sceneInteractor = sceneInteractor, primaryBouncerInteractor = primaryBouncerInteractor, alternateBouncerInteractor = alternateBouncerInteractor, - keyguardUpdateMonitor = keyguardUpdateMonitor + keyguardUpdateMonitor = keyguardUpdateMonitor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt new file mode 100644 index 000000000000..e16756befb03 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles.impl.hearingdevices + +import com.android.systemui.accessibility.qs.QSAccessibilityModule +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger + +val Kosmos.qsHearingDevicesTileConfig by + Kosmos.Fixture { QSAccessibilityModule.provideHearingDevicesTileConfig(qsEventLogger) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt index c0d65a076ca0..2316a2fdcd2b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.packageManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.util.time.fakeSystemClock -val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by +val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by Kosmos.Fixture { - DemoRonChipViewModel( + DemoNotifChipViewModel( commandRegistry = commandRegistry, packageManager = packageManager, systemClock = fakeSystemClock, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt new file mode 100644 index 000000000000..af24c371d62b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor + +val Kosmos.notifChipsViewModel: NotifChipsViewModel by + Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 5382c1c4b8d0..0300bf4636ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -20,7 +20,8 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel import com.android.systemui.statusbar.chips.statusBarChipsLogger @@ -33,7 +34,8 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by shareToAppChipViewModel = shareToAppChipViewModel, castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel, callChipViewModel = callChipViewModel, - demoRonChipViewModel = demoRonChipViewModel, + notifChipsViewModel = notifChipsViewModel, + demoNotifChipViewModel = demoNotifChipViewModel, logger = statusBarChipsLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt index edd660490e4d..9fa3abfdc95c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt @@ -19,11 +19,18 @@ package com.android.systemui.statusbar.core import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarViewController +import org.mockito.kotlin.mock -class FakeStatusBarInitializer( - private val statusBarViewController: PhoneStatusBarViewController, - private val statusBarTransitions: PhoneStatusBarTransitions, -) : StatusBarInitializer { +class FakeStatusBarInitializer : StatusBarInitializer { + + val statusBarViewController = mock<PhoneStatusBarViewController>() + val statusBarTransitions = mock<PhoneStatusBarTransitions>() + + var startedByCoreStartable: Boolean = false + private set + + var initializedByCentralSurfaces: Boolean = false + private set override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null set(value) { @@ -31,5 +38,11 @@ class FakeStatusBarInitializer( value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions) } - override fun initializeStatusBar() {} + override fun initializeStatusBar() { + initializedByCentralSurfaces = true + } + + override fun start() { + startedByCoreStartable = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt index 73ed228f5aaa..8c218be6c982 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt @@ -16,14 +16,11 @@ package com.android.systemui.statusbar.core -import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions -import com.android.systemui.statusbar.phone.PhoneStatusBarViewController +import com.android.systemui.statusbar.window.StatusBarWindowController -class FakeStatusBarInitializerFactory( - private val statusBarViewController: PhoneStatusBarViewController, - private val statusBarTransitions: PhoneStatusBarTransitions, -) : StatusBarInitializer.Factory { +class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory { - override fun create(displayId: Int): StatusBarInitializer = - FakeStatusBarInitializer(statusBarViewController, statusBarTransitions) + override fun create( + statusBarWindowController: StatusBarWindowController + ): StatusBarInitializer = FakeStatusBarInitializer() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt new file mode 100644 index 000000000000..0c2cba981824 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import android.view.Display + +class FakeStatusBarInitializerStore : StatusBarInitializerStore { + + private val initializers = mutableMapOf<Int, FakeStatusBarInitializer>() + + override val defaultDisplay: FakeStatusBarInitializer + get() = forDisplay(Display.DEFAULT_DISPLAY) + + override fun forDisplay(displayId: Int): FakeStatusBarInitializer { + return initializers.computeIfAbsent(displayId) { FakeStatusBarInitializer() } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt new file mode 100644 index 000000000000..9197dcdc3f68 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository +import kotlinx.coroutines.CoroutineScope +import org.mockito.kotlin.mock + +class FakeStatusBarOrchestratorFactory : StatusBarOrchestrator.Factory { + + private val createdOrchestrators = mutableMapOf<Int, StatusBarOrchestrator>() + + fun createdOrchestratorForDisplay(displayId: Int): StatusBarOrchestrator? = + createdOrchestrators[displayId] + + override fun create( + displayId: Int, + displayScope: CoroutineScope, + statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository, + statusBarModeRepository: StatusBarModePerDisplayRepository, + statusBarInitializer: StatusBarInitializer, + statusBarWindowController: StatusBarWindowController, + ): StatusBarOrchestrator = + mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt index 7ad715ba5a89..8066b9138c99 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt @@ -19,20 +19,13 @@ package com.android.systemui.statusbar.core import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.statusbar.phone.phoneStatusBarTransitions -import com.android.systemui.statusbar.phone.phoneStatusBarViewController +import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore -val Kosmos.fakeStatusBarInitializer by - Kosmos.Fixture { - FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions) - } +val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() } var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer } -val Kosmos.fakeStatusBarInitializerFactory by - Kosmos.Fixture { - FakeStatusBarInitializerFactory(phoneStatusBarViewController, phoneStatusBarTransitions) - } +val Kosmos.fakeStatusBarInitializerFactory by Kosmos.Fixture { FakeStatusBarInitializerFactory() } var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by Kosmos.Fixture { fakeStatusBarInitializerFactory } @@ -43,5 +36,11 @@ val Kosmos.multiDisplayStatusBarInitializerStore by applicationCoroutineScope, fakeStatusBarInitializerFactory, displayRepository, + fakeStatusBarWindowControllerStore, ) } + +val Kosmos.fakeStatusBarInitializerStore by Kosmos.Fixture { FakeStatusBarInitializerStore() } + +var Kosmos.statusBarInitializerStore: StatusBarInitializerStore by + Kosmos.Fixture { fakeStatusBarInitializerStore } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt index 54de293b8911..87f7142b8817 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt @@ -16,7 +16,11 @@ package com.android.systemui.statusbar.core +import android.content.testableContext import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.display.data.repository.displayScopeRepository +import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.mockDemoModeController @@ -24,20 +28,25 @@ import com.android.systemui.plugins.mockPluginDependencyProvider import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.shade.mockNotificationShadeWindowViewController import com.android.systemui.shade.mockShadeSurface -import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository +import com.android.systemui.statusbar.data.repository.statusBarModeRepository import com.android.systemui.statusbar.mockNotificationRemoteInputManager import com.android.systemui.statusbar.phone.mockAutoHideController +import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore +import com.android.systemui.statusbar.window.fakeStatusBarWindowController +import com.android.systemui.statusbar.window.statusBarWindowControllerStore import com.android.wm.shell.bubbles.bubblesOptional val Kosmos.statusBarOrchestrator by Kosmos.Fixture { StatusBarOrchestrator( + testableContext.displayId, applicationCoroutineScope, + fakeStatusBarWindowStatePerDisplayRepository, + fakeStatusBarModePerDisplayRepository, fakeStatusBarInitializer, - fakeStatusBarModeRepository, - fakeStatusBarWindowControllerStore, + fakeStatusBarWindowController, mockDemoModeController, mockPluginDependencyProvider, mockAutoHideController, @@ -45,8 +54,28 @@ val Kosmos.statusBarOrchestrator by { mockNotificationShadeWindowViewController }, mockShadeSurface, bubblesOptional, - statusBarWindowStateRepositoryStore, + dumpManager, powerInteractor, primaryBouncerInteractor, ) } + +val Kosmos.fakeStatusBarOrchestratorFactory by Kosmos.Fixture { FakeStatusBarOrchestratorFactory() } + +var Kosmos.statusBarOrchestratorFactory: StatusBarOrchestrator.Factory by + Kosmos.Fixture { fakeStatusBarOrchestratorFactory } + +val Kosmos.multiDisplayStatusBarStarter by + Kosmos.Fixture { + MultiDisplayStatusBarStarter( + applicationCoroutineScope, + displayScopeRepository, + statusBarOrchestratorFactory, + statusBarWindowStateRepositoryStore, + statusBarModeRepository, + displayRepository, + statusBarInitializerStore, + statusBarWindowControllerStore, + statusBarInitializerStore, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt index 60690838fcdf..285cebb96cae 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt @@ -20,7 +20,6 @@ import android.view.Display import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.data.model.StatusBarAppearance import com.android.systemui.statusbar.data.model.StatusBarMode -import com.google.common.truth.Truth.assertThat import dagger.Binds import dagger.Module import javax.inject.Inject @@ -37,7 +36,6 @@ class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositor FakeStatusBarModePerDisplayRepository() override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository { - assertThat(displayId).isEqualTo(DISPLAY_ID) return defaultDisplay } } @@ -51,6 +49,7 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository override fun showTransient() { isTransientShown.value = true } + override fun clearTransient() { isTransientShown.value = false } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt index 0f2b477db67a..12db2f74197d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt @@ -18,6 +18,9 @@ package com.android.systemui.statusbar.data.repository import com.android.systemui.kosmos.Kosmos +val Kosmos.fakeStatusBarModePerDisplayRepository by + Kosmos.Fixture { FakeStatusBarModePerDisplayRepository() } + val Kosmos.statusBarModeRepository: StatusBarModeRepositoryStore by Kosmos.Fixture { fakeStatusBarModeRepository } val Kosmos.fakeStatusBarModeRepository by Kosmos.Fixture { FakeStatusBarModeRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index fc4f05df26ed..7f4c670b05aa 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -69,6 +69,9 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag +import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderImpl +import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger import com.android.systemui.statusbar.phone.KeyguardBypassController @@ -99,7 +102,7 @@ import org.mockito.Mockito class ExpandableNotificationRowBuilder( private val context: Context, dependency: TestableDependency, - private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic() + private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(), ) { private val mMockLogger: ExpandableNotificationRowLogger @@ -161,21 +164,21 @@ class ExpandableNotificationRowBuilder( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "true", - true + true, ) setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_ENABLED, "true", - true + true, ) setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "false", - true + true, ) - } + }, ) val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags) val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY) @@ -192,21 +195,21 @@ class ExpandableNotificationRowBuilder( Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY), remoteInputManager = remoteInputManager, smartReplyController = mSmartReplyController, - context = context + context = context, ), smartActionsInflater = SmartActionInflaterImpl( constants = mSmartReplyConstants, activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY), smartReplyController = mSmartReplyController, - headsUpManager = mHeadsUpManager - ) + headsUpManager = mHeadsUpManager, + ), ) val notifLayoutInflaterFactoryProvider = object : NotifLayoutInflaterFactory.Provider { override fun provide( row: ExpandableNotificationRow, - layoutType: Int + layoutType: Int, ): NotifLayoutInflaterFactory = NotifLayoutInflaterFactory(row, layoutType, remoteViewsFactories) } @@ -270,14 +273,14 @@ class ExpandableNotificationRowBuilder( whenever( mOnUserInteractionCallback.registerFutureDismissal( ArgumentMatchers.any(), - ArgumentMatchers.anyInt() + ArgumentMatchers.anyInt(), ) ) .thenReturn(mFutureDismissalRunnable) } private fun getNotifRemoteViewsFactoryContainer( - featureFlags: FeatureFlags, + featureFlags: FeatureFlags ): NotifRemoteViewsFactoryContainer { return NotifRemoteViewsFactoryContainerImpl( featureFlags, @@ -285,6 +288,10 @@ class ExpandableNotificationRowBuilder( BigPictureLayoutInflaterFactory(), NotificationOptimizedLinearLayoutFactory(), { Mockito.mock(NotificationViewFlipperFactory::class.java) }, + NotificationRowIconViewInflaterFactory( + AppIconProviderImpl(context), + NotificationIconStyleProviderImpl(), + ), ) } @@ -293,7 +300,7 @@ class ExpandableNotificationRowBuilder( NotificationChannel( notification.channelId, notification.channelId, - NotificationManager.IMPORTANCE_DEFAULT + NotificationManager.IMPORTANCE_DEFAULT, ) channel.isBlockable = true val entry = @@ -321,7 +328,7 @@ class ExpandableNotificationRowBuilder( private fun generateRow( entry: NotificationEntry, - @InflationFlag extraInflationFlags: Int + @InflationFlag extraInflationFlags: Int, ): ExpandableNotificationRow { // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be // set, but we do not want to override an existing value that is needed by a specific test. @@ -329,7 +336,7 @@ class ExpandableNotificationRowBuilder( val rowInflaterTask = RowInflaterTask( mFakeSystemClock, - Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY) + Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY), ) val row = rowInflaterTask.inflateSynchronously(context, null, entry) @@ -364,7 +371,7 @@ class ExpandableNotificationRowBuilder( mSmartReplyController, featureFlags, Mockito.mock(IStatusBarService::class.java, STUB_ONLY), - Mockito.mock(UiEventLogger::class.java, STUB_ONLY) + Mockito.mock(UiEventLogger::class.java, STUB_ONLY), ) row.setAboveShelfChangedListener { aboveShelf: Boolean -> } mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt new file mode 100644 index 000000000000..08c6bbab6dd6 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt @@ -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.systemui.statusbar.notification.row.icon + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt new file mode 100644 index 000000000000..611c90a6f4e8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt @@ -0,0 +1,21 @@ +/* + * 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.icon + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt new file mode 100644 index 000000000000..14f4d75d5647 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeBluetoothController by Kosmos.Fixture { FakeBluetoothController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt new file mode 100644 index 000000000000..4876cd8a6086 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy + +import android.bluetooth.BluetoothAdapter +import com.android.internal.annotations.VisibleForTesting +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.systemui.statusbar.policy.BluetoothController.Callback +import java.io.PrintWriter +import java.util.Collections +import java.util.concurrent.Executor + +class FakeBluetoothController : BluetoothController { + + private var callbacks = mutableListOf<Callback>() + private var enabled = false + + override fun addCallback(listener: Callback) { + callbacks += listener + listener.onBluetoothStateChange(isBluetoothEnabled) + } + + override fun removeCallback(listener: Callback) { + callbacks -= listener + } + + override fun dump(pw: PrintWriter, args: Array<out String>) {} + + override fun isBluetoothSupported(): Boolean = false + + override fun isBluetoothEnabled(): Boolean = enabled + + override fun getBluetoothState(): Int = 0 + + override fun isBluetoothConnected(): Boolean = false + + override fun isBluetoothConnecting(): Boolean = false + + override fun isBluetoothAudioProfileOnly(): Boolean = false + + override fun isBluetoothAudioActive(): Boolean = false + + override fun getConnectedDeviceName(): String? = null + + override fun setBluetoothEnabled(enabled: Boolean) { + this.enabled = enabled + callbacks.forEach { it.onBluetoothStateChange(enabled) } + } + + override fun canConfigBluetooth(): Boolean = false + + override fun getConnectedDevices(): MutableList<CachedBluetoothDevice> = Collections.emptyList() + + override fun addOnMetadataChangedListener( + device: CachedBluetoothDevice?, + executor: Executor?, + listener: BluetoothAdapter.OnMetadataChangedListener?, + ) {} + + override fun removeOnMetadataChangedListener( + device: CachedBluetoothDevice?, + listener: BluetoothAdapter.OnMetadataChangedListener?, + ) {} + + /** Trigger the [Callback.onBluetoothDevicesChanged] method for all registered callbacks. */ + @VisibleForTesting + fun onBluetoothDevicesChanged() { + callbacks.forEach { it.onBluetoothDevicesChanged() } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt index 2205a3b6e084..cbaf2bd65083 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt @@ -21,6 +21,9 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.settings.displayTracker import com.android.systemui.statusbar.commandQueue +val Kosmos.fakeStatusBarWindowStatePerDisplayRepository by + Kosmos.Fixture { FakeStatusBarWindowStatePerDisplayRepository() } + val Kosmos.fakeStatusBarWindowStateRepositoryStore by Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() } diff --git a/packages/Vcn/OWNERS b/packages/Vcn/OWNERS new file mode 100644 index 000000000000..ff2146e76b47 --- /dev/null +++ b/packages/Vcn/OWNERS @@ -0,0 +1,6 @@ +evitayan@google.com +nharold@google.com +benedictwong@google.com #{LAST_RESORT_SUGGESTION} +yangji@google.com #{LAST_RESORT_SUGGESTION} + +include platform/packages/modules/common:/MODULES_OWNERS
\ No newline at end of file diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index bfa801f30955..8e884bc3484b 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -372,6 +372,7 @@ java_library { android_ravenwood_libgroup { name: "ravenwood-runtime", data: [ + ":system-build.prop", ":framework-res", ":ravenwood-empty-res", ":framework-platform-compat-config", diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 5894476b9201..24950e65c174 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -20,12 +20,14 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_R import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP; +import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Instrumentation; import android.app.ResourcesManager; @@ -81,6 +83,8 @@ public class RavenwoodRuntimeEnvironmentController { private static final String MAIN_THREAD_NAME = "RavenwoodMain"; private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop"; private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime"; + private static final String RAVENWOOD_BUILD_PROP = + getRavenwoodRuntimePath() + "ravenwood-data/build.prop"; /** * When enabled, attempt to dump all thread stacks just before we hit the @@ -158,7 +162,8 @@ public class RavenwoodRuntimeEnvironmentController { System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME)); // Do the basic set up for the android sysprops. - setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES); + RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP); + setSystemProperties(null); // Make sure libandroid_runtime is loaded. RavenwoodNativeLoader.loadFrameworkNativeCode(); @@ -329,7 +334,7 @@ public class RavenwoodRuntimeEnvironmentController { LocalServices.removeAllServicesForTest(); ServiceManager.reset$ravenwood(); - setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES); + setSystemProperties(null); if (sOriginalIdentityToken != -1) { Binder.restoreCallingIdentity(sOriginalIdentityToken); } @@ -388,9 +393,10 @@ public class RavenwoodRuntimeEnvironmentController { /** * Set the current configuration to the actual SystemProperties. */ - private static void setSystemProperties(RavenwoodSystemProperties systemProperties) { + private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) { SystemProperties.clearChangeCallbacksForTest(); RavenwoodRuntimeNative.clearSystemProperties(); + if (systemProperties == null) systemProperties = new RavenwoodSystemProperties(); sProps = new RavenwoodSystemProperties(systemProperties, true); for (var entry : systemProperties.getValues().entrySet()) { RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue()); diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java index f1e1ef672871..ced151927fdc 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java @@ -18,12 +18,94 @@ package android.platform.test.ravenwood; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP; +import com.android.ravenwood.common.RavenwoodCommonUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class RavenwoodSystemProperties { + private static final String TAG = "RavenwoodSystemProperties"; + + private static final Map<String, String> sDefaultValues = new HashMap<>(); + + private static final String[] PARTITIONS = { + "bootimage", + "odm", + "product", + "system", + "system_ext", + "vendor", + "vendor_dlkm", + }; + + /** + * More info about property file loading: system/core/init/property_service.cpp + * In the following logic, the only partition we would need to consider is "system", + * since we only read from system-build.prop + */ + static void initialize(String propFile) { + // Load all properties from build.prop + try { + Files.readAllLines(Path.of(propFile)).stream() + .map(String::trim) + .filter(s -> !s.startsWith("#")) + .map(s -> s.split("\\s*=\\s*", 2)) + .filter(a -> a.length == 2) + .forEach(a -> sDefaultValues.put(a[0], a[1])); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // If ro.product.${name} is not set, derive from ro.product.${partition}.${name} + // If ro.product.cpu.abilist* is not set, derive from ro.${partition}.product.cpu.abilist* + for (var entry : Set.copyOf(sDefaultValues.entrySet())) { + final String key; + if (entry.getKey().startsWith("ro.product.system.")) { + var name = entry.getKey().substring(18); + key = "ro.product." + name; + + } else if (entry.getKey().startsWith("ro.system.product.cpu.abilist")) { + var name = entry.getKey().substring(22); + key = "ro.product.cpu." + name; + } else { + continue; + } + if (!sDefaultValues.containsKey(key)) { + sDefaultValues.put(key, entry.getValue()); + } + } + + // Some other custom values + sDefaultValues.put("ro.board.first_api_level", "1"); + sDefaultValues.put("ro.product.first_api_level", "1"); + sDefaultValues.put("ro.soc.manufacturer", "Android"); + sDefaultValues.put("ro.soc.model", "Ravenwood"); + sDefaultValues.put(RAVENWOOD_SYSPROP, "1"); + + // Log all values + sDefaultValues.forEach((key, value) -> RavenwoodCommonUtils.log(TAG, key + "=" + value)); + + // Copy ro.product.* and ro.build.* to all partitions, just in case + // We don't want to log these because these are just a lot of duplicate values + for (var entry : Set.copyOf(sDefaultValues.entrySet())) { + var key = entry.getKey(); + if (key.startsWith("ro.product.") || key.startsWith("ro.build.")) { + var name = key.substring(3); + for (String partition : PARTITIONS) { + var newKey = "ro." + partition + "." + name; + if (!sDefaultValues.containsKey(newKey)) { + sDefaultValues.put(newKey, entry.getValue()); + } + } + } + } + } + private volatile boolean mIsImmutable; private final Map<String, String> mValues = new HashMap<>(); @@ -35,47 +117,15 @@ public class RavenwoodSystemProperties { private final Set<String> mKeyWritable = new HashSet<>(); public RavenwoodSystemProperties() { - // TODO: load these values from build.prop generated files - setValueForPartitions("product.brand", "Android"); - setValueForPartitions("product.device", "Ravenwood"); - setValueForPartitions("product.manufacturer", "Android"); - setValueForPartitions("product.model", "Ravenwood"); - setValueForPartitions("product.name", "Ravenwood"); - - setValueForPartitions("product.cpu.abilist", "x86_64"); - setValueForPartitions("product.cpu.abilist32", ""); - setValueForPartitions("product.cpu.abilist64", "x86_64"); - - setValueForPartitions("build.date", "Thu Jan 01 00:00:00 GMT 2024"); - setValueForPartitions("build.date.utc", "1704092400"); - setValueForPartitions("build.id", "MAIN"); - setValueForPartitions("build.tags", "dev-keys"); - setValueForPartitions("build.type", "userdebug"); - setValueForPartitions("build.version.all_codenames", "REL"); - setValueForPartitions("build.version.codename", "REL"); - setValueForPartitions("build.version.incremental", "userdebug.ravenwood.20240101"); - setValueForPartitions("build.version.known_codenames", "REL"); - setValueForPartitions("build.version.release", "14"); - setValueForPartitions("build.version.release_or_codename", "VanillaIceCream"); - setValueForPartitions("build.version.sdk", "34"); - - setValue("ro.board.first_api_level", "1"); - setValue("ro.product.first_api_level", "1"); - - setValue("ro.soc.manufacturer", "Android"); - setValue("ro.soc.model", "Ravenwood"); - - setValue("ro.debuggable", "1"); - - setValue(RAVENWOOD_SYSPROP, "1"); + mValues.putAll(sDefaultValues); } /** Copy constructor */ public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) { - this.mKeyReadable.addAll(source.mKeyReadable); - this.mKeyWritable.addAll(source.mKeyWritable); - this.mValues.putAll(source.mValues); - this.mIsImmutable = immutable; + mKeyReadable.addAll(source.mKeyReadable); + mKeyWritable.addAll(source.mKeyWritable); + mValues.putAll(source.mValues); + mIsImmutable = immutable; } public Map<String, String> getValues() { @@ -123,36 +173,12 @@ public class RavenwoodSystemProperties { return mKeyWritable.contains(key); } - private static final String[] PARTITIONS = { - "bootimage", - "odm", - "product", - "system", - "system_ext", - "vendor", - "vendor_dlkm", - }; - private void ensureNotImmutable() { if (mIsImmutable) { throw new RuntimeException("Unable to update immutable instance"); } } - /** - * Set the given property for all possible partitions where it could be defined. For - * example, the value of {@code ro.build.type} is typically also mirrored under - * {@code ro.system.build.type}, etc. - */ - private void setValueForPartitions(String key, String value) { - ensureNotImmutable(); - - setValue("ro." + key, value); - for (String partition : PARTITIONS) { - setValue("ro." + partition + "." + key, value); - } - } - public void setValue(String key, Object value) { ensureNotImmutable(); @@ -195,11 +221,4 @@ public class RavenwoodSystemProperties { return key; } } - - /** - * Return an immutable, default instance. - */ - // Create a default instance, and make an immutable copy of it. - public static final RavenwoodSystemProperties DEFAULT_VALUES = - new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true); } diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/tests/minimum-test/Android.bp index e4ed3d5b0b73..e4ed3d5b0b73 100644 --- a/ravenwood/minimum-test/Android.bp +++ b/ravenwood/tests/minimum-test/Android.bp diff --git a/ravenwood/minimum-test/README.md b/ravenwood/tests/minimum-test/README.md index 6b0abe968053..6b0abe968053 100644 --- a/ravenwood/minimum-test/README.md +++ b/ravenwood/tests/minimum-test/README.md diff --git a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java index 30abaa2e7d38..30abaa2e7d38 100644 --- a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java +++ b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java diff --git a/ravenwood/mockito/Android.bp b/ravenwood/tests/mockito/Android.bp index d91537bbc334..d91537bbc334 100644 --- a/ravenwood/mockito/Android.bp +++ b/ravenwood/tests/mockito/Android.bp diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/tests/mockito/AndroidManifest.xml index 15f0a2934b5f..15f0a2934b5f 100644 --- a/ravenwood/mockito/AndroidManifest.xml +++ b/ravenwood/tests/mockito/AndroidManifest.xml diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/tests/mockito/AndroidTest.xml index 5ba9b1ff2cd8..5ba9b1ff2cd8 100644 --- a/ravenwood/mockito/AndroidTest.xml +++ b/ravenwood/tests/mockito/AndroidTest.xml diff --git a/ravenwood/mockito/README.md b/ravenwood/tests/mockito/README.md index 4ceb795fe14a..4ceb795fe14a 100644 --- a/ravenwood/mockito/README.md +++ b/ravenwood/tests/mockito/README.md diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java index d566977bd15c..d566977bd15c 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java index aa2b7611da37..aa2b7611da37 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java index fcc6c9cc447d..fcc6c9cc447d 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java diff --git a/ravenwood/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp index c14576550f78..c14576550f78 100644 --- a/ravenwood/resapk_test/Android.bp +++ b/ravenwood/tests/resapk_test/Android.bp diff --git a/ravenwood/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp index 10ed5e2f8410..10ed5e2f8410 100644 --- a/ravenwood/resapk_test/apk/Android.bp +++ b/ravenwood/tests/resapk_test/apk/Android.bp diff --git a/ravenwood/resapk_test/apk/AndroidManifest.xml b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml index f34d8b2f4e81..f34d8b2f4e81 100644 --- a/ravenwood/resapk_test/apk/AndroidManifest.xml +++ b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml diff --git a/ravenwood/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml index 23d4c0f21007..23d4c0f21007 100644 --- a/ravenwood/resapk_test/apk/res/values/strings.xml +++ b/ravenwood/tests/resapk_test/apk/res/values/strings.xml diff --git a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java index e547114bbe40..e547114bbe40 100644 --- a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java +++ b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp index 410292001670..410292001670 100644 --- a/ravenwood/runtime-test/Android.bp +++ b/ravenwood/tests/runtime-test/Android.bp diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java index 633ed4e9d10a..633ed4e9d10a 100644 --- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java index c2230c739ccf..c2230c739ccf 100644 --- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java diff --git a/ravenwood/services-test/Android.bp b/ravenwood/tests/services-test/Android.bp index 39858f05e80d..39858f05e80d 100644 --- a/ravenwood/services-test/Android.bp +++ b/ravenwood/tests/services-test/Android.bp diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java index f833782bc8bb..f833782bc8bb 100644 --- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java index b3d3963270ee..b3d3963270ee 100644 --- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java diff --git a/ravenwood/tools/hoststubgen/.gitignore b/ravenwood/tools/hoststubgen/.gitignore new file mode 100644 index 000000000000..82158c9b25e3 --- /dev/null +++ b/ravenwood/tools/hoststubgen/.gitignore @@ -0,0 +1,4 @@ +framework-all-stub-out +out/ +*-out/ +*.log diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp index a5ff4964b0a4..a5ff4964b0a4 100644 --- a/tools/hoststubgen/hoststubgen/Android.bp +++ b/ravenwood/tools/hoststubgen/Android.bp diff --git a/tools/hoststubgen/README.md b/ravenwood/tools/hoststubgen/README.md index 1a895dc7dfce..615e7671bea1 100644 --- a/tools/hoststubgen/README.md +++ b/ravenwood/tools/hoststubgen/README.md @@ -11,7 +11,7 @@ used at runtime. - HostStubGen itself is design to be agnostic to Android. It doesn't use any Android APIs (hidden or not). But it may use Android specific knowledge -- e.g. as of now, -AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes. +AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes. - `test-tiny-framework/` contains basic tests that are agnostic to Android. @@ -20,19 +20,16 @@ AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classe ## Directories and files -- `hoststubgen/` - Contains source code of the "hoststubgen" tool and relevant code +- `src/` - - `src/` + HostStubGen tool source code. - HostStubGen tool source code. +- `annotations-src/` See `Android.bp`. +- `helper-framework-buildtime-src/` See `Android.bp`. +- `helper-framework-runtime-src/` See `Android.bp`. +- `helper-runtime-src/` See `Android.bp`. - - `annotations-src/` See `Android.bp`. - - `helper-framework-buildtime-src/` See `Android.bp`. - - `helper-framework-runtime-src/` See `Android.bp`. - - `helper-runtime-src/` See `Android.bp`. - - - `test-tiny-framework/` See `README.md` in it. +- `test-tiny-framework/` See `README.md` in it. - `scripts` - `dump-jar.sh` @@ -78,4 +75,4 @@ $ ./scripts/run-all-tests.sh - At some point, we can move or delete all Android specific code to `frameworks/base/ravenwood`. - `helper-framework-*-src` should be moved to `frameworks/base/ravenwood` - - `test-framework` should be deleted.
\ No newline at end of file + - `test-framework` should be deleted. diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java index a774336a897c..a774336a897c 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java index 501fd652145e..501fd652145e 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java index 06ad1c266a14..06ad1c266a14 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java index bc9471b84b97..bc9471b84b97 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java index 28ad236a66f3..28ad236a66f3 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java index 46e5078fb05d..46e5078fb05d 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java index eec72269e0d3..eec72269e0d3 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java index 510a67e0aaed..510a67e0aaed 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java index cd1bef4be505..cd1bef4be505 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java index 3d1ddea2cbb7..3d1ddea2cbb7 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java index b10f0ff1a4b1..b10f0ff1a4b1 100644 --- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java +++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java diff --git a/tools/hoststubgen/common.sh b/ravenwood/tools/hoststubgen/common.sh index b49ee39a3142..b49ee39a3142 100644 --- a/tools/hoststubgen/common.sh +++ b/ravenwood/tools/hoststubgen/common.sh diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/ravenwood/tools/hoststubgen/framework-policy-override.txt index af3789e270a4..af3789e270a4 100644 --- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt +++ b/ravenwood/tools/hoststubgen/framework-policy-override.txt diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java index b01710347537..b01710347537 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java index 18ef1bab203e..18ef1bab203e 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java index 99e38c0b1725..99e38c0b1725 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java index 4933cf8784d9..4933cf8784d9 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java index c54c2c111229..c54c2c111229 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java index 29f7be008eef..29f7be008eef 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java index 78fd8f7f960a..78fd8f7f960a 100644 --- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java +++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt index 001943c18d6b..001943c18d6b 100644 --- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt +++ b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt diff --git a/tools/hoststubgen/hoststubgen/invoketest/Android.bp b/ravenwood/tools/hoststubgen/invoketest/Android.bp index 7e90e421a1f9..7e90e421a1f9 100644 --- a/tools/hoststubgen/hoststubgen/invoketest/Android.bp +++ b/ravenwood/tools/hoststubgen/invoketest/Android.bp diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh index 084448d0a797..084448d0a797 100755 --- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh +++ b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/ravenwood/tools/hoststubgen/jarjar-rules.txt index b1f2fc21c8e9..b1f2fc21c8e9 100644 --- a/tools/hoststubgen/hoststubgen/jarjar-rules.txt +++ b/ravenwood/tools/hoststubgen/jarjar-rules.txt diff --git a/tools/hoststubgen/scripts/Android.bp b/ravenwood/tools/hoststubgen/scripts/Android.bp index b1ba07ec540d..b1ba07ec540d 100644 --- a/tools/hoststubgen/scripts/Android.bp +++ b/ravenwood/tools/hoststubgen/scripts/Android.bp diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh index c3605a9ffaa5..c3605a9ffaa5 100755 --- a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh +++ b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh diff --git a/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar index 87652451359d..87652451359d 100755 --- a/tools/hoststubgen/scripts/dump-jar +++ b/ravenwood/tools/hoststubgen/scripts/dump-jar diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt index f59e143c1e4e..f59e143c1e4e 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt index 6d8d7b768b91..6d8d7b768b91 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt index a218c5599553..a218c5599553 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt index 4bcee409aaec..4bcee409aaec 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt index 85064661cd2b..85064661cd2b 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt index 55e853e3e2fb..55e853e3e2fb 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt index 9045db210495..9045db210495 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt index 10179eefcb95..10179eefcb95 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt index a02082d12934..a02082d12934 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt index e2647eb13ed3..e2647eb13ed3 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt index 5e4e70f0cbaa..5e4e70f0cbaa 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt index 6b360b79c327..6b360b79c327 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt index 36adf0626415..36adf0626415 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt index f8bb526d0a86..f8bb526d0a86 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt index be3c59c80152..be3c59c80152 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt index d771003a955d..d771003a955d 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt index b8b0d8a31268..b8b0d8a31268 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt index 2f2f81b05ad1..2f2f81b05ad1 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt index b10165b835f2..b10165b835f2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt index c5a2f9ff5e96..c5a2f9ff5e96 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt index 474da6dfa1b9..474da6dfa1b9 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt index 59fa464a7212..59fa464a7212 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt index 00e7d77fa6e7..00e7d77fa6e7 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt index f99ce906240a..f99ce906240a 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt index c67e6714d4c2..c67e6714d4c2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt index 18a1e16bcf3a..18a1e16bcf3a 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt index fd7474b55fa6..fd7474b55fa6 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index caf80ebec0c9..caf80ebec0c9 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt index d45f41407a52..d45f41407a52 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt index a78c6552b8d0..a78c6552b8d0 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt index d6aa7617fd59..d6aa7617fd59 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt index 1b3d79cddb8e..1b3d79cddb8e 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt index 261ef59c45c7..261ef59c45c7 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt index 55d0c0e555f1..55d0c0e555f1 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt index dc4f26bdda34..dc4f26bdda34 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt index 567a69e43b58..567a69e43b58 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt index e90ecd7ef678..e90ecd7ef678 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp index ba2c869adfe8..1570549ec27d 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp @@ -16,7 +16,7 @@ java_library { static_libs: [ "hoststubgen-annotations", ], - visibility: ["//frameworks/base/tools/hoststubgen:__subpackages__"], + visibility: ["//frameworks/base/ravenwood/tools/hoststubgen:__subpackages__"], } // Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules. diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml index 84aad69c33bc..84aad69c33bc 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md index 344b4e953b23..344b4e953b23 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt index de4cb0c536c1..de4cb0c536c1 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh index 3726ca972564..3726ca972564 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index 103e152c7e39..103e152c7e39 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt index eeec554e954c..eeec554e954c 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt index 0f8af92dc486..0f8af92dc486 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt index 3c138d21b75d..3c138d21b75d 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh index 80ebf3adab3d..80ebf3adab3d 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py index cee29dcd1d59..cee29dcd1d59 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java index 0a07c2b91fc3..0a07c2b91fc3 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java index b1bedf4b6853..b1bedf4b6853 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java index 3415deb957ed..3415deb957ed 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java index f734790c8dd9..f734790c8dd9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java index e83163edb5e5..e83163edb5e5 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java index 3df21d9a5647..3df21d9a5647 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java index cc665de9cd01..cc665de9cd01 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java index f833ad814513..f833ad814513 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java index c023169b5601..c023169b5601 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java index f7cae7d255fe..f7cae7d255fe 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java index ec1efba99c77..ec1efba99c77 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java index 1ca653ec7da6..1ca653ec7da6 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java index 57c69a336654..57c69a336654 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java index 04a551c8c46e..04a551c8c46e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java index c7a29a1cc0f9..c7a29a1cc0f9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java index c1ea2ee59fbb..c1ea2ee59fbb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java index 941fcff31d8e..941fcff31d8e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java index 707bc0ebb4db..707bc0ebb4db 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java index 8319ced6109a..8319ced6109a 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java index 6a52e4401b45..6a52e4401b45 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java index 1374a288f7aa..1374a288f7aa 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java index 361a7fd04842..361a7fd04842 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java index 716595a44243..716595a44243 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java index 03c9e2a7b5bb..03c9e2a7b5bb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java index 3ca8f1f172bd..3ca8f1f172bd 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java index a6c14f09b680..a6c14f09b680 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java index 2e353709fd1e..2e353709fd1e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java index fe4cee64b2f9..fe4cee64b2f9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java index 12012fcdf6ca..12012fcdf6ca 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java index 8d48ee6f6858..8d48ee6f6858 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java index 6748430a8e6f..6748430a8e6f 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java index 58aa5c3b74eb..58aa5c3b74eb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java index c1c3d624b126..c1c3d624b126 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java index 398b56975f1c..398b56975f1c 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java index 44cbd8f9bffa..44cbd8f9bffa 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java index 42355a34b65c..42355a34b65c 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java index 09c80992e450..09c80992e450 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java index 0806a478d756..0806a478d756 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java index eaa8528a3f31..eaa8528a3f31 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java index 778c5aaf27f0..778c5aaf27f0 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java index 493f7c83c0f0..493f7c83c0f0 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java index 2aa1de18b7f4..2aa1de18b7f4 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java index d9eae0934c42..d9eae0934c42 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java index 9ee42836ac9a..9ee42836ac9a 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java index 50ec2cbc9c6e..50ec2cbc9c6e 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java index 3f3659644a80..3f3659644a80 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java index 960060c8a036..960060c8a036 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java index c678eaa789b0..c678eaa789b0 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java index 1cff484c3cd8..1cff484c3cd8 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java index 84e7173c71b8..84e7173c71b8 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java index fa5866451e83..fa5866451e83 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java index 92f41ac63cdb..92f41ac63cdb 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java index 1ae049371229..1ae049371229 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java index 68673dc2a5b8..68673dc2a5b8 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java index 1816b383f6f7..1816b383f6f7 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt index 5b2795c4cff2..5b2795c4cff2 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt index d4e75d43a54a..d4e75d43a54a 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt index 081d03909926..081d03909926 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt index 75e2536a98fa..75e2536a98fa 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt +++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java index 5d574083b326..28e57775523b 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java @@ -225,12 +225,6 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { .thenAccept( canExecute -> { if (!canExecute) { - safeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_DENIED, - "Caller does not have permission to execute the" - + " appfunction", - /* extras= */ null)); throw new SecurityException( "Caller does not have permission to execute the" + " appfunction"); diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ba8448548365..1803424ae7d7 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -49,3 +49,10 @@ flag { description: "Include the session id into the FillEventHistory events as part of ClientState" bug: "333927465" } + +flag { + name: "highlight_autofill_single_field" + namespace: "autofill" + description: "Highlight single field after autofill selection" + bug: "41496744" +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 2fa0e0d0d946..8f12b1db8f29 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -39,6 +39,7 @@ import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; +import static android.service.autofill.Flags.highlightAutofillSingleField; import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; @@ -49,7 +50,6 @@ import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN; import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; - import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED; import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER; @@ -104,7 +104,6 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Activity; import android.app.ActivityTaskManager; import android.app.IAssistDataReceiver; import android.app.PendingIntent; @@ -143,7 +142,6 @@ import android.os.TransactionTooLargeException; import android.service.assist.classification.FieldClassificationRequest; import android.service.assist.classification.FieldClassificationResponse; import android.service.autofill.AutofillFieldClassificationService.Scores; -import android.service.autofill.AutofillService; import android.service.autofill.CompositeUserData; import android.service.autofill.ConvertCredentialResponse; import android.service.autofill.Dataset; @@ -186,7 +184,6 @@ import android.view.autofill.IAutoFillManagerClient; import android.view.autofill.IAutofillWindowPresenter; import android.view.inputmethod.InlineSuggestionsRequest; import android.widget.RemoteViews; - import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -198,7 +195,6 @@ import com.android.server.autofill.ui.InlineFillUi; import com.android.server.autofill.ui.PendingUi; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; - import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -222,18 +218,21 @@ import java.util.function.Function; /** * A session for a given activity. * - * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track - * of the current {@link ViewState} to display the appropriate UI. + * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track of + * the current {@link ViewState} to display the appropriate UI. * - * <p>Although the autofill requests and callbacks are stateless from the service's point of - * view, we need to keep state in the framework side for cases such as authentication. For - * example, when service return a {@link FillResponse} that contains all the fields needed - * to fill the activity but it requires authentication first, that response need to be held - * until the user authenticates or it times out. + * <p>Although the autofill requests and callbacks are stateless from the service's point of view, + * we need to keep state in the framework side for cases such as authentication. For example, when + * service return a {@link FillResponse} that contains all the fields needed to fill the activity + * but it requires authentication first, that response need to be held until the user authenticates + * or it times out. */ -final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener, - AutoFillUI.AutoFillUiCallback, ValueFinder, - RemoteFieldClassificationService.FieldClassificationServiceCallbacks { +final class Session + implements RemoteFillService.FillServiceCallbacks, + ViewState.Listener, + AutoFillUI.AutoFillUiCallback, + ValueFinder, + RemoteFieldClassificationService.FieldClassificationServiceCallbacks { private static final String TAG = "AutofillSession"; // This should never be true in production. This is only for local debugging. @@ -257,6 +256,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final AutofillManagerServiceImpl mService; private final Handler mHandler; private final AutoFillUI mUi; + /** * Context associated with the session, it has the same {@link Context#getDisplayId() displayId} * of the activity being autofilled. @@ -286,14 +286,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Session is destroyed and removed from the manager service. */ public static final int STATE_REMOVED = 3; - @IntDef(prefix = { "STATE_" }, value = { - STATE_UNKNOWN, - STATE_ACTIVE, - STATE_FINISHED, - STATE_REMOVED - }) + @IntDef( + prefix = {"STATE_"}, + value = {STATE_UNKNOWN, STATE_ACTIVE, STATE_FINISHED, STATE_REMOVED}) @Retention(RetentionPolicy.SOURCE) - @interface SessionState{} + @interface SessionState {} @GuardedBy("mLock") private final SessionFlags mSessionFlags; @@ -318,7 +315,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public final int mFlags; @GuardedBy("mLock") - @NonNull private IBinder mActivityToken; + @NonNull + private IBinder mActivityToken; /** The app activity that's being autofilled */ @NonNull private final ComponentName mComponentName; @@ -341,11 +339,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * autofill. */ @GuardedBy("mLock") - @Nullable private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest; + @Nullable + private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest; - /** - * Id of the View currently being displayed. - */ + /** Id of the View currently being displayed. */ @GuardedBy("mLock") private @Nullable AutofillId mCurrentViewId; @@ -363,8 +360,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * * <p>Only {@code null} when the session is for augmented autofill only. */ - @Nullable - private final RemoteFillService mRemoteFillService; + @Nullable private final RemoteFillService mRemoteFillService; /** * With the credman integration, Autofill Framework handles two types of autofill flows - @@ -379,8 +375,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * service the session was initially created with, the secondary provider handler will contain * the remaining autofill service. */ - @Nullable - private final SecondaryProviderHandler mSecondaryProviderHandler; + @Nullable private final SecondaryProviderHandler mSecondaryProviderHandler; @GuardedBy("mLock") private SparseArray<FillResponse> mResponses; @@ -395,9 +390,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private ArrayList<FillContext> mContexts; - /** - * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. - */ + /** Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. */ private boolean mHasCallback; /** Whether the session has credential manager provider as the primary provider. */ @@ -426,32 +419,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private PendingUi mPendingSaveUi; - /** - * List of dataset ids selected by the user. - */ + /** List of dataset ids selected by the user. */ @GuardedBy("mLock") private ArrayList<String> mSelectedDatasetIds; - /** - * When the session started (using elapsed time since boot). - */ + /** When the session started (using elapsed time since boot). */ private final long mStartTime; - /** - * Count of FillRequests in the session. - */ + /** Count of FillRequests in the session. */ private int mRequestCount; /** - * Starting timestamp of latency logger. - * This is set when Session created or when the view is reset. + * Starting timestamp of latency logger. This is set when Session created or when the view is + * reset. */ @GuardedBy("mLock") private long mLatencyBaseTime; - /** - * When the UI was shown for the first time (using elapsed time since boot). - */ + /** When the UI was shown for the first time (using elapsed time since boot). */ @GuardedBy("mLock") private long mUiShownTime; @@ -475,15 +460,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private final LocalLog mWtfHistory; - /** - * Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. - */ + /** Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. */ @GuardedBy("mLock") private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1); - /** - * Destroys the augmented Autofill UI. - */ + /** Destroys the augmented Autofill UI. */ // TODO(b/123099468): this runnable is called when the Autofill session is destroyed, the // main reason being the cases where user tap HOME. // Right now it's completely destroying the UI, but we need to decide whether / how to @@ -494,13 +475,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable private Runnable mAugmentedAutofillDestroyer; - /** - * List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. - */ + /** List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. */ @GuardedBy("mLock") private ArrayList<LogMaker> mAugmentedRequestsLogs; - /** * List of autofill ids of autofillable fields present in the AssistStructure that can be used * to trigger new augmented autofill requests (because the "standard" service was not interested @@ -509,23 +487,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private ArrayList<AutofillId> mAugmentedAutofillableIds; - @NonNull - final AutofillInlineSessionController mInlineSessionController; + @NonNull final AutofillInlineSessionController mInlineSessionController; - /** - * Receiver of assist data from the app's {@link Activity}. - */ + /** Receiver of assist data from the app's {@link Activity}. */ private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl(); - /** - * Receiver of assist data for pcc purpose - */ + /** Receiver of assist data for pcc purpose */ private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl(); private final ClassificationState mClassificationState = new ClassificationState(); - @Nullable - private final ComponentName mCredentialAutofillService; + @Nullable private final ComponentName mCredentialAutofillService; // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a // new one per Session. @@ -547,7 +519,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received"); synchronized (mLock) { int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0); - FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE, android.service.autofill.FillResponse.class); + FillResponse response = + intent.getParcelableExtra( + EXTRA_FILL_RESPONSE, + android.service.autofill.FillResponse.class); mFillRequestEventLogger.maybeSetRequestTriggerReason( TRIGGER_REASON_RETRIGGER); mAssistReceiver.processDelayedFillLocked(requestId, response); @@ -575,28 +550,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private SessionCommittedEventLogger mSessionCommittedEventLogger; - /** - * Fill dialog request would likely be sent slightly later. - */ + /** Fill dialog request would likely be sent slightly later. */ @NonNull @GuardedBy("mLock") private boolean mPreviouslyFillDialogPotentiallyStarted; /** - * Keeps track of if the user entered view, this is used to - * distinguish Fill Request that did not have user interaction - * with ones that did. + * Keeps track of if the user entered view, this is used to distinguish Fill Request that did + * not have user interaction with ones that did. * - * This is set to true when entering view - after FillDialog FillRequest - * or on plain user tap. + * <p>This is set to true when entering view - after FillDialog FillRequest or on plain user + * tap. */ @NonNull @GuardedBy("mLock") private boolean mLogViewEntered; /** - * Keeps the fill dialog trigger ids of the last response. This invalidates - * the trigger ids of the previous response. + * Keeps the fill dialog trigger ids of the last response. This invalidates the trigger ids of + * the previous response. */ @Nullable @GuardedBy("mLock") @@ -662,9 +634,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } - /** - * Collection of flags/booleans that helps determine Session behaviors. - */ + /** Collection of flags/booleans that helps determine Session behaviors. */ private final class SessionFlags { /** Whether autofill is disabled by the service */ private boolean mAutofillDisabled; @@ -695,31 +665,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { @GuardedBy("mLock") private boolean mWaitForInlineRequest; + @GuardedBy("mLock") private InlineSuggestionsRequest mPendingInlineSuggestionsRequest; + @GuardedBy("mLock") private FillRequest mPendingFillRequest; + @GuardedBy("mLock") private FillRequest mLastFillRequest; - @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState, - boolean isInlineRequest) { + @Nullable + Consumer<InlineSuggestionsRequest> newAutofillRequestLocked( + ViewState viewState, boolean isInlineRequest) { mPendingFillRequest = null; mWaitForInlineRequest = isInlineRequest; mPendingInlineSuggestionsRequest = null; if (isInlineRequest) { WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference = - new WeakReference<AssistDataReceiverImpl>(this); + new WeakReference<AssistDataReceiverImpl>(this); WeakReference<ViewState> viewStateWeakReference = - new WeakReference<ViewState>(viewState); - return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference, - viewStateWeakReference); + new WeakReference<ViewState>(viewState); + return new InlineSuggestionRequestConsumer( + assistDataReceiverWeakReference, viewStateWeakReference); } return null; } - void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest, - ViewState viewState) { + void handleInlineSuggestionRequest( + InlineSuggestionsRequest inlineSuggestionsRequest, ViewState viewState) { if (sVerbose) { Slog.v(TAG, "handleInlineSuggestionRequest(): inline suggestion request received"); } @@ -738,8 +712,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void maybeRequestFillLocked() { if (mPendingFillRequest == null) { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request " - + "due to empty pending fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): cancelling calling fill request " + + "due to empty pending fill request"); } return; } @@ -748,27 +724,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mWaitForInlineRequest) { if (mPendingInlineSuggestionsRequest == null) { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request " - + "due to waiting for inline request and pending inline request is " - + "currently empty"); + Slog.v( + TAG, + "maybeRequestFillLocked(): cancelling calling fill request due to" + + " waiting for inline request and pending inline request is" + + " currently empty"); } return; } if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): adding inline request to pending " - + "fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): adding inline request to pending " + + "fill request"); } - mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), - mPendingFillRequest.getFillContexts(), - mPendingFillRequest.getHints(), - mPendingFillRequest.getClientState(), - mPendingFillRequest.getFlags(), - mPendingInlineSuggestionsRequest, - mPendingFillRequest.getDelayedFillIntentSender()); + mPendingFillRequest = + new FillRequest( + mPendingFillRequest.getId(), + mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getHints(), + mPendingFillRequest.getClientState(), + mPendingFillRequest.getFlags(), + mPendingInlineSuggestionsRequest, + mPendingFillRequest.getDelayedFillIntentSender()); } else { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): not adding inline request to pending " - + "fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): not adding inline request to pending " + + "fill request"); } } @@ -780,19 +764,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && mSecondaryProviderHandler != null) { Slog.v(TAG, "Requesting fill response to secondary provider."); if (!mIsPrimaryCredential) { - mPendingFillRequest = addCredentialManagerDataToClientState( - mPendingFillRequest, - mPendingInlineSuggestionsRequest, id); + mPendingFillRequest = + addCredentialManagerDataToClientState( + mPendingFillRequest, mPendingInlineSuggestionsRequest, id); } - mSecondaryProviderHandler.onFillRequest(mPendingFillRequest, - mPendingFillRequest.getFlags(), mClient.asBinder()); + mSecondaryProviderHandler.onFillRequest( + mPendingFillRequest, mPendingFillRequest.getFlags(), mClient.asBinder()); } else if (mRemoteFillService != null) { if (mIsPrimaryCredential) { - mPendingFillRequest = addCredentialManagerDataToClientState( - mPendingFillRequest, - mPendingInlineSuggestionsRequest, id); - mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, - mClient.asBinder()); + mPendingFillRequest = + addCredentialManagerDataToClientState( + mPendingFillRequest, mPendingInlineSuggestionsRequest, id); + mRemoteFillService.onFillCredentialRequest( + mPendingFillRequest, mClient.asBinder()); } else { mRemoteFillService.onFillRequest(mPendingFillRequest); } @@ -813,8 +797,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onHandleAssistData(Bundle resultData) throws RemoteException { if (mRemoteFillService == null) { - wtf(null, "onHandleAssistData() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "onHandleAssistData() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); return; } // Keeps to prevent it is cleared on multiple threads. @@ -824,7 +811,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); + final AssistStructure structure = + resultData.getParcelable( + ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); if (structure == null) { Slog.e(TAG, "No assist structure - app might have crashed providing it"); return; @@ -852,13 +841,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { structure.ensureDataForAutofill(); } catch (RuntimeException e) { - wtf(e, "Exception lazy loading assist structure for %s: %s", - structure.getActivityComponent(), e); + wtf( + e, + "Exception lazy loading assist structure for %s: %s", + structure.getActivityComponent(), + e); return; } - final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, - /* autofillableOnly= */false); + final ArrayList<AutofillId> ids = + Helper.getAutofillIds(structure, /* autofillableOnly= */ false); for (int i = 0; i < ids.size(); i++) { ids.get(i).setSessionId(Session.this.id); } @@ -868,8 +860,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mCompatMode) { // Sanitize URL bar, if needed - final String[] urlBarIds = mService.getUrlBarResourceIdsForCompatMode( - mComponentName.getPackageName()); + final String[] urlBarIds = + mService.getUrlBarResourceIdsForCompatMode( + mComponentName.getPackageName()); if (sDebug) { Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds)); } @@ -878,11 +871,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mUrlBar != null) { final AutofillId urlBarId = mUrlBar.getAutofillId(); if (sDebug) { - Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain " - + mUrlBar.getWebDomain()); + Slog.d( + TAG, + "Setting urlBar as id=" + + urlBarId + + " and domain " + + mUrlBar.getWebDomain()); } - final ViewState viewState = new ViewState(urlBarId, Session.this, - ViewState.STATE_URL_BAR, mIsPrimaryCredential); + final ViewState viewState = + new ViewState( + urlBarId, + Session.this, + ViewState.STATE_URL_BAR, + mIsPrimaryCredential); mViewStates.put(urlBarId, viewState); } } @@ -907,11 +908,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<String> hints = getTypeHintsForProvider(); mDelayedFillPendingIntent = createPendingIntent(requestId); - request = new FillRequest(requestId, contexts, hints, mClientState, flags, - /*inlineSuggestionsRequest=*/ null, - /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null - ? null - : mDelayedFillPendingIntent.getIntentSender()); + request = + new FillRequest( + requestId, + contexts, + hints, + mClientState, + flags, + /* inlineSuggestionsRequest= */ null, + /* delayedFillIntentSender= */ mDelayedFillPendingIntent == null + ? null + : mDelayedFillPendingIntent.getIntentSender()); mPendingFillRequest = request; maybeRequestFillLocked(); @@ -930,39 +937,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void processDelayedFillLocked(int requestId, FillResponse response) { if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) { - Slog.v(TAG, "processDelayedFillLocked: " - + "calling onFillRequestSuccess with new response"); - onFillRequestSuccess(requestId, response, - mService.getServicePackageName(), mLastFillRequest.getFlags()); + Slog.v( + TAG, + "processDelayedFillLocked: " + + "calling onFillRequestSuccess with new response"); + onFillRequestSuccess( + requestId, + response, + mService.getServicePackageName(), + mLastFillRequest.getFlags()); } } } - private FillRequest addCredentialManagerDataToClientState(FillRequest pendingFillRequest, - InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) { + private FillRequest addCredentialManagerDataToClientState( + FillRequest pendingFillRequest, + InlineSuggestionsRequest pendingInlineSuggestionsRequest, + int sessionId) { if (pendingFillRequest.getClientState() == null) { - pendingFillRequest = new FillRequest(pendingFillRequest.getId(), - pendingFillRequest.getFillContexts(), - pendingFillRequest.getHints(), - new Bundle(), - pendingFillRequest.getFlags(), - pendingInlineSuggestionsRequest, - pendingFillRequest.getDelayedFillIntentSender()); + pendingFillRequest = + new FillRequest( + pendingFillRequest.getId(), + pendingFillRequest.getFillContexts(), + pendingFillRequest.getHints(), + new Bundle(), + pendingFillRequest.getFlags(), + pendingInlineSuggestionsRequest, + pendingFillRequest.getDelayedFillIntentSender()); } pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId); pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId()); - ResultReceiver resultReceiver = constructCredentialManagerCallback( - pendingFillRequest.getId()); - pendingFillRequest.getClientState().putParcelable( - CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver); + ResultReceiver resultReceiver = + constructCredentialManagerCallback(pendingFillRequest.getId()); + pendingFillRequest + .getClientState() + .putParcelable(CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver); return pendingFillRequest; } /** - * Get the list of valid autofill hint types from Device flags - * Returns empty list if PCC is off or no types available - */ + * Get the list of valid autofill hint types from Device flags Returns empty list if PCC is off + * or no types available + */ private List<String> getTypeHintsForProvider() { if (!mService.isPccClassificationEnabled()) { return Collections.EMPTY_LIST; @@ -978,9 +995,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return List.of(typeHints.split(PCC_HINTS_DELIMITER)); } - /** - * Assist Data Receiver for PCC - */ + /** Assist Data Receiver for PCC */ private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub { @GuardedBy("mLock") @@ -998,7 +1013,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState new WeakReference<>(Session.this); remoteFieldClassificationService.onFieldClassificationRequest( mClassificationState.mPendingFieldClassificationRequest, - fieldClassificationServiceCallbacksWeakRef); + fieldClassificationServiceCallbacksWeakRef); } mClassificationState.onFieldClassificationRequestSent(); } @@ -1006,26 +1021,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onHandleAssistData(Bundle resultData) throws RemoteException { // TODO: add a check if pcc field classification service is present - final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, - android.app.assist.AssistStructure.class); + final AssistStructure structure = + resultData.getParcelable( + ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); if (structure == null) { - Slog.e(TAG, "No assist structure for pcc detection - " - + "app might have crashed providing it"); + Slog.e( + TAG, + "No assist structure for pcc detection - " + + "app might have crashed providing it"); return; } final Bundle receiverExtras = resultData.getBundle(ASSIST_KEY_RECEIVER_EXTRAS); if (receiverExtras == null) { - Slog.e(TAG, "No receiver extras for pcc detection - " - + "app might have crashed providing it"); + Slog.e( + TAG, + "No receiver extras for pcc detection - " + + "app might have crashed providing it"); return; } final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID); if (sVerbose) { - Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": " - + structure); + Slog.v( + TAG, + "New structure for PCC Detection: requestId " + + requestId + + ": " + + structure); } synchronized (mLock) { @@ -1037,13 +1061,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { structure.ensureDataForAutofill(); } catch (RuntimeException e) { - wtf(e, "Exception lazy loading assist structure for %s: %s", - structure.getActivityComponent(), e); + wtf( + e, + "Exception lazy loading assist structure for %s: %s", + structure.getActivityComponent(), + e); return; } - final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, - /* autofillableOnly= */false); + final ArrayList<AutofillId> ids = + Helper.getAutofillIds(structure, /* autofillableOnly= */ false); for (int i = 0; i < ids.size(); i++) { ids.get(i).setSessionId(Session.this.id); } @@ -1066,13 +1093,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState PendingIntent pendingIntent; final long identity = Binder.clearCallingIdentity(); try { - Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android") - .putExtra(EXTRA_REQUEST_ID, requestId); - pendingIntent = PendingIntent.getBroadcast( - mContext, this.id, intent, - PendingIntent.FLAG_MUTABLE - | PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_CANCEL_CURRENT); + Intent intent = + new Intent(ACTION_DELAYED_FILL) + .setPackage("android") + .putExtra(EXTRA_REQUEST_ID, requestId); + pendingIntent = + PendingIntent.getBroadcast( + mContext, + this.id, + intent, + PendingIntent.FLAG_MUTABLE + | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_CANCEL_CURRENT); } finally { Binder.restoreCallingIdentity(identity); } @@ -1113,9 +1145,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Returns the ids of all entries in {@link #mViewStates} in the same order. - */ + /** Returns the ids of all entries in {@link #mViewStates} in the same order. */ @GuardedBy("mLock") private AutofillId[] getIdsOfAllViewStatesLocked() { final int numViewState = mViewStates.size(); @@ -1164,8 +1194,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, - * or {@code null} when not found on either of them. + * Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, or + * {@code null} when not found on either of them. */ @GuardedBy("mLock") @Nullable @@ -1180,16 +1210,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this); if (previousSessions != null) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): looking on " + previousSessions.size() - + " previous sessions for autofillId " + autofillId); + Slog.d( + TAG, + "findValueLocked(): looking on " + + previousSessions.size() + + " previous sessions for autofillId " + + autofillId); } for (int i = 0; i < previousSessions.size(); i++) { final Session previousSession = previousSessions.get(i); - final AutofillValue previousValue = previousSession - .findValueFromThisSessionOnlyLocked(autofillId); + final AutofillValue previousValue = + previousSession.findValueFromThisSessionOnlyLocked(autofillId); if (previousValue != null) { - return getSanitizedValue(createSanitizers(previousSession.getSaveInfoLocked()), - autofillId, previousValue); + return getSanitizedValue( + createSanitizers(previousSession.getSaveInfoLocked()), + autofillId, + previousValue); } } } @@ -1212,16 +1248,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState AutofillValue candidateSaveValue = state.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): current value for " + autofillId - + " is empty, using candidateSaveValue instead."); + Slog.d( + TAG, + "findValueLocked(): current value for " + + autofillId + + " is empty, using candidateSaveValue instead."); } return candidateSaveValue; } } if (value == null) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): no current value for " + autofillId - + ", checking value from previous fill contexts"); + Slog.d( + TAG, + "findValueLocked(): no current value for " + + autofillId + + ", checking value from previous fill contexts"); value = getValueFromContextsLocked(autofillId); } } @@ -1231,17 +1273,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Updates values of the nodes in the context's structure so that: * - * - proper node is focused - * - autofillValue is sent back to service when it was previously autofilled - * - autofillValue is sent in the view used to force a request + * <p>- proper node is focused - autofillValue is sent back to service when it was previously + * autofilled - autofillValue is sent in the view used to force a request * * @param fillContext The context to be filled * @param flags The flags that started the session */ @GuardedBy("mLock") private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) { - final ViewNode[] nodes = fillContext - .findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); + final ViewNode[] nodes = + fillContext.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); final int numViewState = mViewStates.size(); for (int i = 0; i < numViewState; i++) { @@ -1250,7 +1291,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ViewNode node = nodes[i]; if (node == null) { if (sVerbose) { - Slog.v(TAG, + Slog.v( + TAG, "fillContextWithAllowedValuesLocked(): no node for " + viewState.id); } continue; @@ -1277,14 +1319,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Cancels the last request sent to the {@link #mRemoteFillService}. - */ + /** Cancels the last request sent to the {@link #mRemoteFillService}. */ @GuardedBy("mLock") private void cancelCurrentRequestLocked() { if (mRemoteFillService == null) { - wtf(null, "cancelCurrentRequestLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "cancelCurrentRequestLocked() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); return; } final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); @@ -1318,21 +1361,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private Optional<Integer> requestNewFillResponseLocked( @NonNull ViewState viewState, int newState, int flags) { boolean isSecondary = shouldRequestSecondaryProvider(flags); - final FillResponse existingResponse = isSecondary - ? viewState.getSecondaryResponse() : viewState.getResponse(); + final FillResponse existingResponse = + isSecondary ? viewState.getSecondaryResponse() : viewState.getResponse(); mFillRequestEventLogger.startLogForNewRequest(); mRequestCount++; mFillRequestEventLogger.maybeSetAppPackageUid(uid); mFillRequestEventLogger.maybeSetFlags(mFlags); - if(mPreviouslyFillDialogPotentiallyStarted) { + if (mPreviouslyFillDialogPotentiallyStarted) { mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_PRE_TRIGGER); } else { if ((flags & FLAG_MANUAL_REQUEST) != 0) { mFillRequestEventLogger.maybeSetRequestTriggerReason( TRIGGER_REASON_EXPLICITLY_REQUESTED); } else { - mFillRequestEventLogger.maybeSetRequestTriggerReason( - TRIGGER_REASON_NORMAL_TRIGGER); + mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_NORMAL_TRIGGER); } } if (existingResponse != null) { @@ -1348,9 +1390,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionState = STATE_ACTIVE; if (mSessionFlags.mAugmentedAutofillOnly || mRemoteFillService == null) { if (sVerbose) { - Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead " - + "(mForAugmentedAutofillOnly=" + mSessionFlags.mAugmentedAutofillOnly - + ", flags=" + flags + ")"); + Slog.v( + TAG, + "requestNewFillResponse(): triggering augmented autofill instead " + + "(mForAugmentedAutofillOnly=" + + mSessionFlags.mAugmentedAutofillOnly + + ", flags=" + + flags + + ")"); } mSessionFlags.mAugmentedAutofillOnly = true; mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID); @@ -1365,16 +1412,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Create a metrics log for the request final int ordinal = mRequestLogs.size() + 1; - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal); + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_REQUEST) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal); if (flags != 0) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags); } mRequestLogs.put(requestId, log); if (sVerbose) { - Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId - + ", flags=" + flags); + Slog.v( + TAG, + "Requesting structure for request #" + + ordinal + + " ,requestId=" + + requestId + + ", flags=" + + flags); } boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0; mFillRequestEventLogger.maybeSetRequestId(requestId); @@ -1406,11 +1460,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // is also not focused. final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); - if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null - && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { + if (mSessionFlags.mInlineSupportedByService + && remoteRenderService != null + && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = - mAssistReceiver.newAutofillRequestLocked(viewState, - /* isInlineRequest= */ true); + mAssistReceiver.newAutofillRequestLocked( + viewState, /* isInlineRequest= */ true); if (inlineSuggestionsRequestConsumer != null) { final int requestIdCopy = requestId; final AutofillId focusedId = mCurrentViewId; @@ -1423,8 +1478,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState requestIdCopy, inlineSuggestionsRequestConsumer, focusedId); - RemoteCallback inlineSuggestionRendorInfoCallback = new RemoteCallback( - inlineSuggestionRendorInfoCallbackOnResultListener, mHandler); + RemoteCallback inlineSuggestionRendorInfoCallback = + new RemoteCallback( + inlineSuggestionRendorInfoCallbackOnResultListener, mHandler); remoteRenderService.getInlineSuggestionsRendererInfo( inlineSuggestionRendorInfoCallback); @@ -1465,8 +1521,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); final long identity = Binder.clearCallingIdentity(); try { - if (!ActivityTaskManager.getService().requestAutofillData(mPccAssistReceiver, - receiverExtras, mActivityToken, flags)) { + if (!ActivityTaskManager.getService() + .requestAutofillData( + mPccAssistReceiver, receiverExtras, mActivityToken, flags)) { Slog.w(TAG, "failed to request autofill data for " + mActivityToken); } } finally { @@ -1483,8 +1540,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); final long identity = Binder.clearCallingIdentity(); try { - if (!ActivityTaskManager.getService().requestAutofillData(mAssistReceiver, - receiverExtras, mActivityToken, flags)) { + if (!ActivityTaskManager.getService() + .requestAutofillData( + mAssistReceiver, receiverExtras, mActivityToken, flags)) { Slog.w(TAG, "failed to request autofill data for " + mActivityToken); } } finally { @@ -1495,13 +1553,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui, - @NonNull Context context, @NonNull Handler handler, int userId, @NonNull Object lock, - int sessionId, int taskId, int uid, @NonNull IBinder activityToken, - @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, - @NonNull LocalLog wtfHistory, @Nullable ComponentName serviceComponentName, - @NonNull ComponentName componentName, boolean compatMode, - boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags, + Session( + @NonNull AutofillManagerServiceImpl service, + @NonNull AutoFillUI ui, + @NonNull Context context, + @NonNull Handler handler, + int userId, + @NonNull Object lock, + int sessionId, + int taskId, + int uid, + @NonNull IBinder activityToken, + @NonNull IBinder client, + boolean hasCallback, + @NonNull LocalLog uiLatencyHistory, + @NonNull LocalLog wtfHistory, + @Nullable ComponentName serviceComponentName, + @NonNull ComponentName componentName, + boolean compatMode, + boolean bindInstantServiceAllowed, + boolean forAugmentedAutofillOnly, + int flags, @NonNull InputMethodManagerInternal inputMethodManagerInternal, boolean isPrimaryCredential) { if (sessionId < 0) { @@ -1533,22 +1605,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState primaryServiceComponentName = serviceComponentName; secondaryServiceComponentName = mCredentialAutofillService; } - Slog.v(TAG, "Primary service component name: " + primaryServiceComponentName - + ", secondary service component name: " + secondaryServiceComponentName); - - mRemoteFillService = primaryServiceComponentName == null ? null - : new RemoteFillService(context, primaryServiceComponentName, userId, this, - bindInstantServiceAllowed, mCredentialAutofillService); - mSecondaryProviderHandler = secondaryServiceComponentName == null ? null - : new SecondaryProviderHandler(context, userId, bindInstantServiceAllowed, - this::onSecondaryFillResponse, secondaryServiceComponentName, - mCredentialAutofillService); + Slog.v( + TAG, + "Primary service component name: " + + primaryServiceComponentName + + ", secondary service component name: " + + secondaryServiceComponentName); + + mRemoteFillService = + primaryServiceComponentName == null + ? null + : new RemoteFillService( + context, + primaryServiceComponentName, + userId, + this, + bindInstantServiceAllowed, + mCredentialAutofillService); + mSecondaryProviderHandler = + secondaryServiceComponentName == null + ? null + : new SecondaryProviderHandler( + context, + userId, + bindInstantServiceAllowed, + this::onSecondaryFillResponse, + secondaryServiceComponentName, + mCredentialAutofillService); mActivityToken = activityToken; mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; mWtfHistory = wtfHistory; - int displayId = LocalServices.getService(ActivityTaskManagerInternal.class) - .getDisplayId(activityToken); + int displayId = + LocalServices.getService(ActivityTaskManagerInternal.class) + .getDisplayId(activityToken); mContext = Helper.getDisplayContext(context, displayId); mComponentName = componentName; mCompatMode = compatMode; @@ -1557,8 +1647,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mStartTime = SystemClock.elapsedRealtime(); mLatencyBaseTime = mStartTime; mRequestCount = 0; - mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog( - sessionId, uid, mLatencyBaseTime); + mPresentationStatsEventLogger = + PresentationStatsEventLogger.createPresentationLog( + sessionId, uid, mLatencyBaseTime); mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId); mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId); mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId); @@ -1574,33 +1665,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState setClientLocked(client); } - mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal, - userId, componentName, handler, mLock, - new InlineFillUi.InlineUiEventCallback() { - @Override - public void notifyInlineUiShown(AutofillId autofillId) { - notifyFillUiShown(autofillId); - } + mInlineSessionController = + new AutofillInlineSessionController( + inputMethodManagerInternal, + userId, + componentName, + handler, + mLock, + new InlineFillUi.InlineUiEventCallback() { + @Override + public void notifyInlineUiShown(AutofillId autofillId) { + notifyFillUiShown(autofillId); + } - @Override - public void notifyInlineUiHidden(AutofillId autofillId) { - notifyFillUiHidden(autofillId); - } - }); + @Override + public void notifyInlineUiHidden(AutofillId autofillId) { + notifyFillUiHidden(autofillId); + } + }); - mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); + mMetricsLogger.write( + newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); mLogViewEntered = false; } private ComponentName getCredentialAutofillService(Context context) { ComponentName componentName = null; - String credentialManagerAutofillCompName = context.getResources().getString( - R.string.config_defaultCredentialManagerAutofillService); + String credentialManagerAutofillCompName = + context.getResources() + .getString(R.string.config_defaultCredentialManagerAutofillService); if (credentialManagerAutofillCompName != null && !credentialManagerAutofillCompName.isEmpty()) { - componentName = ComponentName.unflattenFromString( - credentialManagerAutofillCompName); + componentName = ComponentName.unflattenFromString(credentialManagerAutofillCompName); } if (componentName == null) { Slog.w(TAG, "Invalid CredentialAutofillService"); @@ -1614,7 +1711,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @return The activity token */ @GuardedBy("mLock") - @NonNull IBinder getActivityTokenLocked() { + @NonNull + IBinder getActivityTokenLocked() { return mActivityToken; } @@ -1627,8 +1725,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#switchActivity() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#switchActivity() rejected - session: " + + id + + " destroyed"); return; } mActivityToken = newActivity; @@ -1643,17 +1744,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void setClientLocked(@NonNull IBinder client) { unlinkClientVultureLocked(); mClient = IAutoFillManagerClient.Stub.asInterface(client); - mClientVulture = () -> { - synchronized (mLock) { - Slog.d(TAG, "handling death of " + mActivityToken + " when saving=" - + mSessionFlags.mShowingSaveUi); - if (mSessionFlags.mShowingSaveUi) { - mUi.hideFillUi(this); - } else { - mUi.destroyAll(mPendingSaveUi, this, false); - } - } - }; + mClientVulture = + () -> { + synchronized (mLock) { + Slog.d( + TAG, + "handling death of " + + mActivityToken + + " when saving=" + + mSessionFlags.mShowingSaveUi); + if (mSessionFlags.mShowingSaveUi) { + mUi.hideFillUi(this); + } else { + mUi.destroyAll(mPendingSaveUi, this, false); + } + } + }; try { mClient.asBinder().linkToDeath(mClientVulture, 0); } catch (RemoteException e) { @@ -1676,8 +1782,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override @SuppressWarnings("GuardedBy") - public void onFillRequestSuccess(int requestId, @Nullable FillResponse response, - @NonNull String servicePackageName, int requestFlags) { + public void onFillRequestSuccess( + int requestId, + @Nullable FillResponse response, + @NonNull String servicePackageName, + int requestFlags) { final AutofillId[] fieldClassificationIds; final LogMaker requestLog; @@ -1701,8 +1810,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getDetectionPreferenceForLogging()); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillRequestSuccess() rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1713,8 +1825,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // saveUi gets closed, the session will be destroyed and AutofillManager will reset // its state. Processing the fill request will result in a great chance of corrupt // state in Autofill. - Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " - + id + " is showing saveUi"); + Slog.w( + TAG, + "Call to Session#onFillRequestSuccess() rejected - session: " + + id + + " is showing saveUi"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1760,22 +1875,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final long disableDuration = response.getDisableDuration(); final boolean autofillDisabled = disableDuration > 0; if (autofillDisabled) { final int flags = response.getFlags(); final boolean disableActivityOnly = (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0; - notifyDisableAutofillToClient(disableDuration, - disableActivityOnly ? mComponentName : null); + notifyDisableAutofillToClient( + disableDuration, disableActivityOnly ? mComponentName : null); if (disableActivityOnly) { - mService.disableAutofillForActivity(mComponentName, disableDuration, - id, mCompatMode); + mService.disableAutofillForActivity( + mComponentName, disableDuration, id, mCompatMode); } else { - mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration, - id, mCompatMode); + mService.disableAutofillForApp( + mComponentName.getPackageName(), disableDuration, id, mCompatMode); } synchronized (mLock) { @@ -1786,17 +1900,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (triggerAugmentedAutofillLocked(requestFlags) != null) { mSessionFlags.mAugmentedAutofillOnly = true; if (sDebug) { - Slog.d(TAG, "Service disabled autofill for " + mComponentName - + ", but session is kept for augmented autofill only"); + Slog.d( + TAG, + "Service disabled autofill for " + + mComponentName + + ", but session is kept for augmented autofill only"); } return; } } if (sDebug) { - final StringBuilder message = new StringBuilder("Service disabled autofill for ") + final StringBuilder message = + new StringBuilder("Service disabled autofill for ") .append(mComponentName) - .append(": flags=").append(flags) + .append(": flags=") + .append(flags) .append(", duration="); TimeUtils.formatDuration(disableDuration, message); Slog.d(TAG, message.toString()); @@ -1816,8 +1935,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (requestLog != null) { - requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, - response.getDatasets() == null ? 0 : response.getDatasets().size()); + requestLog.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, + response.getDatasets() == null ? 0 : response.getDatasets().size()); if (fieldClassificationIds != null) { requestLog.addTaggedData( MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS, @@ -1840,10 +1960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - @GuardedBy("mLock") - private void processResponseLockedForPcc(@NonNull FillResponse response, - @Nullable Bundle newClientState, int flags) { + private void processResponseLockedForPcc( + @NonNull FillResponse response, @Nullable Bundle newClientState, int flags) { if (DBG) { Slog.d(TAG, "DBG: Initial response: " + response); } @@ -1851,9 +1970,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState response = getEffectiveFillResponse(response); if (isEmptyResponse(response)) { // Treat it as a null response. - processNullResponseLocked( - response != null ? response.getRequestId() : 0, - flags); + processNullResponseLocked(response != null ? response.getRequestId() : 0, flags); return; } if (DBG) { @@ -1870,9 +1987,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return ((response.getDatasets() == null || response.getDatasets().isEmpty()) && response.getAuthentication() == null && (saveInfo == null - || (ArrayUtils.isEmpty(saveInfo.getOptionalIds()) - && ArrayUtils.isEmpty(saveInfo.getRequiredIds()) - && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0))) + || (ArrayUtils.isEmpty(saveInfo.getOptionalIds()) + && ArrayUtils.isEmpty(saveInfo.getRequiredIds()) + && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0))) && (ArrayUtils.isEmpty(response.getFieldClassificationIds()))); } } @@ -1884,10 +2001,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState computeDatasetsForProviderAndUpdateContainer(response, autofillProviderContainer); if (DBG) { - Slog.d(TAG, "DBG: computeDatasetsForProviderAndUpdateContainer: " - + autofillProviderContainer); + Slog.d( + TAG, + "DBG: computeDatasetsForProviderAndUpdateContainer: " + + autofillProviderContainer); } - if (!mService.isPccClassificationEnabled()) { + if (!mService.isPccClassificationEnabled()) { if (sVerbose) { Slog.v(TAG, "PCC classification is disabled"); } @@ -1897,10 +2016,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mClassificationState.mState != ClassificationState.STATE_RESPONSE || mClassificationState.mLastFieldClassificationResponse == null) { if (sVerbose) { - Slog.v(TAG, "PCC classification no last response:" - + (mClassificationState.mLastFieldClassificationResponse == null) - + " ,ineligible state=" - + (mClassificationState.mState != ClassificationState.STATE_RESPONSE)); + Slog.v( + TAG, + "PCC classification no last response:" + + (mClassificationState.mLastFieldClassificationResponse + == null) + + " ,ineligible state=" + + (mClassificationState.mState + != ClassificationState.STATE_RESPONSE)); } return createShallowCopy(response, autofillProviderContainer); } @@ -1966,8 +2089,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( (int) (fillRequestReceivedRelativeTimestamp)); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSecondaryFillResponse() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSecondaryFillResponse() rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1981,7 +2107,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSecondaryResponses = new SparseArray<>(2); } mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse); - setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false, + setViewStatesLocked( + fillResponse, + ViewState.STATE_FILLABLE, + /* clearResponse= */ false, /* isPrimary= */ false); // Updates the UI, if necessary. @@ -1997,16 +2126,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private FillResponse createShallowCopy( FillResponse response, DatasetComputationContainer container) { return FillResponse.shallowCopy( - response, - new ArrayList<>(container.mDatasets), - getEligibleSaveInfo(response)); + response, new ArrayList<>(container.mDatasets), getEligibleSaveInfo(response)); } private SaveInfo getEligibleSaveInfo(FillResponse response) { SaveInfo saveInfo = response.getSaveInfo(); - if (saveInfo == null || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds()) - || !ArrayUtils.isEmpty(saveInfo.getRequiredIds()) - || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) { + if (saveInfo == null + || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds()) + || !ArrayUtils.isEmpty(saveInfo.getRequiredIds()) + || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) { return saveInfo; } synchronized (mLock) { @@ -2019,12 +2147,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ArraySet<AutofillId> ids = new ArraySet<>(); int saveType = saveInfo.getType(); if (saveType == SaveInfo.SAVE_DATA_TYPE_GENERIC) { - for (Set<AutofillId> autofillIds: hintsToAutofillIdMap.values()) { + for (Set<AutofillId> autofillIds : hintsToAutofillIdMap.values()) { ids.addAll(autofillIds); } } else { Set<String> hints = HintsHelper.getHintsForSaveType(saveType); - for (Map.Entry<String, Set<AutofillId>> entry: hintsToAutofillIdMap.entrySet()) { + for (Map.Entry<String, Set<AutofillId>> entry : hintsToAutofillIdMap.entrySet()) { String hint = entry.getKey(); if (hints.contains(hint)) { ids.addAll(entry.getValue()); @@ -2039,9 +2167,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * A private class to hold & compute datasets to be shown - */ + /** A private class to hold & compute datasets to be shown */ private static class DatasetComputationContainer { // List of all autofill ids that have a corresponding datasets Set<AutofillId> mAutofillIds = new LinkedHashSet<>(); @@ -2103,9 +2229,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Computes datasets that are eligible to be shown based on provider detections. - * Datasets are populated in the provided container for them to be later merged with the - * PCC eligible datasets based on preference strategy. + * Computes datasets that are eligible to be shown based on provider detections. Datasets are + * populated in the provided container for them to be later merged with the PCC eligible + * datasets based on preference strategy. + * * @param response * @param container */ @@ -2210,9 +2337,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Computes datasets that are eligible to be shown based on PCC detections. - * Datasets are populated in the provided container for them to be later merged with the - * provider eligible datasets based on preference strategy. + * Computes datasets that are eligible to be shown based on PCC detections. Datasets are + * populated in the provided container for them to be later merged with the provider eligible + * datasets based on preference strategy. + * * @param response * @param container */ @@ -2267,14 +2395,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // For that, there has to be a datatype detected by PCC, and the dataset // for that datatype provided by the provider. AutofillId autofillId = dataset.getFieldIds().get(j); - if (!mClassificationState.mClassificationCombinedHintsMap - .containsKey(autofillId)) { + if (!mClassificationState.mClassificationCombinedHintsMap.containsKey( + autofillId)) { additionalEligibleAutofillIds.add(autofillId); additionalDatasetAutofillIds.add(autofillId); // For each of the field, copy over values. - copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues, - fieldPresentations, fieldDialogPresentations, - fieldInlinePresentations, fieldInlineTooltipPresentations, + copyFieldsFromDataset( + dataset, + j, + autofillId, + fieldIds, + fieldValues, + fieldPresentations, + fieldDialogPresentations, + fieldInlinePresentations, + fieldInlineTooltipPresentations, fieldFilters); } continue; @@ -2292,9 +2427,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState eligibleAutofillIds.add(autofillId); datasetAutofillIds.add(autofillId); // For each of the field, copy over values. - copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues, - fieldPresentations, fieldDialogPresentations, - fieldInlinePresentations, fieldInlineTooltipPresentations, + copyFieldsFromDataset( + dataset, + j, + autofillId, + fieldIds, + fieldValues, + fieldPresentations, + fieldDialogPresentations, + fieldInlinePresentations, + fieldInlineTooltipPresentations, fieldFilters); } } @@ -2364,8 +2506,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState fieldPresentations.add(dataset.getFieldPresentation(index)); fieldDialogPresentations.add(dataset.getFieldDialogPresentation(index)); fieldInlinePresentations.add(dataset.getFieldInlinePresentation(index)); - fieldInlineTooltipPresentations.add( - dataset.getFieldInlineTooltipPresentation(index)); + fieldInlineTooltipPresentations.add(dataset.getFieldInlineTooltipPresentation(index)); fieldFilters.add(dataset.getFilter(index)); } @@ -2395,16 +2536,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState unregisterDelayedFillBroadcastLocked(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId - + ") rejected - session: " + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillRequestFailureOrTimeout(req=" + + requestId + + ") rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; } if (sDebug) { - Slog.d(TAG, "finishing session due to service " - + (timedOut ? "timeout" : "failure")); + Slog.d( + TAG, + "finishing session due to service " + (timedOut ? "timeout" : "failure")); } mService.resetLastResponse(); mLastFillDialogTriggerIds = null; @@ -2418,12 +2565,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int targetSdk = mService.getTargedSdkLocked(); if (targetSdk >= Build.VERSION_CODES.Q) { showMessage = false; - Slog.w(TAG, "onFillRequestFailureOrTimeout(): not showing '" + message - + "' because service's targetting API " + targetSdk); + Slog.w( + TAG, + "onFillRequestFailureOrTimeout(): not showing '" + + message + + "' because service's targeting API " + + targetSdk); } if (message != null) { - requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, - message.length()); + requestLog.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length()); } } @@ -2445,8 +2596,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis(); mFillResponseEventLogger.logAndEndEvent(); } - notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, - /* autofillableIds= */ null); + notifyUnavailableToClient( + AutofillManager.STATE_UNKNOWN_FAILED, /* autofillableIds= */ null); if (showMessage) { getUiForShowing().showError(message, this); } @@ -2455,8 +2606,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onSaveRequestSuccess(@NonNull String servicePackageName, - @Nullable IntentSender intentSender) { + public void onSaveRequestSuccess( + @NonNull String servicePackageName, @Nullable IntentSender intentSender) { synchronized (mLock) { mSessionFlags.mShowingSaveUi = false; // Log onSaveRequest result. @@ -2464,16 +2615,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetLatencySaveFinishMillis(); mSaveEventLogger.logAndEndEvent(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSaveRequestSuccess() rejected - session: " + + id + + " destroyed"); return; } } - LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) - .setType(intentSender == null ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_OPEN); + LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) + .setType( + intentSender == null + ? MetricsEvent.TYPE_SUCCESS + : MetricsEvent.TYPE_OPEN); mMetricsLogger.write(log); - if (intentSender != null) { if (sDebug) Slog.d(TAG, "Starting intent sender on save()"); startIntentSenderAndFinishSession(intentSender); @@ -2485,8 +2642,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onSaveRequestFailure(@Nullable CharSequence message, - @NonNull String servicePackageName) { + public void onSaveRequestFailure( + @Nullable CharSequence message, @NonNull String servicePackageName) { boolean showMessage = !TextUtils.isEmpty(message); synchronized (mLock) { @@ -2495,28 +2652,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetLatencySaveFinishMillis(); mSaveEventLogger.logAndEndEvent(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSaveRequestFailure() rejected - session: " + + id + + " destroyed"); return; } if (showMessage) { final int targetSdk = mService.getTargedSdkLocked(); if (targetSdk >= Build.VERSION_CODES.Q) { showMessage = false; - Slog.w(TAG, "onSaveRequestFailure(): not showing '" + message - + "' because service's targetting API " + targetSdk); + Slog.w( + TAG, + "onSaveRequestFailure(): not showing '" + + message + + "' because service's targeting API " + + targetSdk); } } } final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) - .setType(MetricsEvent.TYPE_FAILURE); + .setType(MetricsEvent.TYPE_FAILURE); if (message != null) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length()); } mMetricsLogger.write(log); - if (showMessage) { getUiForShowing().showError(message, this); } @@ -2525,8 +2688,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse - convertCredentialResponse) { + public void onConvertCredentialRequestSuccess( + @NonNull ConvertCredentialResponse convertCredentialResponse) { Dataset dataset = convertCredentialResponse.getDataset(); Bundle clientState = convertCredentialResponse.getClientState(); if (dataset != null) { @@ -2534,15 +2697,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (clientState != null) { requestId = clientState.getInt(EXTRA_AUTOFILL_REQUEST_ID); } else { - Slog.e(TAG, "onConvertCredentialRequestSuccess(): client state is null, this " - + "would cause loss in logging."); + Slog.e( + TAG, + "onConvertCredentialRequestSuccess(): client state is null, this " + + "would cause loss in logging."); } // TODO: Add autofill related logging; consider whether to log the index - fill(requestId, /* datasetIndex=*/ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET); + fill(requestId, /* datasetIndex= */ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET); } else { // TODO: Add logging to log this error case - Slog.e(TAG, "onConvertCredentialRequestSuccess(): dataset inside response is " - + "null"); + Slog.e( + TAG, + "onConvertCredentialRequestSuccess(): dataset inside response is " + "null"); } } @@ -2550,11 +2716,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Gets the {@link FillContext} for a request. * * @param requestId The id of the request - * * @return The context or {@code null} if there is no context */ @GuardedBy("mLock") - @Nullable private FillContext getFillContextByRequestIdLocked(int requestId) { + @Nullable + private FillContext getFillContextByRequestIdLocked(int requestId) { if (mContexts == null) { return null; } @@ -2582,19 +2748,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override - public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras, - int uiType) { + public void authenticate( + int requestId, int datasetIndex, IntentSender intent, Bundle extras, int uiType) { if (sDebug) { - Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex - + "; intentSender=" + intent); + Slog.d( + TAG, + "authenticate(): requestId=" + + requestId + + "; datasetIdx=" + + datasetIndex + + "; intentSender=" + + intent); } final Intent fillInIntent; synchronized (mLock) { mPresentationStatsEventLogger.maybeSetAuthenticationType( - AUTHENTICATION_TYPE_FULL_AUTHENTICATION); + AUTHENTICATION_TYPE_FULL_AUTHENTICATION); if (mDestroyed) { - Slog.w(TAG, "Call to Session#authenticate() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#authenticate() rejected - session: " + id + " destroyed"); return; } fillInIntent = createAuthFillInIntentLocked(requestId, extras); @@ -2607,10 +2780,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.setAuthenticationSelected(id, mClientState, uiType); final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); - mHandler.sendMessage(obtainMessage( - Session::startAuthentication, - this, authenticationId, intent, fillInIntent, - /* authenticateInline= */ uiType == UI_TYPE_INLINE)); + mHandler.sendMessage( + obtainMessage( + Session::startAuthentication, + this, + authenticationId, + intent, + fillInIntent, + /* authenticateInline= */ uiType == UI_TYPE_INLINE)); } // AutoFillUiCallback @@ -2618,14 +2795,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void fill(int requestId, int datasetIndex, Dataset dataset, int uiType) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#fill() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#fill() rejected - session: " + id + " destroyed"); return; } } - mHandler.sendMessage(obtainMessage( - Session::autoFill, - this, requestId, datasetIndex, dataset, true, uiType)); + mHandler.sendMessage( + obtainMessage( + Session::autoFill, this, requestId, datasetIndex, dataset, true, uiType)); } // AutoFillUiCallback @@ -2633,15 +2809,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void save() { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#save() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#save() rejected - session: " + id + " destroyed"); return; } mSaveEventLogger.maybeSetLatencySaveRequestMillis(); } - mHandler.sendMessage(obtainMessage( - AutofillManagerServiceImpl::handleSessionSave, - mService, this)); + mHandler.sendMessage( + obtainMessage(AutofillManagerServiceImpl::handleSessionSave, mService, this)); } // AutoFillUiCallback @@ -2650,13 +2824,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { mSessionFlags.mShowingSaveUi = false; if (mDestroyed) { - Slog.w(TAG, "Call to Session#cancelSave() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#cancelSave() rejected - session: " + id + " destroyed"); return; } } - mHandler.sendMessage(obtainMessage( - Session::removeFromService, this)); + mHandler.sendMessage(obtainMessage(Session::removeFromService, this)); } // AutofillUiCallback @@ -2692,26 +2866,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override - public void requestShowFillUi(AutofillId id, int width, int height, - IAutofillWindowPresenter presenter) { + public void requestShowFillUi( + AutofillId id, int width, int height, IAutofillWindowPresenter presenter) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#requestShowFillUi() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#requestShowFillUi() rejected - session: " + + id + + " destroyed"); return; } if (id.equals(mCurrentViewId)) { try { final ViewState view = mViewStates.get(id); - mClient.requestShowFillUi(this.id, id, width, height, view.getVirtualBounds(), - presenter); + mClient.requestShowFillUi( + this.id, id, width, height, view.getVirtualBounds(), presenter); } catch (RemoteException e) { Slog.e(TAG, "Error requesting to show fill UI", e); } } else { if (sDebug) { - Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view (" - + mCurrentViewId + ") anymore"); + Slog.d( + TAG, + "Do not show full UI on " + + id + + " as it is not the current view (" + + mCurrentViewId + + ") anymore"); } } } @@ -2722,8 +2904,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#dispatchUnhandledKey() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#dispatchUnhandledKey() rejected - session: " + + id + + " destroyed"); return; } if (id.equals(mCurrentViewId)) { @@ -2733,8 +2918,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.e(TAG, "Error requesting to dispatch unhandled key", e); } } else { - Slog.w(TAG, "Do not dispatch unhandled key on " + id - + " as it is not the current view (" + mCurrentViewId + ") anymore"); + Slog.w( + TAG, + "Do not dispatch unhandled key on " + + id + + " as it is not the current view (" + + mCurrentViewId + + ") anymore"); } } } @@ -2790,17 +2980,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void startIntentSender(IntentSender intentSender, Intent intent) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#startIntentSender() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#startIntentSender() rejected - session: " + + id + + " destroyed"); return; } if (intent == null) { removeFromServiceLocked(); } } - mHandler.sendMessage(obtainMessage( - Session::doStartIntentSender, - this, intentSender, intent)); + mHandler.sendMessage( + obtainMessage(Session::doStartIntentSender, this, intentSender, intent)); } // AutoFillUiCallback @@ -2865,13 +3057,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setAuthenticationResultLocked(Bundle data, int authenticationId) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#setAuthenticationResultLocked() rejected - session: " + + id + + " destroyed"); return; } if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId - + ", data=" + data); + Slog.d( + TAG, + "setAuthenticationResultLocked(): id= " + authenticationId + ", data=" + data); } final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId); if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) { @@ -2890,9 +3086,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState removeFromService(); return; } - final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId) - ? mSecondaryResponses.get(requestId) - : mResponses.get(requestId); + final FillResponse authenticatedResponse = + mRequestId.isSecondaryProvider(requestId) + ? mSecondaryResponses.get(requestId) + : mResponses.get(requestId); if (authenticatedResponse == null || data == null) { Slog.w(TAG, "no authenticated response"); mPresentationStatsEventLogger.maybeSetAuthenticationResult( @@ -2902,8 +3099,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId( - authenticationId); + final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId(authenticationId); Dataset dataset = null; // Authenticated a dataset - reset view state regardless if we got a response or a dataset if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { @@ -2922,33 +3118,45 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags.mExpiredResponse = false; final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT); - final GetCredentialException exception = data.getSerializable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, - GetCredentialException.class); + final GetCredentialException exception = + data.getSerializable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, + GetCredentialException.class); final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE); if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result - + ", clientState=" + newClientState + ", authenticationId=" + authenticationId); - } - if (Flags.autofillCredmanDevIntegration() && exception != null + Slog.d( + TAG, + "setAuthenticationResultLocked(): result=" + + result + + ", clientState=" + + newClientState + + ", authenticationId=" + + authenticationId); + } + if (Flags.autofillCredmanDevIntegration() + && exception != null && !exception.getType().equals(GetCredentialException.TYPE_USER_CANCELED)) { if (dataset != null && dataset.getFieldIds().size() == 1) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): result returns with" - + "Credential Manager Exception"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): result returns with" + + "Credential Manager Exception"); } AutofillId autofillId = dataset.getFieldIds().get(0); - sendCredentialManagerResponseToApp(/*response=*/ null, - (GetCredentialException) exception, autofillId); + sendCredentialManagerResponseToApp( + /* response= */ null, (GetCredentialException) exception, autofillId); } return; } if (result instanceof FillResponse) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from" - + " authentication flow"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): received FillResponse from" + + " authentication flow"); } logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED); mPresentationStatsEventLogger.maybeSetAuthenticationResult( @@ -2963,33 +3171,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (dataset != null && dataset.getFieldIds().size() == 1) { AutofillId autofillId = dataset.getFieldIds().get(0); if (sDebug) { - Slog.d(TAG, "Received GetCredentialResponse from authentication flow," - + "for autofillId: " + autofillId); + Slog.d( + TAG, + "Received GetCredentialResponse from authentication flow," + + "for autofillId: " + + autofillId); } - sendCredentialManagerResponseToApp(response, - /*exception=*/ null, autofillId); + sendCredentialManagerResponseToApp(response, /* exception= */ null, autofillId); } } else if (Flags.autofillCredmanIntegration()) { - Dataset datasetFromCredentialResponse = getDatasetFromCredentialResponse( - (GetCredentialResponse) result); + Dataset datasetFromCredentialResponse = + getDatasetFromCredentialResponse((GetCredentialResponse) result); if (datasetFromCredentialResponse != null) { - autoFill(requestId, datasetIdx, datasetFromCredentialResponse, - false, UI_TYPE_UNKNOWN); + autoFill( + requestId, + datasetIdx, + datasetFromCredentialResponse, + false, + UI_TYPE_UNKNOWN); } } } else if (result instanceof Dataset) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from" - + " authentication flow"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): received Dataset from" + + " authentication flow"); } if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); + logAuthenticationStatusLocked( + requestId, MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_SUCCESS); if (newClientState != null) { - if (sDebug) - Slog.d(TAG, "Updating client state from auth dataset"); + if (sDebug) Slog.d(TAG, "Updating client state from auth dataset"); mClientState = newClientState; } Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result); @@ -2999,10 +3214,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } autoFill(requestId, datasetIdx, datasetFromResult, false, UI_TYPE_UNKNOWN); } else { - Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id " - + authenticationId); - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); + Slog.w( + TAG, + "invalid index (" + + datasetIdx + + ") for authentication id " + + authenticationId); + logAuthenticationStatusLocked( + requestId, MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_FAILURE); } @@ -3010,8 +3229,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (result != null) { Slog.w(TAG, "service returned invalid auth type: " + result); } - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); + logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_FAILURE); processNullResponseLocked(requestId, 0); @@ -3036,8 +3254,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "DBG: authenticated effective response: " + response); } if (response == null || response.getDatasets().size() == 0) { - Log.wtf(TAG, "No datasets in fill response on authentication. response = " - + (response == null ? "null" : response.toString())); + Log.wtf( + TAG, + "No datasets in fill response on authentication. response = " + + (response == null ? "null" : response.toString())); return authenticatedDataset; } List<Dataset> datasets = response.getDatasets(); @@ -3047,8 +3267,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState for (Dataset dataset : datasets) { if (!dataset.getFieldIds().isEmpty()) { for (int i = 0; i < dataset.getFieldIds().size(); i++) { - builder.setField(dataset.getFieldIds().get(i), - new Field.Builder().setValue(dataset.getFieldValues().get(i)) + builder.setField( + dataset.getFieldIds().get(i), + new Field.Builder() + .setValue(dataset.getFieldValues().get(i)) .build()); } } @@ -3063,12 +3285,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Returns whether the dataset returned from the authentication result is ephemeral or not. - * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more - * information. + * Returns whether the dataset returned from the authentication result is ephemeral or not. See + * {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more information. */ - private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset, - @NonNull Bundle authResultData) { + private static boolean isAuthResultDatasetEphemeral( + @Nullable Dataset oldDataset, @NonNull Bundle authResultData) { if (authResultData.containsKey( AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) { return authResultData.getBoolean( @@ -3079,11 +3300,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * A dataset can potentially have multiple fields, and it's possible that some of the fields' - * has inline presentation and some don't. It's also possible that some of the fields' - * inline presentation is pinned and some isn't. So the concept of whether a dataset is - * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a - * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient - * for most of the cases. + * has inline presentation and some don't. It's also possible that some of the fields' inline + * presentation is pinned and some isn't. So the concept of whether a dataset is pinned or not + * is ill-defined. Here we say a dataset is pinned if any of the field has a pinned inline + * presentation in the dataset. It's not ideal but hopefully it is sufficient for most of the + * cases. */ private static boolean isPinnedDataset(@Nullable Dataset dataset) { if (dataset != null && dataset.getFieldIds() != null) { @@ -3100,16 +3321,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) { - final Dataset dataset = (data == null) ? null : - data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT, android.service.autofill.Dataset.class); + final Dataset dataset = + (data == null) + ? null + : data.getParcelable( + AutofillManager.EXTRA_AUTHENTICATION_RESULT, + android.service.autofill.Dataset.class); if (sDebug) { - Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id - + ", authId=" + authId + ", dataset=" + dataset); - } - final AutofillId fieldId = (dataset != null && dataset.getFieldIds().size() == 1) - ? dataset.getFieldIds().get(0) : null; - final AutofillValue value = (dataset != null && dataset.getFieldValues().size() == 1) - ? dataset.getFieldValues().get(0) : null; + Slog.d( + TAG, + "Auth result for augmented autofill: sessionId=" + + id + + ", authId=" + + authId + + ", dataset=" + + dataset); + } + final AutofillId fieldId = + (dataset != null && dataset.getFieldIds().size() == 1) + ? dataset.getFieldIds().get(0) + : null; + final AutofillValue value = + (dataset != null && dataset.getFieldValues().size() == 1) + ? dataset.getFieldValues().get(0) + : null; final ClipData content = (dataset != null) ? dataset.getFieldContent() : null; if (fieldId == null || (value == null && content == null)) { if (sDebug) { @@ -3154,8 +3389,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Fill the value into the field. if (sDebug) { - Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value - + ", content=" + content); + Slog.d( + TAG, + "Filling after auth: fieldId=" + + fieldId + + ", value=" + + value + + ", content=" + + content); } try { if (content != null) { @@ -3164,8 +3405,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues(), true); } } catch (RemoteException e) { - Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value - + ", content=" + content, e); + Slog.w( + TAG, + "Error filling after auth: fieldId=" + + fieldId + + ", value=" + + value + + ", content=" + + content, + e); } // Clear the suggestions since the user already accepted one of them. @@ -3175,8 +3423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setHasCallbackLocked(boolean hasIt) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#setHasCallbackLocked() rejected - session: " + + id + + " destroyed"); return; } mHasCallback = hasIt; @@ -3185,9 +3436,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") @Nullable private FillResponse getLastResponseLocked(@Nullable String logPrefixFmt) { - final String logPrefix = sDebug && logPrefixFmt != null - ? String.format(logPrefixFmt, this.id) - : null; + final String logPrefix = + sDebug && logPrefixFmt != null ? String.format(logPrefixFmt, this.id) : null; if (mContexts == null) { if (logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts"); return null; @@ -3204,16 +3454,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int lastResponseIdx = getLastResponseIndexLocked(); if (lastResponseIdx < 0) { if (logPrefix != null) { - Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses - + ", mViewStates=" + mViewStates); + Slog.w( + TAG, + logPrefix + + ": did not get last response. mResponses=" + + mResponses + + ", mViewStates=" + + mViewStates); } return null; } final FillResponse response = mResponses.valueAt(lastResponseIdx); if (sVerbose && logPrefix != null) { - Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts - + ", mViewStates=" + mViewStates); + Slog.v( + TAG, + logPrefix + + ": mResponses=" + + mResponses + + ", mContexts=" + + mContexts + + ", mViewStates=" + + mViewStates); } return response; } @@ -3232,9 +3494,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Get statistic information of save info in current session. Specifically - * 1. how many save info the current session has. - * 2. How many distinct save data types current session has. + * Get statistic information of save info in current session. Specifically 1. how many save info + * the current session has. 2. How many distinct save data types current session has. * * @return SaveInfoStats returns the above two number in a SaveInfoStats object */ @@ -3255,12 +3516,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ public void logContextCommitted() { if (sVerbose) { - Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN - + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE); - } - mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, - Event.NO_SAVE_UI_REASON_NONE, - COMMIT_REASON_UNKNOWN)); + Slog.v( + TAG, + "logContextCommitted (" + + id + + "): commit_reason:" + + COMMIT_REASON_UNKNOWN + + " no_save_reason:" + + Event.NO_SAVE_UI_REASON_NONE); + } + mHandler.sendMessage( + obtainMessage( + Session::handleLogContextCommitted, + this, + Event.NO_SAVE_UI_REASON_NONE, + COMMIT_REASON_UNKNOWN)); synchronized (mLock) { logAllEventsLocked(COMMIT_REASON_UNKNOWN); } @@ -3274,16 +3544,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @param saveDialogNotShowReason The reason why a save dialog was not shown. * @param commitReason The reason why context is committed. */ - @GuardedBy("mLock") - public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason, - @AutofillCommitReason int commitReason) { + public void logContextCommittedLocked( + @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { if (sVerbose) { - Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason - + " no_save_reason:" + saveDialogNotShowReason); - } - mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, - saveDialogNotShowReason, commitReason)); + Slog.v( + TAG, + "logContextCommittedLocked (" + + id + + "): commit_reason:" + + commitReason + + " no_save_reason:" + + saveDialogNotShowReason); + } + mHandler.sendMessage( + obtainMessage( + Session::handleLogContextCommitted, + this, + saveDialogNotShowReason, + commitReason)); mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); @@ -3295,8 +3574,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); } - private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason, - @AutofillCommitReason int commitReason) { + private void handleLogContextCommitted( + @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { final FillResponse lastResponse; synchronized (mLock) { lastResponse = getLastResponseLocked("logContextCommited(%s)"); @@ -3326,31 +3605,42 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Sets field classification scores if (userData != null && fcStrategy != null) { - logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason, - commitReason); + logFieldClassificationScore( + fcStrategy, userData, saveDialogNotShowReason, commitReason); } else { logContextCommitted(null, null, saveDialogNotShowReason, commitReason); } } - private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds, + private void logContextCommitted( + @Nullable ArrayList<AutofillId> detectedFieldIds, @Nullable ArrayList<FieldClassification> detectedFieldClassifications, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { synchronized (mLock) { - logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, commitReason); + logContextCommittedLocked( + detectedFieldIds, + detectedFieldClassifications, + saveDialogNotShowReason, + commitReason); } } @GuardedBy("mLock") - private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, + private void logContextCommittedLocked( + @Nullable ArrayList<AutofillId> detectedFieldIds, @Nullable ArrayList<FieldClassification> detectedFieldClassifications, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { if (sVerbose) { - Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason - + " no_save_reason:" + saveDialogNotShowReason); + Slog.v( + TAG, + "logContextCommittedLocked (" + + id + + "): commit_reason:" + + commitReason + + " no_save_reason:" + + saveDialogNotShowReason); } final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)"); if (lastResponse == null) return; @@ -3423,8 +3713,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue currentValue = viewState.getCurrentValue(); if (autofilledValue != null && autofilledValue.equals(currentValue)) { if (sDebug) { - Slog.d(TAG, "logContextCommitted(): ignoring changed " + viewState - + " because it has same value that was autofilled"); + Slog.d( + TAG, + "logContextCommitted(): ignoring changed " + + viewState + + " because it has same value that was autofilled"); } continue; } @@ -3442,8 +3735,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue currentValue = viewState.getCurrentValue(); if (currentValue == null) { if (sDebug) { - Slog.d(TAG, "logContextCommitted(): skipping view without current " - + "value ( " + viewState + ")"); + Slog.d( + TAG, + "logContextCommitted(): skipping view without current " + + "value ( " + + viewState + + ")"); } continue; } @@ -3455,7 +3752,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<Dataset> datasets = response.getDatasets(); if (datasets == null || datasets.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "logContextCommitted() no datasets at " + j); + Slog.v(TAG, "logContextCommitted() no datasets at " + j); } } else { for (int k = 0; k < datasets.size(); k++) { @@ -3463,8 +3760,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final String datasetId = dataset.getId(); if (datasetId == null) { if (sVerbose) { - Slog.v(TAG, "logContextCommitted() skipping idless " - + "dataset " + dataset); + Slog.v( + TAG, + "logContextCommitted() skipping idless " + + "dataset " + + dataset); } } else { final ArrayList<AutofillValue> values = @@ -3473,9 +3773,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue candidate = values.get(l); if (currentValue.equals(candidate)) { if (sDebug) { - Slog.d(TAG, "field " + viewState.id + " was " - + "manually filled with value set by " - + "dataset " + datasetId); + Slog.d( + TAG, + "field " + + viewState.id + + " was manually filled with" + + " value set by dataset " + + datasetId); } if (manuallyFilledIds == null) { manuallyFilledIds = new ArrayMap<>(); @@ -3524,10 +3828,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets, - changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, - manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - mComponentName, mCompatMode, saveDialogNotShowReason); + mService.logContextCommittedLocked( + id, + mClientState, + mSelectedDatasetIds, + ignoredDatasets, + changedFieldIds, + changedDatasetIds, + manuallyFilledFieldIds, + manuallyFilledDatasetIds, + detectedFieldIds, + detectedFieldClassifications, + mComponentName, + mCompatMode, + saveDialogNotShowReason); mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason); @@ -3537,7 +3851,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for * {@code fieldId} based on its {@code currentValue} and {@code userData}. */ - private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy, + private void logFieldClassificationScore( + @NonNull FieldClassificationStrategy fcStrategy, @NonNull FieldClassificationUserData userData, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { @@ -3555,16 +3870,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) { final int valuesLength = userValues == null ? -1 : userValues.length; final int idsLength = categoryIds == null ? -1 : categoryIds.length; - Slog.w(TAG, "setScores(): user data mismatch: values.length = " - + valuesLength + ", ids.length = " + idsLength); + Slog.w( + TAG, + "setScores(): user data mismatch: values.length = " + + valuesLength + + ", ids.length = " + + idsLength); return; } final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize(); final ArrayList<AutofillId> detectedFieldIds = new ArrayList<>(maxFieldsSize); - final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>( - maxFieldsSize); + final ArrayList<FieldClassification> detectedFieldClassifications = + new ArrayList<>(maxFieldsSize); final Collection<ViewState> viewStates; synchronized (mLock) { @@ -3583,33 +3902,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Then use the results, asynchronously - final RemoteCallback callback = new RemoteCallback( - new LogFieldClassificationScoreOnResultListener( - this, - saveDialogNotShowReason, - commitReason, - viewsSize, - autofillIds, - userValues, - categoryIds, - detectedFieldIds, - detectedFieldClassifications)); - - fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, - defaultAlgorithm, defaultArgs, algorithms, args); - } - - void handleLogFieldClassificationScore(@Nullable Bundle result, int saveDialogNotShowReason, - int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues, - String[] categoryIds, ArrayList<AutofillId> detectedFieldIds, + final RemoteCallback callback = + new RemoteCallback( + new LogFieldClassificationScoreOnResultListener( + this, + saveDialogNotShowReason, + commitReason, + viewsSize, + autofillIds, + userValues, + categoryIds, + detectedFieldIds, + detectedFieldClassifications)); + + fcStrategy.calculateScores( + callback, + currentValues, + userValues, + categoryIds, + defaultAlgorithm, + defaultArgs, + algorithms, + args); + } + + void handleLogFieldClassificationScore( + @Nullable Bundle result, + int saveDialogNotShowReason, + int commitReason, + int viewsSize, + AutofillId[] autofillIds, + String[] userValues, + String[] categoryIds, + ArrayList<AutofillId> detectedFieldIds, ArrayList<FieldClassification> detectedFieldClassifications) { if (result == null) { if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); logContextCommitted(null, null, saveDialogNotShowReason, commitReason); return; } - final Scores scores = result.getParcelable(EXTRA_SCORES, - android.service.autofill.AutofillFieldClassificationService.Scores.class); + final Scores scores = + result.getParcelable( + EXTRA_SCORES, + android.service.autofill.AutofillFieldClassificationService.Scores.class); if (scores == null) { Slog.w(TAG, "No field classification score on " + result); return; @@ -3633,14 +3968,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final Float currentScore = scoresByField.get(categoryId); if (currentScore != null && currentScore > score) { if (sVerbose) { - Slog.v(TAG, "skipping score " + score - + " because it's less than " + currentScore); + Slog.v( + TAG, + "skipping score " + + score + + " because it's less than " + + currentScore); } continue; } if (sVerbose) { - Slog.v(TAG, "adding score " + score + " at index " + j + " and id " - + autofillId); + Slog.v( + TAG, + "adding score " + + score + + " at index " + + j + + " and id " + + autofillId); } scoresByField.put(categoryId, score); } else if (sVerbose) { @@ -3666,13 +4011,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e); return; } - logContextCommitted(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, commitReason); + logContextCommitted( + detectedFieldIds, + detectedFieldClassifications, + saveDialogNotShowReason, + commitReason); } /** - * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} - * when necessary. + * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} when + * necessary. * * <p>Note: It is necessary to call logContextCommitted() first before calling this method. */ @@ -3689,11 +4037,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull public SaveResult showSaveLocked() { if (mDestroyed) { - Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#showSaveLocked() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_NONE); } mSessionState = STATE_FINISHED; @@ -3705,12 +4056,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ if (mSessionFlags.mScreenHasCredmanField) { if (sVerbose) { - Slog.v(TAG, "Call to Session#showSaveLocked() rejected - " - + "there is credman field in screen"); + Slog.v( + TAG, + "Call to Session#showSaveLocked() rejected - " + + "there is credman field in screen"); } mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NONE); } @@ -3728,7 +4083,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NO_SAVE_INFO); } @@ -3737,7 +4094,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG); } @@ -3774,12 +4133,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Some apps clear the form before navigating to other activities. // If current value is empty, consider fall back to last cached // non-empty result first. - final AutofillValue candidateSaveValue = - viewState.getCandidateSaveValue(); + final AutofillValue candidateSaveValue = viewState.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "current value is empty, using cached last non-empty " - + "value instead"); + Slog.v( + TAG, + "current value is empty, using cached last non-empty " + + "value instead"); } value = candidateSaveValue; } else { @@ -3788,8 +4148,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue initialValue = getValueFromContextsLocked(id); if (initialValue != null) { if (sDebug) { - Slog.d(TAG, "Value of required field " + id + " didn't change; " - + "using initial value (" + initialValue + ") instead"); + Slog.d( + TAG, + "Value of required field " + + id + + " didn't change; " + + "using initial value (" + + initialValue + + ") instead"); } value = initialValue; } else { @@ -3821,8 +4187,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue initialValue = getValueFromContextsLocked(id); if (initialValue != null && initialValue.equals(value)) { if (sDebug) { - Slog.d(TAG, "id " + id + " is part of dataset but initial value " - + "didn't change: " + value); + Slog.d( + TAG, + "id " + + id + + " is part of dataset but initial value " + + "didn't change: " + + value); } changed = false; } else { @@ -3833,8 +4204,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (changed) { if (sDebug) { - Slog.d(TAG, "found a change on required " + id + ": " + filledValue - + " => " + value); + Slog.d( + TAG, + "found a change on required " + + id + + ": " + + filledValue + + " => " + + value); } atLeastOneChanged = true; } @@ -3844,8 +4221,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId[] optionalIds = saveInfo.getOptionalIds(); if (sVerbose) { - Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: " - + (optionalIds != null)); + Slog.v( + TAG, + "allRequiredAreNotEmpty: " + + allRequiredAreNotEmpty + + " hasOptional: " + + (optionalIds != null)); } int saveDialogNotShowReason; if (!allRequiredAreNotEmpty) { @@ -3859,7 +4240,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // - if at least one required id changed but it was not part of a filled dataset, we // need to check if an optional id is part of a filled datased (in which case we show // Update instead of Save) - if (optionalIds!= null && (!atLeastOneChanged || !isUpdate)) { + if (optionalIds != null && (!atLeastOneChanged || !isUpdate)) { // No change on required ids yet, look for changes on optional ids. for (int i = 0; i < optionalIds.length; i++) { final AutofillId id = optionalIds[i]; @@ -3879,8 +4260,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState viewState.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "current value is empty, using cached last " - + "non-empty value instead"); + Slog.v( + TAG, + "current value is empty, using cached last " + + "non-empty value instead"); } currentValue = candidateSaveValue; } @@ -3897,8 +4280,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue filledValue = viewState.getAutofilledValue(); if (value != null && !value.equals(filledValue)) { if (sDebug) { - Slog.d(TAG, "found a change on optional " + id + ": " + filledValue - + " => " + value); + Slog.d( + TAG, + "found a change on optional " + + id + + ": " + + filledValue + + " => " + + value); } if (filledValue != null) { isUpdate = true; @@ -3907,12 +4296,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } atLeastOneChanged = true; } - } else { + } else { // Update current values cache based on initial value final AutofillValue initialValue = getValueFromContextsLocked(id); if (sDebug) { - Slog.d(TAG, "no current value for " + id + "; initial value is " - + initialValue); + Slog.d( + TAG, + "no current value for " + + id + + "; initial value is " + + initialValue); } if (initialValue != null) { currentValues.put(id, initialValue); @@ -3935,17 +4328,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { isValid = validator.isValid(this); if (sDebug) Slog.d(TAG, validator + " returned " + isValid); - log.setType(isValid - ? MetricsEvent.TYPE_SUCCESS - : MetricsEvent.TYPE_DISMISS); + log.setType( + isValid ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_DISMISS); } catch (Exception e) { Slog.e(TAG, "Not showing save UI because validation failed:", e); log.setType(MetricsEvent.TYPE_FAILURE); mMetricsLogger.write(log); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_FIELD_VALIDATION_FAILED); + NO_SAVE_REASON_FIELD_VALIDATION_FAILED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED); } @@ -3953,9 +4347,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (!isValid) { Slog.i(TAG, "not showing save UI because fields failed validation"); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_FIELD_VALIDATION_FAILED); + NO_SAVE_REASON_FIELD_VALIDATION_FAILED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED); } } @@ -3964,15 +4360,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // content. final List<Dataset> datasets = response.getDatasets(); if (datasets != null) { - datasets_loop: for (int i = 0; i < datasets.size(); i++) { + datasets_loop: + for (int i = 0; i < datasets.size(); i++) { final Dataset dataset = datasets.get(i); final ArrayMap<AutofillId, AutofillValue> datasetValues = Helper.getFields(dataset); if (sVerbose) { - Slog.v(TAG, "Checking if saved fields match contents of dataset #" + i - + ": " + dataset + "; savableIds=" + savableIds); + Slog.v( + TAG, + "Checking if saved fields match contents of dataset #" + + i + + ": " + + dataset + + "; savableIds=" + + savableIds); } - savable_ids_loop: for (int j = 0; j < savableIds.size(); j++) { + savable_ids_loop: + for (int j = 0; j < savableIds.size(); j++) { final AutofillId id = savableIds.valueAt(j); final AutofillValue currentValue = currentValues.get(id); if (currentValue == null) { @@ -3984,20 +4388,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue datasetValue = datasetValues.get(id); if (!currentValue.equals(datasetValue)) { if (sDebug) { - Slog.d(TAG, "found a dataset change on id " + id + ": from " - + datasetValue + " to " + currentValue); + Slog.d( + TAG, + "found a dataset change on id " + + id + + ": from " + + datasetValue + + " to " + + currentValue); } continue datasets_loop; } if (sVerbose) Slog.v(TAG, "no dataset changes for id " + id); } if (sDebug) { - Slog.d(TAG, "ignoring Save UI because all fields match contents of " - + "dataset #" + i + ": " + dataset); + Slog.d( + TAG, + "ignoring Save UI because all fields match contents of " + + "dataset #" + + i + + ": " + + dataset); } mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_DATASET_MATCH); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_DATASET_MATCH); } } @@ -4015,13 +4432,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState wtf(null, "showSaveLocked(): no service label or icon"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NONE); } - getUiForShowing().showSaveUi(serviceLabel, serviceIcon, - mService.getServicePackageName(), saveInfo, this, - mComponentName, this, mContext, mPendingSaveUi, isUpdate, mCompatMode, - response.getShowSaveDialogIcon(), mSaveEventLogger); + getUiForShowing() + .showSaveUi( + serviceLabel, + serviceIcon, + mService.getServicePackageName(), + saveInfo, + this, + mComponentName, + this, + mContext, + mPendingSaveUi, + isUpdate, + mCompatMode, + response.getShowSaveDialogIcon(), + mSaveEventLogger); if (client != null) { try { client.setSaveUiState(id, true); @@ -4031,21 +4461,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mSessionFlags.mShowingSaveUi = true; if (sDebug) { - Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for " - + id + "!"); + Slog.d( + TAG, + "Good news, everyone! All checks passed, show save UI for " + id + "!"); } - return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ true, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_NONE); } } // Nothing changed... if (sDebug) { - Slog.d(TAG, "showSaveLocked(" + id +"): with no changes, comes no responsibilities." - + "allRequiredAreNotNull=" + allRequiredAreNotEmpty - + ", atLeastOneChanged=" + atLeastOneChanged); + Slog.d( + TAG, + "showSaveLocked(" + + id + + "): with no changes, comes no responsibilities." + + "allRequiredAreNotNull=" + + allRequiredAreNotEmpty + + ", atLeastOneChanged=" + + atLeastOneChanged); } - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, - saveDialogNotShowReason); + return new SaveResult( + /* logSaveShown= */ false, /* removeSession= */ true, saveDialogNotShowReason); } private void logSaveShown() { @@ -4078,25 +4517,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return sanitized; } - /** - * Returns whether the session is currently showing the save UI - */ + /** Returns whether the session is currently showing the save UI */ @GuardedBy("mLock") boolean isSaveUiShowingLocked() { return mSessionFlags.mShowingSaveUi; } - /** - * Gets the latest non-empty value for the given id in the autofill contexts. - */ + /** Gets the latest non-empty value for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private ViewNode getViewNodeFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null) { return node; } @@ -4104,22 +4539,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - /** - * Gets the latest non-empty value for the given id in the autofill contexts. - */ + /** Gets the latest non-empty value for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null) { final AutofillValue value = node.getAutofillValue(); if (sDebug) { - Slog.d(TAG, "getValueFromContexts(" + this.id + "/" + autofillId + ") at " - + i + ": " + value); + Slog.d( + TAG, + "getValueFromContexts(" + + this.id + + "/" + + autofillId + + ") at " + + i + + ": " + + value); } if (value != null && !value.isEmpty()) { return value; @@ -4129,17 +4570,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - /** - * Gets the latest autofill options for the given id in the autofill contexts. - */ + /** Gets the latest autofill options for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private CharSequence[] getAutofillOptionsFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null && node.getAutofillOptions() != null) { return node.getAutofillOptions(); } @@ -4160,7 +4599,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = mContexts.get(contextNum); final ViewNode[] nodes = - context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); + context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); if (sVerbose) Slog.v(TAG, "updateValuesForSaveLocked(): updating " + context); @@ -4191,8 +4630,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sanitizedValue != null) { node.updateAutofillValue(sanitizedValue); } else if (sDebug) { - Slog.d(TAG, "updateValuesForSaveLocked(): not updating field " + id - + " because it failed sanitization"); + Slog.d( + TAG, + "updateValuesForSaveLocked(): not updating field " + + id + + " because it failed sanitization"); } } @@ -4200,28 +4642,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState context.getStructure().sanitizeForParceling(false); if (sVerbose) { - Slog.v(TAG, "updateValuesForSaveLocked(): dumping structure of " + context - + " before calling service.save()"); + Slog.v( + TAG, + "updateValuesForSaveLocked(): dumping structure of " + + context + + " before calling service.save()"); context.getStructure().dump(false); } } } - /** - * Calls service when user requested save. - */ + /** Calls service when user requested save. */ @GuardedBy("mLock") void callSaveLocked() { if (mDestroyed) { - Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#callSaveLocked() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetIsSaved(false); mSaveEventLogger.logAndEndEvent(); return; } if (mRemoteFillService == null) { - wtf(null, "callSaveLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "callSaveLocked() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); mSaveEventLogger.maybeSetIsSaved(false); mSaveEventLogger.logAndEndEvent(); return; @@ -4241,7 +4688,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Remove pending fill requests as the session is finished. cancelCurrentRequestLocked(); - final ArrayList<FillContext> contexts = mergePreviousSessionLocked( /* forSave= */ true); + final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ true); FieldClassificationResponse fieldClassificationResponse = mClassificationState.mLastFieldClassificationResponse; @@ -4251,8 +4698,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mClientState == null) { mClientState = new Bundle(); } - mClientState.putParcelableArrayList(EXTRA_KEY_DETECTIONS, new ArrayList<>( - fieldClassificationResponse.getClassifications())); + mClientState.putParcelableArrayList( + EXTRA_KEY_DETECTIONS, + new ArrayList<>(fieldClassificationResponse.getClassifications())); } final SaveRequest saveRequest = new SaveRequest(contexts, mClientState, mSelectedDatasetIds); @@ -4270,11 +4718,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * from previous sessions that were asked by the service to be delayed (if any). * * <p>As a side-effect: + * * <ul> - * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- - * {@code null} client state from previous sessions. + * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- {@code + * null} client state from previous sessions. * <li>When {@code forSave} is {@code true}, calls {@link #updateValuesForSaveLocked()} in the - * previous sessions. + * previous sessions. * </ul> */ @NonNull @@ -4283,30 +4732,51 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts; if (previousSessions != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): Merging the content of " - + previousSessions.size() + " sessions for task " + taskId); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): Merging the content of " + + previousSessions.size() + + " sessions for task " + + taskId); } contexts = new ArrayList<>(); for (int i = 0; i < previousSessions.size(); i++) { final Session previousSession = previousSessions.get(i); final ArrayList<FillContext> previousContexts = previousSession.mContexts; if (previousContexts == null) { - Slog.w(TAG, "mergeSessions(" + this.id + "): Not merging null contexts from " - + previousSession.id); + Slog.w( + TAG, + "mergeSessions(" + + this.id + + "): Not merging null contexts from " + + previousSession.id); continue; } if (forSave) { previousSession.updateValuesForSaveLocked(); } if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): adding " + previousContexts.size() - + " context from previous session #" + previousSession.id); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): adding " + + previousContexts.size() + + " context from previous session #" + + previousSession.id); } contexts.addAll(previousContexts); if (mClientState == null && previousSession.mClientState != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): setting client state from " - + "previous session" + previousSession.id); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): setting client state from " + + "previous session" + + previousSession.id); } mClientState = previousSession.mClientState; } @@ -4351,8 +4821,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // If it's not, then check if it should start a partition. if (shouldStartNewPartitionLocked(id, flags)) { if (sDebug) { - Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": " - + viewState.getStateAsString()); + Slog.d( + TAG, + "Starting partition or augmented request for view id " + + id + + ": " + + viewState.getStateAsString()); } // Fix to always let standard autofill start. // Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as @@ -4363,8 +4837,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (sVerbose) { - Slog.v(TAG, "Not starting new partition for view " + id + ": " - + viewState.getStateAsString()); + Slog.v( + TAG, + "Not starting new partition for view " + + id + + ": " + + viewState.getStateAsString()); } return Optional.empty(); } @@ -4373,17 +4851,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Determines if a new partition should be started for an id. * * @param id The id of the view that is entered - * * @return {@code true} if a new partition should be started */ @GuardedBy("mLock") private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) { final ViewState currentView = mViewStates.get(id); - SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags) - ? mSecondaryResponses : mResponses; + SparseArray<FillResponse> responses = + shouldRequestSecondaryProvider(flags) ? mSecondaryResponses : mResponses; if (responses == null) { - return currentView != null && (currentView.getState() - & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0; + return currentView != null + && (currentView.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) + == 0; } if (mSessionFlags.mExpiredResponse) { @@ -4395,8 +4873,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int numResponses = responses.size(); if (numResponses >= AutofillManagerService.getPartitionMaxCount()) { - Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id - + " reached maximum of " + AutofillManagerService.getPartitionMaxCount()); + Slog.e( + TAG, + "Not starting a new partition on " + + id + + " because session " + + this.id + + " reached maximum of " + + AutofillManagerService.getPartitionMaxCount()); return false; } @@ -4437,8 +4921,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } boolean shouldRequestSecondaryProvider(int flags) { - if (!mService.isAutofillCredmanIntegrationEnabled() - || mSecondaryProviderHandler == null) { + if (!mService.isAutofillCredmanIntegrationEnabled() || mSecondaryProviderHandler == null) { return false; } if (mIsPrimaryCredential) { @@ -4452,8 +4935,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // 'Session.this.mLock', which is the same as mLock. @SuppressWarnings("GuardedBy") @GuardedBy("mLock") - void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action, - int flags) { + void updateLocked( + AutofillId id, Rect virtualBounds, AutofillValue value, int action, int flags) { if (mDestroyed) { Slog.w(TAG, "updateLocked(" + id + "): rejected - session: destroyed"); return; @@ -4464,7 +4947,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "updateLocked(" + id + "): Set the response has expired."); } mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists( - NOT_SHOWN_REASON_VIEW_CHANGED); + NOT_SHOWN_REASON_VIEW_CHANGED); mPresentationStatsEventLogger.logAndEndEvent("ACTION_RESPONSE_EXPIRED"); return; } @@ -4474,23 +4957,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) { Slog.v( TAG, - "updateLocked(" + id + "): " - + "id=" + this.id - + ", action=" + actionAsString(action) - + ", flags=" + flags - + ", mCurrentViewId=" + mCurrentViewId - + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse - + ", viewState=" + viewState); + "updateLocked(" + + id + + "): " + + "id=" + + this.id + + ", action=" + + actionAsString(action) + + ", flags=" + + flags + + ", mCurrentViewId=" + + mCurrentViewId + + ", mExpiredResponse=" + + mSessionFlags.mExpiredResponse + + ", viewState=" + + viewState); } if (viewState == null) { - if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED + if (action == ACTION_START_SESSION + || action == ACTION_VALUE_CHANGED || action == ACTION_VIEW_ENTERED) { if (sVerbose) Slog.v(TAG, "Creating viewState for " + id); boolean isIgnored = isIgnoredLocked(id); - viewState = new ViewState(id, this, - isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL, - mIsPrimaryCredential); + viewState = + new ViewState( + id, + this, + isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL, + mIsPrimaryCredential); mViewStates.put(id, viewState); // TODO(b/73648631): for optimization purposes, should also ignore if change is @@ -4521,7 +5016,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags.mScreenHasCredmanField = true; } - switch(action) { + switch (action) { case ACTION_START_SESSION: // View is triggering autofill. mCurrentViewId = viewState.id; @@ -4545,14 +5040,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState case ACTION_VALUE_CHANGED: if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) { // Must cancel the session if the value of the URL bar changed - final String currentUrl = mUrlBar == null ? null - : mUrlBar.getText().toString().trim(); + final String currentUrl = + mUrlBar == null ? null : mUrlBar.getText().toString().trim(); if (currentUrl == null) { // Validation check - shouldn't happen. wtf(null, "URL bar value changed, but current value is null"); return; } - if (value == null || ! value.isText()) { + if (value == null || !value.isText()) { // Validation check - shouldn't happen. wtf(null, "URL bar value changed to null or non-text: %s", value); return; @@ -4567,8 +5062,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // are finished, as the URL bar changed callback is usually called before // the virtual views become invisible. if (sDebug) { - Slog.d(TAG, "Ignoring change on URL because session will finish when " - + "views are gone"); + Slog.d( + TAG, + "Ignoring change on URL because session will finish when " + + "views are gone"); } return; } @@ -4598,8 +5095,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // isSameViewEntered has some limitations, where it isn't considered same view when // autofill suggestions pop up, user selects, and the focus lands back on the view. // isSameViewAgain tries to overcome that situation. - final boolean isSameViewAgain = isSameViewEntered - || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId); + final boolean isSameViewAgain = + isSameViewEntered + || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId); if (mCurrentViewId != null) { mPreviousNonNullEnteredViewId = mCurrentViewId; } @@ -4655,8 +5153,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Trigger augmented autofill if applicable if ((flags & FLAG_MANUAL_REQUEST) == 0) { // Not a manual request - if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains( - id)) { + if (mAugmentedAutofillableIds != null + && mAugmentedAutofillableIds.contains(id)) { // Regular autofill handled the view and returned null response, but it // triggered augmented autofill if (!isSameViewEntered) { @@ -4664,16 +5162,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState triggerAugmentedAutofillLocked(flags); } else { if (sDebug) { - Slog.d(TAG, "skip augmented autofill for same view: " - + "same view entered"); + Slog.d( + TAG, + "skip augmented autofill for same view: " + + "same view entered"); } } return; } else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) { // Regular autofill is disabled. if (sDebug) { - Slog.d(TAG, "skip augmented autofill for same view: " - + "standard autofill disabled."); + Slog.d( + TAG, + "skip augmented autofill for same view: " + + "standard autofill disabled."); } return; } @@ -4717,8 +5219,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // on the IME side if it arrives before the input view is finished on the IME. mInlineSessionController.resetInlineFillUiLocked(); - if ((viewState.getState() & - ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) != 0) { + if ((viewState.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) + != 0) { // View was exited before Inline Request sent back, do not set it to // null yet to let onHandleAssistData finish processing } else { @@ -4728,7 +5230,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // It's not necessary that there's no more presentation for this view. It could // be that the user chose some suggestion, in which case, view exits. mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); + NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); } break; default: @@ -4737,8 +5239,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logPresentationStatsOnViewEnteredLocked(FillResponse response, - boolean isCredmanRequested) { + private void logPresentationStatsOnViewEnteredLocked( + FillResponse response, boolean isCredmanRequested) { mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested); mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( mFieldClassificationIdSnapshot); @@ -4754,16 +5256,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) { - if ((viewState.getState() - & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { + if ((viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { viewState.resetState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL); cancelAugmentedAutofillLocked(); } } - /** - * Checks whether a view should be ignored. - */ + /** Checks whether a view should be ignored. */ @GuardedBy("mLock") private boolean isIgnoredLocked(AutofillId id) { // Always check the latest response only @@ -4782,22 +5281,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && getSaveInfoLocked() != null) { final int length = viewState.getCurrentValue().getTextValue().length(); if (sDebug) { - Slog.d(TAG, "updateLocked(" + id + "): resetting value that was " - + length + " chars long"); - } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); + Slog.d( + TAG, + "updateLocked(" + + id + + "): resetting value that was " + + length + + " chars long"); + } + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); mMetricsLogger.write(log); } } @GuardedBy("mLock") - private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value, - ViewState viewState, int flags) { + private void updateViewStateAndUiOnValueChangedLocked( + AutofillId id, AutofillValue value, ViewState viewState, int flags) { // Cache the last non-empty value for save purpose. Some apps clear the form before // navigating to other activities. - if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty()) - && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText() + if (mIgnoreViewStateResetToEmpty + && (value == null || value.isEmpty()) + && viewState.getCurrentValue() != null + && viewState.getCurrentValue().isText() && viewState.getCurrentValue().getTextValue() != null && viewState.getCurrentValue().getTextValue().length() > 1) { if (sVerbose) { @@ -4875,8 +5382,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * indicate the IME attempting to probe the potentially sensitive content of inline suggestions. */ @GuardedBy("mLock") - private void updateFilteringStateOnValueChangedLocked(@Nullable String newTextValue, - ViewState viewState) { + private void updateFilteringStateOnValueChangedLocked( + @Nullable String newTextValue, ViewState viewState) { if (newTextValue == null) { // Don't just return here, otherwise the IME can circumvent this logic using non-text // values. @@ -4901,18 +5408,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @Override - public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId, - @Nullable AutofillValue value, int flags) { + public void onFillReady( + @NonNull FillResponse response, + @NonNull AutofillId filledId, + @Nullable AutofillValue value, + int flags) { synchronized (mLock) { mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( mFieldClassificationIdSnapshot); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillReady() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillReady() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED); mSaveEventLogger.logAndEndEvent(); mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY); + NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY); mPresentationStatsEventLogger.logAndEndEvent("on fill ready"); return; } @@ -4954,7 +5465,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } else { setFillDialogDisabled(); } - } if (response.supportsInlineSuggestions()) { @@ -4971,10 +5481,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - getUiForShowing().showFillUi(filledId, response, filterText, - mService.getServicePackageName(), mComponentName, - serviceLabel, serviceIcon, this, mContext, id, mCompatMode, - mService.getMaster().getMaxInputLengthForAutofill()); + getUiForShowing() + .showFillUi( + filledId, + response, + filterText, + mService.getServicePackageName(), + mComponentName, + serviceLabel, + serviceIcon, + this, + mContext, + id, + mCompatMode, + mService.getMaster().getMaxInputLengthForAutofill()); synchronized (mLock) { if (mUiShownTime == 0) { @@ -4983,21 +5503,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final long duration = mUiShownTime - mStartTime; if (sDebug) { - final StringBuilder msg = new StringBuilder("1st UI for ") - .append(mActivityToken) - .append(" shown in "); + final StringBuilder msg = + new StringBuilder("1st UI for ") + .append(mActivityToken) + .append(" shown in "); TimeUtils.formatDuration(duration, msg); Slog.d(TAG, msg.toString()); } - final StringBuilder historyLog = new StringBuilder("id=").append(id) - .append(" app=").append(mActivityToken) - .append(" svc=").append(mService.getServicePackageName()) - .append(" latency="); + final StringBuilder historyLog = + new StringBuilder("id=") + .append(id) + .append(" app=") + .append(mActivityToken) + .append(" svc=") + .append(mService.getServicePackageName()) + .append(" latency="); TimeUtils.formatDuration(duration, historyLog); mUiLatencyHistory.log(historyLog.toString()); - addTaggedDataToRequestLogLocked(response.getRequestId(), - MetricsEvent.FIELD_AUTOFILL_DURATION, duration); + addTaggedDataToRequestLogLocked( + response.getRequestId(), MetricsEvent.FIELD_AUTOFILL_DURATION, duration); } } } @@ -5052,8 +5577,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private boolean requestShowFillDialog(FillResponse response, - AutofillId filledId, String filterText, int flags) { + private boolean requestShowFillDialog( + FillResponse response, AutofillId filledId, String filterText, int flags) { if (!isFillDialogUiEnabled()) { // Unsupported fill dialog UI if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled"); @@ -5079,7 +5604,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed."); return false; } - } Drawable serviceIcon = null; @@ -5087,21 +5611,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState serviceIcon = getServiceIcon(response); } - getUiForShowing().showFillDialog(filledId, response, filterText, - mService.getServicePackageName(), mComponentName, serviceIcon, this, - id, mCompatMode, mPresentationStatsEventLogger, mLock); + getUiForShowing() + .showFillDialog( + filledId, + response, + filterText, + mService.getServicePackageName(), + mComponentName, + serviceIcon, + this, + id, + mCompatMode, + mPresentationStatsEventLogger, + mLock); return true; } /** - * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able - * to be fetched, use the default provider icon instead + * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able to + * be fetched, use the default provider icon instead * * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise */ @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's - // actually the same object as mLock. - // TODO: Expose mService.mLock or redesign instead. + // actually the same object as mLock. + // TODO: Expose mService.mLock or redesign instead. @GuardedBy("mLock") private Drawable getServiceIcon(FillResponse response) { Drawable serviceIcon = null; @@ -5130,14 +5664,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Get the custom label that was passed through FillResponse. If the custom label - * wasn't able to be fetched, use the default provider icon instead + * Get the custom label that was passed through FillResponse. If the custom label wasn't able to + * be fetched, use the default provider icon instead * * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise */ @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's - // actually the same object as mLock. - // TODO: Expose mService.mLock or redesign instead. + // actually the same object as mLock. + // TODO: Expose mService.mLock or redesign instead. @GuardedBy("mLock") private CharSequence getServiceLabel(FillResponse response) { CharSequence serviceLabel = null; @@ -5167,11 +5701,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return serviceLabel; } - /** - * Returns whether we made a request to show inline suggestions. - */ - private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response, - @Nullable String filterText) { + /** Returns whether we made a request to show inline suggestions. */ + private boolean requestShowInlineSuggestionsLocked( + @NonNull FillResponse response, @Nullable String filterText) { if (mCurrentViewId == null) { Log.w(TAG, "requestShowInlineSuggestionsLocked(): no view currently focused"); return false; @@ -5199,89 +5731,122 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = - new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, - filterText, remoteRenderService, userId, id); - InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, - new InlineFillUi.InlineSuggestionUiCallback() { - @Override - public void autofill(@NonNull Dataset dataset, int datasetIndex) { - fill(response.getRequestId(), datasetIndex, dataset, UI_TYPE_INLINE); - } + new InlineFillUi.InlineFillUiInfo( + inlineSuggestionsRequest.get(), + focusedId, + filterText, + remoteRenderService, + userId, + id); + InlineFillUi inlineFillUi = + InlineFillUi.forAutofill( + inlineFillUiInfo, + response, + new InlineFillUi.InlineSuggestionUiCallback() { + @Override + public void autofill(@NonNull Dataset dataset, int datasetIndex) { + fill( + response.getRequestId(), + datasetIndex, + dataset, + UI_TYPE_INLINE); + } - @Override - public void authenticate(int requestId, int datasetIndex) { - Session.this.authenticate(response.getRequestId(), datasetIndex, - response.getAuthentication(), response.getClientState(), - UI_TYPE_INLINE); - } + @Override + public void authenticate(int requestId, int datasetIndex) { + Session.this.authenticate( + response.getRequestId(), + datasetIndex, + response.getAuthentication(), + response.getClientState(), + UI_TYPE_INLINE); + } - @Override - public void startIntentSender(@NonNull IntentSender intentSender) { - Session.this.startIntentSender(intentSender, new Intent()); - } + @Override + public void startIntentSender(@NonNull IntentSender intentSender) { + Session.this.startIntentSender(intentSender, new Intent()); + } - @Override - public void onError() { - synchronized (mLock) { - mInlineSessionController.setInlineFillUiLocked( - InlineFillUi.emptyUi(focusedId)); - } - } + @Override + public void onError() { + synchronized (mLock) { + mInlineSessionController.setInlineFillUiLocked( + InlineFillUi.emptyUi(focusedId)); + } + } - @Override - public void onInflate() { - Session.this.onShown(UI_TYPE_INLINE, 1); - } - }, mService.getMaster().getMaxInputLengthForAutofill()); + @Override + public void onInflate() { + Session.this.onShown(UI_TYPE_INLINE, 1); + } + }, + mService.getMaster().getMaxInputLengthForAutofill()); return mInlineSessionController.setInlineFillUiLocked(inlineFillUi); } private ResultReceiver constructCredentialManagerCallback(int requestId) { - final ResultReceiver resultReceiver = new ResultReceiver(mHandler) { - final AutofillId mAutofillId = mCurrentViewId; - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == SUCCESS_CREDMAN_SELECTOR) { - Slog.d(TAG, "onReceiveResult from Credential Manager " - + "bottom sheet with mCurrentViewId: " + mAutofillId); - GetCredentialResponse getCredentialResponse = - resultData.getParcelable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, - GetCredentialResponse.class); - - if (Flags.autofillCredmanDevIntegration()) { - sendCredentialManagerResponseToApp(getCredentialResponse, - /*exception=*/ null, mAutofillId); - } else { - Dataset datasetFromCredential = getDatasetFromCredentialResponse( - getCredentialResponse); - if (datasetFromCredential != null) { - autoFill(requestId, /*datasetIndex=*/-1, - datasetFromCredential, false, - UI_TYPE_CREDMAN_BOTTOM_SHEET); + final ResultReceiver resultReceiver = + new ResultReceiver(mHandler) { + final AutofillId mAutofillId = mCurrentViewId; + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == SUCCESS_CREDMAN_SELECTOR) { + Slog.d( + TAG, + "onReceiveResult from Credential Manager " + + "bottom sheet with mCurrentViewId: " + + mAutofillId); + GetCredentialResponse getCredentialResponse = + resultData.getParcelable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, + GetCredentialResponse.class); + + if (Flags.autofillCredmanDevIntegration()) { + sendCredentialManagerResponseToApp( + getCredentialResponse, /* exception= */ null, mAutofillId); + } else { + Dataset datasetFromCredential = + getDatasetFromCredentialResponse(getCredentialResponse); + if (datasetFromCredential != null) { + autoFill( + requestId, + /* datasetIndex= */ -1, + datasetFromCredential, + false, + UI_TYPE_CREDMAN_BOTTOM_SHEET); + } + } + } else if (resultCode == FAILURE_CREDMAN_SELECTOR) { + String[] exception = + resultData.getStringArray( + CredentialProviderService + .EXTRA_GET_CREDENTIAL_EXCEPTION); + if (exception != null && exception.length >= 2) { + String errType = exception[0]; + String errMsg = exception[1]; + Slog.w( + TAG, + "Credman bottom sheet from pinned " + + "entry failed with: + " + + errType + + " , " + + errMsg); + sendCredentialManagerResponseToApp( + /* response= */ null, + new GetCredentialException(errType, errMsg), + mAutofillId); + } + } else { + Slog.d( + TAG, + "Unknown resultCode from credential " + + "manager bottom sheet: " + + resultCode); } } - } else if (resultCode == FAILURE_CREDMAN_SELECTOR) { - String[] exception = resultData.getStringArray( - CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION); - if (exception != null && exception.length >= 2) { - String errType = exception[0]; - String errMsg = exception[1]; - Slog.w(TAG, "Credman bottom sheet from pinned " - + "entry failed with: + " + errType + " , " - + errMsg); - sendCredentialManagerResponseToApp(/*response=*/ null, - new GetCredentialException(errType, errMsg), - mAutofillId); - } - } else { - Slog.d(TAG, "Unknown resultCode from credential " - + "manager bottom sheet: " + resultCode); - } - } - }; - ResultReceiver ipcFriendlyResultReceiver = - toIpcFriendlyResultReceiver(resultReceiver); + }; + ResultReceiver ipcFriendlyResultReceiver = toIpcFriendlyResultReceiver(resultReceiver); return ipcFriendlyResultReceiver; } @@ -5309,8 +5874,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private void notifyUnavailableToClient(int sessionFinishedState, - @Nullable ArrayList<AutofillId> autofillableIds) { + private void notifyUnavailableToClient( + int sessionFinishedState, @Nullable ArrayList<AutofillId> autofillableIds) { synchronized (mLock) { if (mCurrentViewId == null) return; try { @@ -5374,27 +5939,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (saveInfo.getRequiredIds() != null) { Collections.addAll(trackedViews, saveInfo.getRequiredIds()); mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE); + SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE); } if (saveInfo.getOptionalIds() != null) { Collections.addAll(trackedViews, saveInfo.getOptionalIds()); mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE); + SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE); } } if ((flags & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) { - mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_UNKNOWN); + mSaveEventLogger.maybeSetSaveUiShownReason(SAVE_UI_SHOWN_REASON_UNKNOWN); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG); + NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG); saveOnFinish = false; } } else { flags = 0; - mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_NO_SAVE_INFO); + mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO); saveTriggerId = null; } @@ -5427,21 +5990,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { if (sVerbose) { - Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews - + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId - + " saveOnFinish:" + saveOnFinish + " flags: " + flags - + " hasSaveInfo: " + (saveInfo != null)); - } - mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible, - saveOnFinish, toArray(fillableIds), saveTriggerId, hasAuthentication); + Slog.v( + TAG, + "updateTrackedIdsLocked(): trackedViews: " + + trackedViews + + " fillableIds: " + + fillableIds + + " triggerId: " + + saveTriggerId + + " saveOnFinish:" + + saveOnFinish + + " flags: " + + flags + + " hasSaveInfo: " + + (saveInfo != null)); + } + mClient.setTrackedViews( + id, + toArray(trackedViews), + mSaveOnAllViewsInvisible, + saveOnFinish, + toArray(fillableIds), + saveTriggerId, + hasAuthentication); } catch (RemoteException e) { Slog.w(TAG, "Cannot set tracked ids", e); } } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ @GuardedBy("mLock") void setAutofillFailureLocked(@NonNull List<AutofillId> ids, boolean isRefill) { if (sVerbose && !ids.isEmpty()) { @@ -5464,9 +6041,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetViewFillFailureCounts(ids, isRefill); } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ @GuardedBy("mLock") void setViewAutofilledLocked(@NonNull AutofillId id) { if (sVerbose) { @@ -5478,17 +6053,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeAddSuccessId(id); } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ void setNotifyNotExpiringResponseDuringAuth() { synchronized (mLock) { mPresentationStatsEventLogger.maybeSetNotifyNotExpiringResponseDuringAuth(); } } - /** - * Sets the state of views that failed to autofill. - */ + + /** Sets the state of views that failed to autofill. */ void setLogViewEnteredIgnoredDuringAuth() { synchronized (mLock) { mPresentationStatsEventLogger.notifyViewEnteredIgnoredDuringAuthCount(); @@ -5496,10 +6068,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void replaceResponseLocked(@NonNull FillResponse oldResponse, - @NonNull FillResponse newResponse, @Nullable Bundle newClientState) { + private void replaceResponseLocked( + @NonNull FillResponse oldResponse, + @NonNull FillResponse newResponse, + @Nullable Bundle newClientState) { // Disassociate view states with the old response - setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, /* clearResponse= */ true, + setViewStatesLocked( + oldResponse, + ViewState.STATE_INITIAL, + /* clearResponse= */ true, /* isPrimary= */ true); // Move over the id newResponse.setRequestId(oldResponse.getRequestId()); @@ -5519,7 +6096,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<AutofillId> autofillableIds; if (context != null) { final AssistStructure structure = context.getStructure(); - autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true); + autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */ true); } else { Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId); autofillableIds = null; @@ -5535,8 +6112,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags); if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) { if (sVerbose) { - Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot " - + "be augmented. AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "canceling session " + + id + + " when service returned null and it cannot " + + "be augmented. AutofillableIds: " + + autofillableIds); } // Nothing to be done, but need to notify client. notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds); @@ -5544,15 +6126,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } else { if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null and " - + "augmented service is disabled for password fields. " - + "AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "keeping session " + + id + + " when service returned null and " + + "augmented service is disabled for password fields. " + + "AutofillableIds: " + + autofillableIds); } mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); } else { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null but " - + "it can be augmented. AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "keeping session " + + id + + " when service returned null but " + + "it can be augmented. AutofillableIds: " + + autofillableIds); } } mAugmentedAutofillableIds = autofillableIds; @@ -5567,8 +6159,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Tries to trigger Augmented Autofill when the standard service could not fulfill a request. * - * <p> The request may not have been sent when this method returns as it may be waiting for - * the inline suggestion request asynchronously. + * <p>The request may not have been sent when this method returns as it may be waiting for the + * inline suggestion request asynchronously. * * @return callback to destroy the autofill UI, or {@code null} if not supported. */ @@ -5582,8 +6174,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Check if Smart Suggestions is supported... - final @SmartSuggestionMode int supportedModes = mService - .getSupportedSmartSuggestionModesLocked(); + final @SmartSuggestionMode int supportedModes = + mService.getSupportedSmartSuggestionModesLocked(); if (supportedModes == 0) { if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no supported modes"); return null; @@ -5591,8 +6183,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // ...then if the service is set for the user - final RemoteAugmentedAutofillService remoteService = mService - .getRemoteAugmentedAutofillServiceLocked(); + final RemoteAugmentedAutofillService remoteService = + mService.getRemoteAugmentedAutofillServiceLocked(); if (remoteService == null) { if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user"); return null; @@ -5612,25 +6204,37 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - final boolean isAllowlisted = mService - .isWhitelistedForAugmentedAutofillLocked(mComponentName); + final boolean isAllowlisted = + mService.isWhitelistedForAugmentedAutofillLocked(mComponentName); if (!isAllowlisted) { if (sVerbose) { - Slog.v(TAG, "triggerAugmentedAutofillLocked(): " - + ComponentName.flattenToShortString(mComponentName) + " not whitelisted "); - } - logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - mCurrentViewId, isAllowlisted, /* isInline= */ null); + Slog.v( + TAG, + "triggerAugmentedAutofillLocked(): " + + ComponentName.flattenToShortString(mComponentName) + + " not whitelisted "); + } + logAugmentedAutofillRequestLocked( + mode, + remoteService.getComponentName(), + mCurrentViewId, + isAllowlisted, + /* isInline= */ null); return null; } if (sVerbose) { - Slog.v(TAG, "calling Augmented Autofill Service (" - + ComponentName.flattenToShortString(remoteService.getComponentName()) - + ") on view " + mCurrentViewId + " using suggestion mode " - + getSmartSuggestionModeToString(mode) - + " when server returned null for session " + this.id); + Slog.v( + TAG, + "calling Augmented Autofill Service (" + + ComponentName.flattenToShortString(remoteService.getComponentName()) + + ") on view " + + mCurrentViewId + + " using suggestion mode " + + getSmartSuggestionModeToString(mode) + + " when server returned null for session " + + this.id); } // Log FillRequest for Augmented Autofill. mFillRequestEventLogger.startLogForNewRequest(); @@ -5648,8 +6252,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mAugmentedRequestsLogs == null) { mAugmentedRequestsLogs = new ArrayList<>(); } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_AUGMENTED_REQUEST, - remoteService.getComponentName().getPackageName()); + final LogMaker log = + newLogMaker( + MetricsEvent.AUTOFILL_AUGMENTED_REQUEST, + remoteService.getComponentName().getPackageName()); mAugmentedRequestsLogs.add(log); final AutofillId focusedId = mCurrentViewId; @@ -5715,8 +6321,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } synchronized (session.mLock) { session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked( - mFocusedId, /*requestConsumer=*/ mRequestAugmentedAutofill, - result); + mFocusedId, /* requestConsumer= */ mRequestAugmentedAutofill, result); } } } @@ -5741,19 +6346,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mIsAllowlisted = isAllowlisted; mMode = mode; mCurrentValue = currentValue; - } + @Override public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) { Session session = mSessionWeakRef.get(); - if (logIfSessionNull( - session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) { + if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) { return; } session.onAugmentedAutofillInlineSuggestionAccept( inlineSuggestionsRequest, mFocusedId, mIsAllowlisted, mMode, mCurrentValue); - } } @@ -5770,8 +6373,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public Boolean apply(InlineFillUi inlineFillUi) { Session session = mSessionWeakRef.get(); - if (logIfSessionNull( - session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) { + if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) { return false; } @@ -5801,8 +6403,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * If the session is null or has been destroyed, log the error msg, and return true. - * This is a helper function intended to be called when de-referencing from a weak reference. + * If the session is null or has been destroyed, log the error msg, and return true. This is a + * helper function intended to be called when de-referencing from a weak reference. + * * @param session * @param logPrefix * @return true if the session is null, false otherwise. @@ -5830,15 +6433,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { final RemoteAugmentedAutofillService remoteService = mService.getRemoteAugmentedAutofillServiceLocked(); - logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - focussedId, isAllowlisted, inlineSuggestionsRequest != null); - remoteService.onRequestAutofillLocked(id, mClient, - taskId, mComponentName, mActivityToken, - AutofillId.withoutSession(focussedId), currentValue, + logAugmentedAutofillRequestLocked( + mode, + remoteService.getComponentName(), + focussedId, + isAllowlisted, + inlineSuggestionsRequest != null); + remoteService.onRequestAutofillLocked( + id, + mClient, + taskId, + mComponentName, + mActivityToken, + AutofillId.withoutSession(focussedId), + currentValue, inlineSuggestionsRequest, new AugmentedAutofillInlineSuggestionsResponseCallback(this), new AugmentedAutofillErrorCallback(this), - mService.getRemoteInlineSuggestionRenderServiceLocked(), userId); + mService.getRemoteInlineSuggestionRenderServiceLocked(), + userId); } } @@ -5847,15 +6460,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState cancelAugmentedAutofillLocked(); // Also cancel augmented in IME - mInlineSessionController.setInlineFillUiLocked( - InlineFillUi.emptyUi(mCurrentViewId)); + mInlineSessionController.setInlineFillUiLocked(InlineFillUi.emptyUi(mCurrentViewId)); } } @GuardedBy("mLock") private void cancelAugmentedAutofillLocked() { - final RemoteAugmentedAutofillService remoteService = mService - .getRemoteAugmentedAutofillServiceLocked(); + final RemoteAugmentedAutofillService remoteService = + mService.getRemoteAugmentedAutofillServiceLocked(); if (remoteService == null) { Slog.w(TAG, "cancelAugmentedAutofillLocked(): no service for user"); return; @@ -5865,8 +6477,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void processResponseLocked(@NonNull FillResponse newResponse, - @Nullable Bundle newClientState, int flags) { + private void processResponseLocked( + @NonNull FillResponse newResponse, @Nullable Bundle newClientState, int flags) { // Make sure we are hiding the UI which will be shown // only if handling the current response requires it. mUi.hideAll(this); @@ -5878,9 +6490,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int requestId = newResponse.getRequestId(); if (sVerbose) { - Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId - + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse - + ",newClientState=" + newClientState); + Slog.v( + TAG, + "processResponseLocked(): mCurrentViewId=" + + mCurrentViewId + + ",flags=" + + flags + + ", reqId=" + + requestId + + ", resp=" + + newResponse + + ",newClientState=" + + newClientState); } if (mResponses == null) { @@ -5892,8 +6513,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mResponses.put(requestId, newResponse); mClientState = newClientState != null ? newClientState : newResponse.getClientState(); - boolean webviewRequestedCredman = newClientState != null && newClientState.getBoolean( - WEBVIEW_REQUESTED_CREDENTIAL_KEY, false); + boolean webviewRequestedCredman = + newClientState != null + && newClientState.getBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, false); List<Dataset> datasetList = newResponse.getDatasets(); mPresentationStatsEventLogger.maybeSetWebviewRequestedCredential(webviewRequestedCredman); @@ -5901,7 +6523,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId); mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList); - setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false, + setViewStatesLocked( + newResponse, + ViewState.STATE_FILLABLE, + /* clearResponse= */ false, /* isPrimary= */ true); updateFillDialogTriggerIdsLocked(); updateTrackedIdsLocked(); @@ -5914,12 +6539,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentView.maybeCallOnFillReady(flags); } - /** - * Sets the state of all views in the given response. - */ + /** Sets the state of all views in the given response. */ @GuardedBy("mLock") - private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse, - boolean isPrimary) { + private void setViewStatesLocked( + FillResponse response, int state, boolean clearResponse, boolean isPrimary) { final List<Dataset> datasets = response.getDatasets(); if (datasets != null && !datasets.isEmpty()) { for (int i = 0; i < datasets.size(); i++) { @@ -5964,12 +6587,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Sets the state and response of all views in the given dataset. - */ + /** Sets the state and response of all views in the given dataset. */ @GuardedBy("mLock") - private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset, - int state, boolean clearResponse, boolean isPrimary) { + private void setViewStatesLocked( + @Nullable FillResponse response, + @NonNull Dataset dataset, + int state, + boolean clearResponse, + boolean isPrimary) { final ArrayList<AutofillId> ids = dataset.getFieldIds(); final ArrayList<AutofillValue> values = dataset.getFieldValues(); for (int j = 0; j < ids.size(); j++) { @@ -5989,10 +6614,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state, - @Nullable AutofillValue value) { + private ViewState createOrUpdateViewStateLocked( + @NonNull AutofillId id, int state, @Nullable AutofillValue value) { ViewState viewState = mViewStates.get(id); - if (viewState != null) { + if (viewState != null) { viewState.setState(state); } else { viewState = new ViewState(id, this, state, mIsPrimaryCredential); @@ -6008,22 +6633,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return viewState; } - void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, - int uiType) { + void autoFill( + int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, int uiType) { if (sDebug) { - Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex - + "; dataset=" + dataset); + Slog.d( + TAG, + "autoFill(): requestId=" + + requestId + + "; datasetIdx=" + + datasetIndex + + "; dataset=" + + dataset); } synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#autoFill() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#autoFill() rejected - session: " + id + " destroyed"); return; } // Selected dataset id is logged regardless of authentication result. mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex); mPresentationStatsEventLogger.maybeSetSelectedDatasetPickReason( - dataset.getEligibleReason()); + dataset.getEligibleReason()); // Autofill it directly... if (dataset.getAuthentication() == null) { if (generateEvent) { @@ -6039,10 +6669,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // ...or handle authentication. mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType); mPresentationStatsEventLogger.maybeSetAuthenticationType( - AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); + AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); // does not matter the value of isPrimary because null response won't be overridden. - setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, - /* clearResponse= */ false, /* isPrimary= */ true); + setViewStatesLocked( + null, + dataset, + ViewState.STATE_WAITING_DATASET_AUTH, + /* clearResponse= */ false, + /* isPrimary= */ true); final Intent fillInIntent; if (dataset.getCredentialFillInIntent() != null && Flags.autofillCredmanIntegration()) { Slog.d(TAG, "Setting credential fill intent"); @@ -6055,11 +6689,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState forceRemoveFromServiceLocked(); return; } - final int authenticationId = AutofillManager.makeAuthenticationId(requestId, - datasetIndex); - startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent, - /* authenticateInline= */false); - + final int authenticationId = + AutofillManager.makeAuthenticationId(requestId, datasetIndex); + startAuthentication( + authenticationId, + dataset.getAuthentication(), + fillInIntent, + /* authenticateInline= */ false); } } @@ -6072,13 +6708,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = getFillContextByRequestIdLocked(requestId); if (context == null) { - wtf(null, "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s", - requestId, mContexts); + wtf( + null, + "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s", + requestId, + mContexts); return null; } if (mLastInlineSuggestionsRequest != null && mLastInlineSuggestionsRequest.first == requestId) { - fillInIntent.putExtra(AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST, + fillInIntent.putExtra( + AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST, mLastInlineSuggestionsRequest.second); } fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure()); @@ -6121,12 +6761,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private void startAuthentication(int authenticationId, IntentSender intent, - Intent fillInIntent, boolean authenticateInline) { + private void startAuthentication( + int authenticationId, + IntentSender intent, + Intent fillInIntent, + boolean authenticateInline) { try { synchronized (mLock) { - mClient.authenticate(id, authenticationId, intent, fillInIntent, - authenticateInline); + mClient.authenticate( + id, authenticationId, intent, fillInIntent, authenticateInline); } } catch (RemoteException e) { Slog.e(TAG, "Error launching auth intent", e); @@ -6139,22 +6782,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @hide */ static final class SaveResult { - /** - * Whether to record the save dialog has been shown. - */ + /** Whether to record the save dialog has been shown. */ private boolean mLogSaveShown; - /** - * Whether to remove the session. - */ + /** Whether to remove the session. */ private boolean mRemoveSession; - /** - * The reason why a save dialog was not shown. - */ + /** The reason why a save dialog was not shown. */ @NoSaveReason private int mSaveDialogNotShowReason; - SaveResult(boolean logSaveShown, boolean removeSession, + SaveResult( + boolean logSaveShown, + boolean removeSession, @NoSaveReason int saveDialogNotShowReason) { mLogSaveShown = logSaveShown; mRemoveSession = removeSession; @@ -6218,15 +6857,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { - return "SaveResult: [logSaveShown=" + mLogSaveShown - + ", removeSession=" + mRemoveSession - + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]"; + return "SaveResult: [logSaveShown=" + + mLogSaveShown + + ", removeSession=" + + mRemoveSession + + ", saveDialogNotShowReason=" + + mSaveDialogNotShowReason + + "]"; } } /** - * Class maintaining the state of the requests to - * {@link android.service.assist.classification.FieldClassificationService}. + * Class maintaining the state of the requests to {@link + * android.service.assist.classification.FieldClassificationService}. */ private static final class ClassificationState { @@ -6234,18 +6877,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Initial state indicating that the request for classification hasn't been triggered yet. */ private static final int STATE_INITIAL = 1; - /** - * Assist request has been triggered, but awaiting response. - */ + + /** Assist request has been triggered, but awaiting response. */ private static final int STATE_PENDING_ASSIST_REQUEST = 2; - /** - * Classification request has been triggered, but awaiting response. - */ + + /** Classification request has been triggered, but awaiting response. */ private static final int STATE_PENDING_REQUEST = 3; - /** - * Classification response has been received. - */ + + /** Classification response has been received. */ private static final int STATE_RESPONSE = 4; + /** * Classification state has been invalidated, and the last response may no longer be valid. * This could occur due to various reasons like views changing their layouts, becoming @@ -6254,15 +6895,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private static final int STATE_INVALIDATED = 5; - @IntDef(prefix = { "STATE_" }, value = { - STATE_INITIAL, - STATE_PENDING_ASSIST_REQUEST, - STATE_PENDING_REQUEST, - STATE_RESPONSE, - STATE_INVALIDATED - }) + @IntDef( + prefix = {"STATE_"}, + value = { + STATE_INITIAL, + STATE_PENDING_ASSIST_REQUEST, + STATE_PENDING_REQUEST, + STATE_RESPONSE, + STATE_INVALIDATED + }) @Retention(RetentionPolicy.SOURCE) - @interface ClassificationRequestState{} + @interface ClassificationRequestState {} @GuardedBy("mLock") private @ClassificationRequestState int mState = STATE_INITIAL; @@ -6284,8 +6927,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Typically, there would be a 1:1 mapping. However, in certain cases, we may have a hint - * being applicable to many types. An example of this being new/change password forms, - * where you need to confirm the passward twice. + * being applicable to many types. An example of this being new/change password forms, where + * you need to confirm the passward twice. */ @GuardedBy("mLock") private ArrayMap<String, Set<AutofillId>> mHintsToAutofillIdMap; @@ -6317,8 +6960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Process the response received. + * * @return true if the response was processed, false otherwise. If there wasn't any - * response, yet this function was called, it would return false. + * response, yet this function was called, it would return false. */ @GuardedBy("mLock") private boolean processResponse() { @@ -6358,7 +7002,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private static void processDetections(Set<String> detections, AutofillId id, + private static void processDetections( + Set<String> detections, + AutofillId id, ArrayMap<String, Set<AutofillId>> currentMap) { for (String detection : detections) { Set<AutofillId> autofillIds; @@ -6416,37 +7062,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { return "ClassificationState: [" - + "state=" + stateToString() - + ", mPendingFieldClassificationRequest=" + mPendingFieldClassificationRequest - + ", mLastFieldClassificationResponse=" + mLastFieldClassificationResponse - + ", mClassificationHintsMap=" + mClassificationHintsMap - + ", mClassificationGroupHintsMap=" + mClassificationGroupHintsMap - + ", mHintsToAutofillIdMap=" + mHintsToAutofillIdMap - + ", mGroupHintsToAutofillIdMap=" + mGroupHintsToAutofillIdMap + + "state=" + + stateToString() + + ", mPendingFieldClassificationRequest=" + + mPendingFieldClassificationRequest + + ", mLastFieldClassificationResponse=" + + mLastFieldClassificationResponse + + ", mClassificationHintsMap=" + + mClassificationHintsMap + + ", mClassificationGroupHintsMap=" + + mClassificationGroupHintsMap + + ", mHintsToAutofillIdMap=" + + mHintsToAutofillIdMap + + ", mGroupHintsToAutofillIdMap=" + + mGroupHintsToAutofillIdMap + "]"; } - } @Override public String toString() { - return "Session: [id=" + id + ", component=" + mComponentName - + ", state=" + sessionStateAsString(mSessionState) + "]"; + return "Session: [id=" + + id + + ", component=" + + mComponentName + + ", state=" + + sessionStateAsString(mSessionState) + + "]"; } @GuardedBy("mLock") void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; - pw.print(prefix); pw.print("id: "); pw.println(id); - pw.print(prefix); pw.print("uid: "); pw.println(uid); - pw.print(prefix); pw.print("taskId: "); pw.println(taskId); - pw.print(prefix); pw.print("flags: "); pw.println(mFlags); - pw.print(prefix); pw.print("displayId: "); pw.println(mContext.getDisplayId()); - pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState)); - pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName); - pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); - pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); - pw.print(prefix); pw.print("Time to show UI: "); + pw.print(prefix); + pw.print("id: "); + pw.println(id); + pw.print(prefix); + pw.print("uid: "); + pw.println(uid); + pw.print(prefix); + pw.print("taskId: "); + pw.println(taskId); + pw.print(prefix); + pw.print("flags: "); + pw.println(mFlags); + pw.print(prefix); + pw.print("displayId: "); + pw.println(mContext.getDisplayId()); + pw.print(prefix); + pw.print("state: "); + pw.println(sessionStateAsString(mSessionState)); + pw.print(prefix); + pw.print("mComponentName: "); + pw.println(mComponentName); + pw.print(prefix); + pw.print("mActivityToken: "); + pw.println(mActivityToken); + pw.print(prefix); + pw.print("mStartTime: "); + pw.println(mStartTime); + pw.print(prefix); + pw.print("Time to show UI: "); if (mUiShownTime == 0) { pw.println("N/A"); } else { @@ -6454,41 +7130,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.println(); } final int requestLogsSizes = mRequestLogs.size(); - pw.print(prefix); pw.print("mSessionLogs: "); pw.println(requestLogsSizes); + pw.print(prefix); + pw.print("mSessionLogs: "); + pw.println(requestLogsSizes); for (int i = 0; i < requestLogsSizes; i++) { final int requestId = mRequestLogs.keyAt(i); final LogMaker log = mRequestLogs.valueAt(i); - pw.print(prefix2); pw.print('#'); pw.print(i); pw.print(": req="); - pw.print(requestId); pw.print(", log=" ); dumpRequestLog(pw, log); pw.println(); + pw.print(prefix2); + pw.print('#'); + pw.print(i); + pw.print(": req="); + pw.print(requestId); + pw.print(", log="); + dumpRequestLog(pw, log); + pw.println(); } - pw.print(prefix); pw.print("mResponses: "); + pw.print(prefix); + pw.print("mResponses: "); if (mResponses == null) { pw.println("null"); } else { pw.println(mResponses.size()); for (int i = 0; i < mResponses.size(); i++) { - pw.print(prefix2); pw.print('#'); pw.print(i); - pw.print(' '); pw.println(mResponses.valueAt(i)); - } - } - pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId); - pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed); - pw.print(prefix); pw.print("mShowingSaveUi: "); pw.println(mSessionFlags.mShowingSaveUi); - pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi); + pw.print(prefix2); + pw.print('#'); + pw.print(i); + pw.print(' '); + pw.println(mResponses.valueAt(i)); + } + } + pw.print(prefix); + pw.print("mCurrentViewId: "); + pw.println(mCurrentViewId); + pw.print(prefix); + pw.print("mDestroyed: "); + pw.println(mDestroyed); + pw.print(prefix); + pw.print("mShowingSaveUi: "); + pw.println(mSessionFlags.mShowingSaveUi); + pw.print(prefix); + pw.print("mPendingSaveUi: "); + pw.println(mPendingSaveUi); final int numberViews = mViewStates.size(); - pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size()); + pw.print(prefix); + pw.print("mViewStates size: "); + pw.println(mViewStates.size()); for (int i = 0; i < numberViews; i++) { - pw.print(prefix); pw.print("ViewState at #"); pw.println(i); + pw.print(prefix); + pw.print("ViewState at #"); + pw.println(i); mViewStates.valueAt(i).dump(prefix2, pw); } - pw.print(prefix); pw.print("mContexts: " ); + pw.print(prefix); + pw.print("mContexts: "); if (mContexts != null) { int numContexts = mContexts.size(); for (int i = 0; i < numContexts; i++) { FillContext context = mContexts.get(i); - pw.print(prefix2); pw.print(context); + pw.print(prefix2); + pw.print(context); if (sVerbose) { pw.println("AssistStructure dumped at logcat)"); @@ -6500,43 +7202,62 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.println("null"); } - pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); + pw.print(prefix); + pw.print("mHasCallback: "); + pw.println(mHasCallback); if (mClientState != null) { - pw.print(prefix); pw.print("mClientState: "); pw.print(mClientState.getSize()); pw - .println(" bytes"); - } - pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode); - pw.print(prefix); pw.print("mUrlBar: "); + pw.print(prefix); + pw.print("mClientState: "); + pw.print(mClientState.getSize()); + pw.println(" bytes"); + } + pw.print(prefix); + pw.print("mCompatMode: "); + pw.println(mCompatMode); + pw.print(prefix); + pw.print("mUrlBar: "); if (mUrlBar == null) { pw.println("N/A"); } else { - pw.print("id="); pw.print(mUrlBar.getAutofillId()); - pw.print(" domain="); pw.print(mUrlBar.getWebDomain()); - pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText()); - } - pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println( - mSaveOnAllViewsInvisible); - pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds); + pw.print("id="); + pw.print(mUrlBar.getAutofillId()); + pw.print(" domain="); + pw.print(mUrlBar.getWebDomain()); + pw.print(" text="); + Helper.printlnRedactedText(pw, mUrlBar.getText()); + } + pw.print(prefix); + pw.print("mSaveOnAllViewsInvisible: "); + pw.println(mSaveOnAllViewsInvisible); + pw.print(prefix); + pw.print("mSelectedDatasetIds: "); + pw.println(mSelectedDatasetIds); if (mSessionFlags.mAugmentedAutofillOnly) { - pw.print(prefix); pw.println("For Augmented Autofill Only"); + pw.print(prefix); + pw.println("For Augmented Autofill Only"); } if (mSessionFlags.mFillDialogDisabled) { - pw.print(prefix); pw.println("Fill Dialog disabled"); + pw.print(prefix); + pw.println("Fill Dialog disabled"); } if (mLastFillDialogTriggerIds != null) { - pw.print(prefix); pw.println("Last Fill Dialog trigger ids: "); + pw.print(prefix); + pw.println("Last Fill Dialog trigger ids: "); pw.println(mSelectedDatasetIds); } if (mAugmentedAutofillDestroyer != null) { - pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer"); + pw.print(prefix); + pw.println("has mAugmentedAutofillDestroyer"); } if (mAugmentedRequestsLogs != null) { - pw.print(prefix); pw.print("number augmented requests: "); + pw.print(prefix); + pw.print("number augmented requests: "); pw.println(mAugmentedRequestsLogs.size()); } if (mAugmentedAutofillableIds != null) { - pw.print(prefix); pw.print("mAugmentedAutofillableIds: "); + pw.print(prefix); + pw.print("mAugmentedAutofillableIds: "); pw.println(mAugmentedAutofillableIds); } if (mRemoteFillService != null) { @@ -6545,21 +7266,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private static void dumpRequestLog(@NonNull PrintWriter pw, @NonNull LogMaker log) { - pw.print("CAT="); pw.print(log.getCategory()); + pw.print("CAT="); + pw.print(log.getCategory()); pw.print(", TYPE="); final int type = log.getType(); switch (type) { - case MetricsEvent.TYPE_SUCCESS: pw.print("SUCCESS"); break; - case MetricsEvent.TYPE_FAILURE: pw.print("FAILURE"); break; - case MetricsEvent.TYPE_CLOSE: pw.print("CLOSE"); break; - default: pw.print("UNSUPPORTED"); - } - pw.print('('); pw.print(type); pw.print(')'); - pw.print(", PKG="); pw.print(log.getPackageName()); - pw.print(", SERVICE="); pw.print(log - .getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE)); - pw.print(", ORDINAL="); pw.print(log - .getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL)); + case MetricsEvent.TYPE_SUCCESS: + pw.print("SUCCESS"); + break; + case MetricsEvent.TYPE_FAILURE: + pw.print("FAILURE"); + break; + case MetricsEvent.TYPE_CLOSE: + pw.print("CLOSE"); + break; + default: + pw.print("UNSUPPORTED"); + } + pw.print('('); + pw.print(type); + pw.print(')'); + pw.print(", PKG="); + pw.print(log.getPackageName()); + pw.print(", SERVICE="); + pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE)); + pw.print(", ORDINAL="); + pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL)); dumpNumericValue(pw, log, "FLAGS", MetricsEvent.FIELD_AUTOFILL_FLAGS); dumpNumericValue(pw, log, "NUM_DATASETS", MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS); dumpNumericValue(pw, log, "UI_LATENCY", MetricsEvent.FIELD_AUTOFILL_DURATION); @@ -6569,64 +7301,86 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(", AUTH_STATUS="); switch (authStatus) { case MetricsEvent.AUTOFILL_AUTHENTICATED: - pw.print("AUTHENTICATED"); break; + pw.print("AUTHENTICATED"); + break; case MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED: - pw.print("DATASET_AUTHENTICATED"); break; + pw.print("DATASET_AUTHENTICATED"); + break; case MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION: - pw.print("INVALID_AUTHENTICATION"); break; + pw.print("INVALID_AUTHENTICATION"); + break; case MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION: - pw.print("INVALID_DATASET_AUTHENTICATION"); break; - default: pw.print("UNSUPPORTED"); + pw.print("INVALID_DATASET_AUTHENTICATION"); + break; + default: + pw.print("UNSUPPORTED"); } - pw.print('('); pw.print(authStatus); pw.print(')'); + pw.print('('); + pw.print(authStatus); + pw.print(')'); } - dumpNumericValue(pw, log, "FC_IDS", - MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS); - dumpNumericValue(pw, log, "COMPAT_MODE", - MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE); + dumpNumericValue( + pw, log, "FC_IDS", MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS); + dumpNumericValue(pw, log, "COMPAT_MODE", MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE); } - private static void dumpNumericValue(@NonNull PrintWriter pw, @NonNull LogMaker log, - @NonNull String field, int tag) { + private static void dumpNumericValue( + @NonNull PrintWriter pw, @NonNull LogMaker log, @NonNull String field, int tag) { final int value = getNumericValue(log, tag); if (value != 0) { - pw.print(", "); pw.print(field); pw.print('='); pw.print(value); + pw.print(", "); + pw.print(field); + pw.print('='); + pw.print(value); } } - void sendCredentialManagerResponseToApp(@Nullable GetCredentialResponse response, - @Nullable GetCredentialException exception, @NonNull AutofillId viewId) { + void sendCredentialManagerResponseToApp( + @Nullable GetCredentialResponse response, + @Nullable GetCredentialException exception, + @NonNull AutofillId viewId) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#sendCredentialManagerResponseToApp() rejected " - + "- session: " + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#sendCredentialManagerResponseToApp() rejected " + + "- session: " + + id + + " destroyed"); return; } try { final ViewState viewState = mViewStates.get(viewId); if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly() - && viewState != null && viewState.id.getSessionId() != id) { + && viewState != null + && viewState.id.getSessionId() != id) { if (sVerbose) { - Slog.v(TAG, "Skipping sending credential response to view: " - + viewId + " as it isn't part of the current session: " + id); + Slog.v( + TAG, + "Skipping sending credential response to view: " + + viewId + + " as it isn't part of the current session: " + + id); } } if (exception != null) { if (viewId.isVirtualInt()) { - sendResponseToViewNode(viewId, /*response=*/ null, exception); + sendResponseToViewNode(viewId, /* response= */ null, exception); } else { - mClient.onGetCredentialException(id, viewId, exception.getType(), - exception.getMessage()); + mClient.onGetCredentialException( + id, viewId, exception.getType(), exception.getMessage()); } } else if (response != null) { if (viewId.isVirtualInt()) { - sendResponseToViewNode(viewId, response, /*exception=*/ null); + sendResponseToViewNode(viewId, response, /* exception= */ null); } else { mClient.onGetCredentialResponse(id, viewId, response); } } else { - Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response" - + "and exception"); + Slog.w( + TAG, + "sendCredentialManagerResponseToApp called with null response" + + "and exception"); } } catch (RemoteException e) { Slog.w(TAG, "Error sending credential response to activity: " + e); @@ -6635,23 +7389,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void sendResponseToViewNode(AutofillId viewId, GetCredentialResponse response, - GetCredentialException exception) { + private void sendResponseToViewNode( + AutofillId viewId, GetCredentialResponse response, GetCredentialException exception) { ViewNode viewNode = getViewNodeFromContextsLocked(viewId); if (viewNode != null && viewNode.getPendingCredentialCallback() != null) { Bundle resultData = new Bundle(); if (response != null) { resultData.putParcelable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, - response); - viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, - resultData); + CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response); + viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, resultData); } else if (exception != null) { resultData.putStringArray( CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, new String[] {exception.getType(), exception.getMessage()}); - viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, - resultData); + viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, resultData); } } else { Slog.w(TAG, "View node not found after GetCredentialResponse"); @@ -6661,8 +7412,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void autoFillApp(Dataset dataset) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#autoFillApp() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#autoFillApp() rejected - session: " + id + " destroyed"); return; } try { @@ -6671,8 +7423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<AutofillId> ids = new ArrayList<>(entryCount); final List<AutofillValue> values = new ArrayList<>(entryCount); boolean waitingDatasetAuth = false; - boolean hideHighlight = (entryCount == 1 - && dataset.getFieldIds().get(0).equals(mCurrentViewId)); + boolean hideHighlight = + highlightAutofillSingleField() + ? false + : (entryCount == 1 + && dataset.getFieldIds().get(0).equals(mCurrentViewId)); // Count how many views are filtered because they are not in current session int numOfViewsFiltered = 0; for (int i = 0; i < entryCount; i++) { @@ -6682,10 +7437,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId viewId = dataset.getFieldIds().get(i); final ViewState viewState = mViewStates.get(viewId); if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly() - && viewState != null && viewState.id.getSessionId() != id) { + && viewState != null + && viewState.id.getSessionId() != id) { if (sVerbose) { - Slog.v(TAG, "Skipping filling view: " + - viewId + " as it isn't part of the current session: " + id); + Slog.v( + TAG, + "Skipping filling view: " + + viewId + + " as it isn't part of the current session: " + + id); } numOfViewsFiltered += 1; continue; @@ -6721,8 +7481,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // does not matter the value of isPrimary because null response won't be // overridden. - setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, - /* clearResponse= */ false, /* isPrimary= */ true); + setViewStatesLocked( + null, + dataset, + ViewState.STATE_AUTOFILLED, + /* clearResponse= */ false, + /* isPrimary= */ true); } } catch (RemoteException e) { Slog.w(TAG, "Error autofilling activity: " + e); @@ -6750,7 +7514,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionCommittedEventLogger.maybeSetCommitReason(val); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); mSessionCommittedEventLogger.maybeSetSessionDurationMillis( - SystemClock.elapsedRealtime() - mStartTime); + SystemClock.elapsedRealtime() - mStartTime); mFillRequestEventLogger.logAndEndEvent(); mFillResponseEventLogger.logAndEndEvent(); mPresentationStatsEventLogger.logAndEndEvent("log all events"); @@ -6808,8 +7572,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final int totalAugmentedRequests = mAugmentedRequestsLogs == null ? 0 - : mAugmentedRequestsLogs.size(); + final int totalAugmentedRequests = + mAugmentedRequestsLogs == null ? 0 : mAugmentedRequestsLogs.size(); if (totalAugmentedRequests > 0) { if (sVerbose) { Slog.v(TAG, "destroyLocked(): logging " + totalRequests + " augmented requests"); @@ -6820,11 +7584,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests); + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests); if (totalAugmentedRequests > 0) { - log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, - totalAugmentedRequests); + log.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, totalAugmentedRequests); } if (mSessionFlags.mAugmentedAutofillOnly) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1); @@ -6846,8 +7611,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void forceRemoveFromServiceIfForAugmentedOnlyLocked() { if (sVerbose) { - Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): " - + mSessionFlags.mAugmentedAutofillOnly); + Slog.v( + TAG, + "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + + this.id + + "): " + + mSessionFlags.mAugmentedAutofillOnly); } if (!mSessionFlags.mAugmentedAutofillOnly) return; @@ -6880,9 +7649,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Thread-safe version of {@link #removeFromServiceLocked()}. - */ + /** Thread-safe version of {@link #removeFromServiceLocked()}. */ private void removeFromService() { synchronized (mLock) { removeFromServiceLocked(); @@ -6897,8 +7664,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void removeFromServiceLocked() { if (sVerbose) Slog.v(TAG, "removeFromServiceLocked(" + this.id + "): " + mPendingSaveUi); if (mDestroyed) { - Slog.w(TAG, "Call to Session#removeFromServiceLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#removeFromServiceLocked() rejected - session: " + + id + + " destroyed"); return; } if (isSaveUiPendingLocked()) { @@ -6922,18 +7692,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Checks whether this session is hiding the Save UI to handle a custom description link for - * a specific {@code token} created by - * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}. + * Checks whether this session is hiding the Save UI to handle a custom description link for a + * specific {@code token} created by {@link PendingUi#PendingUi(IBinder, int, + * IAutoFillManagerClient)}. */ @GuardedBy("mLock") boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) { return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken()); } - /** - * Checks whether this session is hiding the Save UI to handle a custom description link. - */ + /** Checks whether this session is hiding the Save UI to handle a custom description link. */ @GuardedBy("mLock") private boolean isSaveUiPendingLocked() { return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING; @@ -6942,8 +7710,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Return latest response index in mResponses SparseArray. @GuardedBy("mLock") private int getLastResponseIndexLocked() { - if (mResponses == null || mResponses.size() == 0) { - return -1; + if (mResponses == null || mResponses.size() == 0) { + return -1; } List<Integer> requestIdList = new ArrayList<>(); final int responseCount = mResponses.size(); @@ -6967,15 +7735,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void logAuthenticationStatusLocked(int requestId, int status) { - addTaggedDataToRequestLogLocked(requestId, - MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status); + addTaggedDataToRequestLogLocked( + requestId, MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status); } @GuardedBy("mLock") private void addTaggedDataToRequestLogLocked(int requestId, int tag, @Nullable Object value) { final LogMaker requestLog = mRequestLogs.get(requestId); if (requestLog == null) { - Slog.w(TAG, + Slog.w( + TAG, "addTaggedDataToRequestLogLocked(tag=" + tag + "): no log for id " + requestId); return; } @@ -6983,20 +7752,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logAugmentedAutofillRequestLocked(int mode, - ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted, + private void logAugmentedAutofillRequestLocked( + int mode, + ComponentName augmentedRemoteServiceName, + AutofillId focusedId, + boolean isWhitelisted, Boolean isInline) { final String historyItem = - "aug:id=" + id + " u=" + uid + " m=" + mode - + " a=" + ComponentName.flattenToShortString(mComponentName) - + " f=" + focusedId - + " s=" + augmentedRemoteServiceName - + " w=" + isWhitelisted - + " i=" + isInline; + "aug:id=" + + id + + " u=" + + uid + + " m=" + + mode + + " a=" + + ComponentName.flattenToShortString(mComponentName) + + " f=" + + focusedId + + " s=" + + augmentedRemoteServiceName + + " w=" + + isWhitelisted + + " i=" + + isInline; mService.getMaster().logRequestLocked(historyItem); } - private void wtf(@Nullable Exception e, String fmt, Object...args) { + private void wtf(@Nullable Exception e, String fmt, Object... args) { final String message = String.format(fmt, args); synchronized (mLock) { mWtfHistory.log(message); @@ -7051,13 +7833,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClassificationState.updateResponseReceived(response); } - public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) { + public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {} - } - - public void onClassificationRequestTimeout(int requestId) { - - } + public void onClassificationRequestTimeout(int requestId) {} @Override public void onServiceDied(@NonNull RemoteFieldClassificationService service) { @@ -7070,12 +7848,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void logFieldClassificationEvent( - long startTime, FieldClassificationResponse response, + long startTime, + FieldClassificationResponse response, @FieldClassificationEventLogger.FieldClassificationStatus int status) { final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger(); logger.startNewLogForRequest(); - logger.maybeSetLatencyMillis( - SystemClock.elapsedRealtime() - startTime); + logger.maybeSetLatencyMillis(SystemClock.elapsedRealtime() - startTime); logger.maybeSetAppPackageUid(uid); logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1); logger.maybeSetRequestId(sIdCounterForPcc.get()); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 5236b0399f25..7831c393844b 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -813,10 +813,8 @@ public final class ProcessList { private final Object mProcessChangeLock = new Object(); /** - * All of the applications we currently have running organized by name. - * The keys are strings of the application package name (as - * returned by the package manager), and the keys are ApplicationRecord - * objects. + * All of the processes that are running organized by name. + * The keys are process names and the values are the associated ProcessRecord objects. */ @CompositeRWLock({"mService", "mProcLock"}) private final MyProcessMap mProcessNames = new MyProcessMap(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 939aad469bd8..83044c202a04 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1536,7 +1536,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); } final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - return settings.getMethodMap().get(settings.getSelectedInputMethod()); + final String selectedImeId; + if (Flags.consistentGetCurrentInputMethodInfo()) { + final var bindingController = getInputMethodBindingController(userId); + synchronized (ImfLock.class) { + selectedImeId = bindingController.getSelectedMethodId(); + } + } else { + selectedImeId = settings.getSelectedInputMethod(); + } + return settings.getMethodMap().get(selectedImeId); } @BinderThread diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index a6f4c0e597d1..2a3be1e119bf 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -3100,11 +3100,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } synchronized (mUidRulesFirstLock) { - final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); - policy |= oldPolicy; - if (oldPolicy != policy) { - setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); - mLogger.uidPolicyChanged(uid, oldPolicy, policy); + final long token = Binder.clearCallingIdentity(); + try { + final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); + policy |= oldPolicy; + if (oldPolicy != policy) { + setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); + mLogger.uidPolicyChanged(uid, oldPolicy, policy); + } + } finally { + Binder.restoreCallingIdentity(token); } } } @@ -3119,11 +3124,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } synchronized (mUidRulesFirstLock) { - final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); - policy = oldPolicy & ~policy; - if (oldPolicy != policy) { - setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); - mLogger.uidPolicyChanged(uid, oldPolicy, policy); + final long token = Binder.clearCallingIdentity(); + try { + final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE); + policy = oldPolicy & ~policy; + if (oldPolicy != policy) { + setUidPolicyUncheckedUL(uid, oldPolicy, policy, true); + mLogger.uidPolicyChanged(uid, oldPolicy, policy); + } + } finally { + Binder.restoreCallingIdentity(token); } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index f3d6a2dd0a75..d5d4070ee4c3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -44,7 +44,7 @@ public interface NotificationManagerInternal { void onConversationRemoved(String pkg, int uid, Set<String> shortcuts); - /** Get the number of notification channels for a given package */ + /** Get the number of app created notification channels for a given package */ int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted); /** Does the specified package/uid have permission to post notifications? */ diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 828e02ce3edc..62d762244617 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4392,8 +4392,9 @@ public class NotificationManagerService extends SystemService { List<NotificationChannel> channels = channelsList.getList(); final int channelsSize = channels.size(); ParceledListSlice<NotificationChannel> oldChannels = - mPreferencesHelper.getNotificationChannels(pkg, uid, true); - final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty(); + mPreferencesHelper.getNotificationChannels(pkg, uid, true, false); + final boolean hadNonBundleChannel = + oldChannels != null && !oldChannels.getList().isEmpty(); boolean needsPolicyFileChange = false; boolean hasRequestedNotificationPermission = false; for (int i = 0; i < channelsSize; i++) { @@ -4410,13 +4411,18 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false), NOTIFICATION_CHANNEL_OR_GROUP_ADDED); - boolean hasChannel = hadChannel || hasRequestedNotificationPermission; - if (!hasChannel) { + boolean hasNonBundleChannel = + hadNonBundleChannel || hasRequestedNotificationPermission; + if (!hasNonBundleChannel) { ParceledListSlice<NotificationChannel> currChannels = - mPreferencesHelper.getNotificationChannels(pkg, uid, true); - hasChannel = currChannels != null && !currChannels.getList().isEmpty(); - } - if (!hadChannel && hasChannel && !hasRequestedNotificationPermission + mPreferencesHelper.getNotificationChannels(pkg, uid, true, false); + hasNonBundleChannel = + currChannels != null && !currChannels.getList().isEmpty(); + } + // show perm prompt if new non-bundle channel added and the user has not + // seen the prompt + if (!hadNonBundleChannel && hasNonBundleChannel + && !hasRequestedNotificationPermission && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) { hasRequestedNotificationPermission = true; if (mPermissionPolicyInternal == null) { @@ -4651,7 +4657,7 @@ public class NotificationManagerService extends SystemService { public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { enforceSystemOrSystemUI("getNotificationChannelsForPackage"); - return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted); + return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted, true); } @Override @@ -4783,7 +4789,7 @@ public class NotificationManagerService extends SystemService { /* ignore */ } return mPreferencesHelper.getNotificationChannels( - targetPkg, targetUid, false /* includeDeleted */); + targetPkg, targetUid, false /* includeDeleted */, true); } throw new SecurityException("Pkg " + callingPkg + " cannot read channels for " + targetPkg + " in " + userId); @@ -6652,7 +6658,7 @@ public class NotificationManagerService extends SystemService { verifyPrivilegedListener(token, user, true); return mPreferencesHelper.getNotificationChannels(pkg, - getUidForPackageAndUser(pkg, user), false /* includeDeleted */); + getUidForPackageAndUser(pkg, user), false /* includeDeleted */, true); } @Override @@ -7693,8 +7699,9 @@ public class NotificationManagerService extends SystemService { } int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { - return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList() - .size(); + // don't show perm prompt if the only channels are bundle channels + return mPreferencesHelper.getNotificationChannels( + pkg, uid, includeDeleted, false).getList().size(); } void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid, @@ -8005,11 +8012,16 @@ public class NotificationManagerService extends SystemService { } /** - * Returns a channel, if exists, and restores deleted conversation channels. + * Returns a channel, if exists and is not a bundle channel, and restores deleted + * conversation channels. */ @Nullable private NotificationChannel getNotificationChannelRestoreDeleted(String pkg, int callingUid, int notificationUid, String channelId, String conversationId) { + if (SYSTEM_RESERVED_IDS.contains(channelId)) { + // apps cannot post to these channels directly, in case they post incorrect content + return null; + } // Restore a deleted conversation channel, if exists. Otherwise use the parent channel. NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel( pkg, notificationUid, channelId, conversationId, diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 3349b1308c3f..c9edc4106943 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -1870,7 +1870,7 @@ public class PreferencesHelper implements RankingConfig { @Override public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, - boolean includeDeleted) { + boolean includeDeleted, boolean includeBundles) { Objects.requireNonNull(pkg); List<NotificationChannel> channels = new ArrayList<>(); synchronized (mLock) { @@ -1882,7 +1882,9 @@ public class PreferencesHelper implements RankingConfig { for (int i = 0; i < N; i++) { final NotificationChannel nc = r.channels.valueAt(i); if (includeDeleted || !nc.isDeleted()) { - channels.add(nc); + if (includeBundles || !SYSTEM_RESERVED_IDS.contains(nc.getId())) { + channels.add(nc); + } } } return new ParceledListSlice<>(channels); diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 8df24c9911a6..001e91cbea12 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -55,5 +55,5 @@ public interface RankingConfig { void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId); void permanentlyDeleteNotificationChannels(String pkg, int uid); ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, - boolean includeDeleted); + boolean includeDeleted, boolean includeBundles); } diff --git a/services/core/java/com/android/server/pinner/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java index d7ac5203ff53..2c75926c4943 100644 --- a/services/core/java/com/android/server/pinner/PinnerService.java +++ b/services/core/java/com/android/server/pinner/PinnerService.java @@ -121,9 +121,6 @@ public final class PinnerService extends SystemService { private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean("pinner.use_pinlist", true); - private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app. - private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app. - public static final String ANON_REGION_STAT_NAME = "[anon]"; private static final String SYSTEM_GROUP_NAME = "system"; @@ -179,8 +176,10 @@ public final class PinnerService extends SystemService { // Resource-configured pinner flags; private final boolean mConfiguredToPinCamera; + private final int mConfiguredCameraPinBytes; private final int mConfiguredHomePinBytes; private final boolean mConfiguredToPinAssistant; + private final int mConfiguredAssistantPinBytes; private final int mConfiguredWebviewPinBytes; // This is the percentage of total device memory that will be used to set the global quota. @@ -250,6 +249,10 @@ public final class PinnerService extends SystemService { mDeviceConfigInterface = mInjector.getDeviceConfigInterface(); mConfiguredToPinCamera = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerCameraApp); + mConfiguredCameraPinBytes = context.getResources().getInteger( + com.android.internal.R.integer.config_pinnerCameraPinBytes); + mConfiguredAssistantPinBytes = context.getResources().getInteger( + com.android.internal.R.integer.config_pinnerAssistantPinBytes); mConfiguredHomePinBytes = context.getResources().getInteger( com.android.internal.R.integer.config_pinnerHomePinBytes); mConfiguredToPinAssistant = context.getResources().getBoolean( @@ -812,11 +815,11 @@ public final class PinnerService extends SystemService { private int getSizeLimitForKey(@AppKey int key) { switch (key) { case KEY_CAMERA: - return MAX_CAMERA_PIN_SIZE; + return mConfiguredCameraPinBytes; case KEY_HOME: return mConfiguredHomePinBytes; case KEY_ASSISTANT: - return MAX_ASSISTANT_PIN_SIZE; + return mConfiguredAssistantPinBytes; default: return 0; } @@ -1303,6 +1306,10 @@ public final class PinnerService extends SystemService { pw.format(" Maximum Pinner quota: %d bytes (%.2f MB)\n", mConfiguredMaxPinnedMemory, mConfiguredMaxPinnedMemory / bytesPerMB); pw.format(" Max Home App Pin Bytes (without deps): %d\n", mConfiguredHomePinBytes); + pw.format(" Max Assistant App Pin Bytes (without deps): %d\n", + mConfiguredAssistantPinBytes); + pw.format( + " Max Camera App Pin Bytes (without deps): %d\n", mConfiguredCameraPinBytes); pw.format("\nPinned Files:\n"); synchronized (PinnerService.this) { long totalSize = 0; diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 9e8598a04a98..0b58c759b284 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -791,16 +791,6 @@ public final class DexOptHelper { } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); - - // This mirrors logic from commitReconciledScanResultLocked, where the library - // files needed for dexopt are assigned. - PackageSetting realPkgSetting = installRequest.getRealPackageSetting(); - // Unfortunately, the updated system app flag is only tracked on this - // PackageSetting - boolean isUpdatedSystemApp = - installRequest.getScannedPackageSetting().isUpdatedSystemApp(); - realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); - DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService( installRequest, dexoptOptions); installRequest.onDexoptFinished(dexOptResult); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index eb62b5631c43..8ba56c5320f2 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -771,6 +771,10 @@ public class Notifier { public void onGroupRemoved(int groupId) { mInteractivityByGroupId.remove(groupId); mWakefulnessSessionObserver.removePowerGroup(groupId); + if (mFlags.isPerDisplayWakeByTouchEnabled()) { + resetDisplayInteractivities(); + mInputManagerInternal.setDisplayInteractivities(mDisplayInteractivities); + } } /** diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java index fbcc856d0974..d192e64c897f 100644 --- a/services/core/java/com/android/server/vibrator/HalVibration.java +++ b/services/core/java/com/android/server/vibrator/HalVibration.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; import android.os.CombinedVibration; import android.os.VibrationAttributes; import android.os.VibrationEffect; +import android.os.VibratorInfo; +import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.VibrationEffectSegment; import android.util.SparseArray; @@ -145,19 +147,30 @@ final class HalVibration extends Vibration { originalEffect, mScaleLevel, mAdaptiveScale); } - /** - * Returns true if this vibration can pipeline with the specified one. - * - * <p>Note that currently, repeating vibrations can't pipeline with following vibrations, - * because the cancel() call to stop the repetition will cancel a pending vibration too. This - * can be changed if we have a use-case to reason around behavior for. It may also be nice to - * pipeline very short vibrations together, regardless of the flag. - */ - public boolean canPipelineWith(HalVibration vib) { - return callerInfo.uid == vib.callerInfo.uid && callerInfo.attrs.isFlagSet( - VibrationAttributes.FLAG_PIPELINED_EFFECT) - && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT) - && (mOriginalEffect.getDuration() != Long.MAX_VALUE); + /** Returns true if this vibration can pipeline with the specified one. */ + public boolean canPipelineWith(HalVibration vib, + @Nullable SparseArray<VibratorInfo> vibratorInfos, int durationThresholdMs) { + long effectDuration = Flags.vibrationPipelineEnabled() && (vibratorInfos != null) + ? mEffectToPlay.getDuration(vibratorInfos) + : mEffectToPlay.getDuration(); + if (effectDuration == Long.MAX_VALUE) { + // Repeating vibrations can't pipeline with following vibrations, because the cancel() + // call to stop the repetition will cancel a pending vibration too. This can be changed + // if we have a use-case, requiring changes to how pipelined vibrations are cancelled. + return false; + } + if (Flags.vibrationPipelineEnabled() + && (effectDuration > 0) && (effectDuration < durationThresholdMs)) { + // Duration is known and it's less than the pipeline threshold, so allow it. + // No need to check UID, as we want to avoid cancelling any short effect and let the + // vibrator hardware gracefully finish the vibration. + return true; + } + // Check the same app is requesting multiple vibrations with the pipeline flag, + // independently of the effect durations. + return callerInfo.uid == vib.callerInfo.uid + && callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT) + && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT); } private void fillFallbacksForEffect(CombinedVibration effect, diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 9b7bdece69f9..7d5d34dbf7ab 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -168,12 +168,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @VisibleForTesting final VibrationSettings mVibrationSettings; + private final VibrationConfig mVibrationConfig; private final VibrationScaler mVibrationScaler; private final VibratorControlService mVibratorControlService; private final InputDeviceDelegate mInputDeviceDelegate; private final DeviceAdapter mDeviceAdapter; @GuardedBy("mLock") + @Nullable private SparseArray<VibratorInfo> mVibratorInfos; + @GuardedBy("mLock") @Nullable private VibratorInfo mCombinedVibratorInfo; @GuardedBy("mLock") @Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider; @@ -247,9 +250,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mHandler = injector.createHandler(Looper.myLooper()); mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler); - VibrationConfig vibrationConfig = new VibrationConfig(context.getResources()); - mVibrationSettings = new VibrationSettings(mContext, mHandler, vibrationConfig); - mVibrationScaler = new VibrationScaler(vibrationConfig, mVibrationSettings); + mVibrationConfig = new VibrationConfig(context.getResources()); + mVibrationSettings = new VibrationSettings(mContext, mHandler, mVibrationConfig); + mVibrationScaler = new VibrationScaler(mVibrationConfig, mVibrationSettings); mVibratorControlService = new VibratorControlService(mContext, injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings, mFrameworkStatsLogger, mLock); @@ -295,7 +298,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibratorIds = vibratorIds; mVibrators = new SparseArray<>(mVibratorIds.length); for (int vibratorId : vibratorIds) { - mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener)); + VibratorController vibratorController = + injector.createVibratorController(vibratorId, listener); + mVibrators.put(vibratorId, vibratorController); } } @@ -334,6 +339,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrators.valueAt(i).reloadVibratorInfoIfNeeded(); } + synchronized (mLock) { + mVibratorInfos = transformAllVibratorsLocked(VibratorController::getVibratorInfo); + VibratorInfo[] infos = new VibratorInfo[mVibratorInfos.size()]; + for (int i = 0; i < mVibratorInfos.size(); i++) { + infos[i] = mVibratorInfos.valueAt(i); + } + mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos); + } + mVibrationSettings.onSystemReady(); mInputDeviceDelegate.onSystemReady(); @@ -633,7 +647,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { endExternalVibrateLocked(Status.CANCELLED_SUPERSEDED, callerInfo, /* continueExternalControl= */ false); } else if (mCurrentVibration != null) { - if (mCurrentVibration.getVibration().canPipelineWith(vib)) { + if (mCurrentVibration.getVibration().canPipelineWith(vib, mVibratorInfos, + mVibrationConfig.getVibrationPipelineMaxDurationMs())) { // Don't cancel the current vibration if it's pipeline-able. // Note that if there is a pending next vibration that can't be // pipelined, it will have already cancelled the current one, so we @@ -1871,33 +1886,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } + @Nullable private VibratorInfo getCombinedVibratorInfo() { synchronized (mLock) { - // Used a cached resolving vibrator if one exists. - if (mCombinedVibratorInfo != null) { - return mCombinedVibratorInfo; - } - - // Return an empty resolving vibrator if the service has no vibrator. - if (mVibratorIds.length == 0) { - return mCombinedVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO; - } - - // Combine the vibrator infos of all the service's vibrator to create a single resolving - // vibrator that is based on the combined info. - VibratorInfo[] infos = new VibratorInfo[mVibratorIds.length]; - for (int i = 0; i < mVibratorIds.length; i++) { - VibratorInfo info = getVibratorInfo(mVibratorIds[i]); - // If any one of the service's vibrator does not have a valid vibrator info, stop - // trying to create and cache a combined resolving vibrator. Combine the infos only - // when infos for all vibrators are available. - if (info == null) { - return null; - } - infos[i] = info; - } - - return mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos); + // This is only initialized at system ready, when all vibrator infos are fully loaded. + return mCombinedVibratorInfo; } } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index a380ba1a6f11..f9902cf0db9c 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -66,6 +66,7 @@ import android.app.BackgroundStartPrivileges; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; +import android.compat.annotation.Overridable; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -129,6 +130,7 @@ public class BackgroundActivityStartController { /** If enabled the creator will not allow BAL on its behalf by default. */ @ChangeId @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE) + @Overridable private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR = 296478951; public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED = diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 6a23aaa663ef..e9c6e93891df 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -274,34 +274,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { if (caller != controlTarget) { if (Flags.refactorInsetsController()) { if (isImeInputTarget(caller)) { - // In case of the multi window mode, update the requestedVisibleTypes from - // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController. - // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with - // its new requested visibility for the IME - boolean imeVisible = caller.isRequestedVisible(WindowInsets.Type.ime()); - if (controlTarget != null) { - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); - controlTarget.setImeInputTargetRequestedVisibility(imeVisible); - } else if (caller instanceof InsetsControlTarget) { - // In case of a virtual display that cannot show the IME, the - // controlTarget will be null here, as no controlTarget was set yet. In - // that case, proceed similar to the multi window mode (fallback = - // RemoteInsetsControlTarget of the default display) - controlTarget = mDisplayContent.getImeHostOrFallback( - ((InsetsControlTarget) caller).getWindow()); - - if (controlTarget != caller) { - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); - controlTarget.setImeInputTargetRequestedVisibility(imeVisible); - } else { - ImeTracker.forLogging().onFailed(statsToken, - ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); - } - } - - invokeOnImeRequestedChangedListener(caller, statsToken); + reportImeInputTargetStateToControlTarget(caller, controlTarget, statsToken); } else { // TODO(b/353463205) add ImeTracker? } @@ -332,14 +305,42 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { if (Flags.refactorInsetsController() && target != null) { InsetsControlTarget imeControlTarget = getControlTarget(); if (target != imeControlTarget) { - // If the targetWin is not the imeControlTarget (=RemoteInsetsControlTarget) let it - // know about the new requestedVisibleTypes for the IME. - if (imeControlTarget != null) { - imeControlTarget.setImeInputTargetRequestedVisibility( - (target.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0); - } + // TODO(b/353463205): start new request here? + reportImeInputTargetStateToControlTarget(target, imeControlTarget, + null /* statsToken */); + } + } + } + + private void reportImeInputTargetStateToControlTarget(@NonNull InsetsTarget imeInsetsTarget, + InsetsControlTarget controlTarget, @Nullable ImeTracker.Token statsToken) { + // In case of the multi window mode, update the requestedVisibleTypes from + // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController. + // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with + // its new requested visibility for the IME + boolean imeVisible = imeInsetsTarget.isRequestedVisible(WindowInsets.Type.ime()); + if (controlTarget != null) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); + controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + } else if (imeInsetsTarget instanceof InsetsControlTarget) { + // In case of a virtual display that cannot show the IME, the + // controlTarget will be null here, as no controlTarget was set yet. In + // that case, proceed similar to the multi window mode (fallback = + // RemoteInsetsControlTarget of the default display) + controlTarget = mDisplayContent.getImeHostOrFallback( + ((InsetsControlTarget) imeInsetsTarget).getWindow()); + + if (controlTarget != imeInsetsTarget) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); + controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + } else { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); } } + invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken); } // TODO(b/353463205) check callers to see if we can make statsToken @NonNull diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index cc340c0a5f79..891c3349a43f 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -58,6 +58,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.EventLog; import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; @@ -1737,6 +1738,11 @@ public class MidiService extends IMidiManager.Stub { pw.decreaseIndent(); } + @Override + protected void onUnhandledException(int code, int flags, Exception e) { + Slog.wtf(TAG, "Uncaught exception in AudioService: " + code + ", " + flags, e); + } + @GuardedBy("mUsbMidiLock") private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) { String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 3b5a386dd303..b917af4d796e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -1658,6 +1658,70 @@ public class DisplayManagerServiceTest { } @Test + public void testGetDisplayIdsByGroupsIds() throws Exception { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + DisplayManagerInternal localService = displayManager.new LocalService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + // Create display 1 + FakeDisplayDevice displayDevice1 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1); + final int groupId1 = display1.getDisplayInfoLocked().displayGroupId; + // Create display 2 + FakeDisplayDevice displayDevice2 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2); + final int groupId2 = display2.getDisplayInfoLocked().displayGroupId; + // Both displays should be in the same display group + assertEquals(groupId1, groupId2); + final int[] displayIds = new int[]{ + display1.getDisplayIdLocked(), display2.getDisplayIdLocked()}; + final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>(); + expectedDisplayGroups.put(groupId1, displayIds); + + final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds(); + + for (int i = 0; i < expectedDisplayGroups.size(); i++) { + final int groupId = expectedDisplayGroups.keyAt(i); + assertTrue(displayGroups.contains(groupId)); + assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId)); + } + } + + @Test + public void testGetDisplayIdsByGroupsIds_multipleDisplayGroups() throws Exception { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + DisplayManagerInternal localService = displayManager.new LocalService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + // Create display 1 + FakeDisplayDevice displayDevice1 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1); + final int groupId1 = display1.getDisplayInfoLocked().displayGroupId; + // Create display 2 + FakeDisplayDevice displayDevice2 = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL); + LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2); + final int groupId2 = display2.getDisplayInfoLocked().displayGroupId; + // Both displays should be in different display groups + assertNotEquals(groupId1, groupId2); + final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>(); + expectedDisplayGroups.put(groupId1, new int[]{display1.getDisplayIdLocked()}); + expectedDisplayGroups.put(groupId2, new int[]{display2.getDisplayIdLocked()}); + + final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds(); + + assertEquals(expectedDisplayGroups.size(), displayGroups.size()); + for (int i = 0; i < expectedDisplayGroups.size(); i++) { + final int groupId = expectedDisplayGroups.keyAt(i); + assertTrue(displayGroups.contains(groupId)); + assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId)); + } + } + + @Test public void testCreateVirtualDisplay_isValidProjection_notValid() throws RemoteException { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 31157f9e27dc..9781851da7e6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -95,7 +95,6 @@ import com.android.server.job.JobSchedulerService; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.QcConstants; -import com.android.server.job.controllers.QuotaController.QuotaBump; import com.android.server.job.controllers.QuotaController.ShrinkableDebits; import com.android.server.job.controllers.QuotaController.TimedEvent; import com.android.server.job.controllers.QuotaController.TimingSession; @@ -136,7 +135,6 @@ public class QuotaControllerTest { private QuotaController.QcConstants mQcConstants; private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants(); private int mSourceUid; - private AppStandbyInternal.AppIdleStateChangeListener mAppIdleStateChangeListener; private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener; private IUidObserver mUidObserver; private UsageStatsManagerInternal.UsageEventListener mUsageEventListener; @@ -191,8 +189,7 @@ public class QuotaControllerTest { when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); doReturn(mActivityMangerInternal) .when(() -> LocalServices.getService(ActivityManagerInternal.class)); - final AppStandbyInternal appStandbyInternal = mock(AppStandbyInternal.class); - doReturn(appStandbyInternal) + doReturn(mock(AppStandbyInternal.class)) .when(() -> LocalServices.getService(AppStandbyInternal.class)); doReturn(mock(BatteryManagerInternal.class)) .when(() -> LocalServices.getService(BatteryManagerInternal.class)); @@ -239,8 +236,6 @@ public class QuotaControllerTest { // Initialize real objects. // Capture the listeners. - ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> aiscListenerCaptor = - ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class); ArgumentCaptor<IUidObserver> uidObserverCaptor = ArgumentCaptor.forClass(IUidObserver.class); ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = @@ -250,8 +245,6 @@ public class QuotaControllerTest { mQuotaController = new QuotaController(mJobSchedulerService, mock(BackgroundJobsController.class), mock(ConnectivityController.class)); - verify(appStandbyInternal).addListener(aiscListenerCaptor.capture()); - mAppIdleStateChangeListener = aiscListenerCaptor.getValue(); verify(mPowerAllowlistInternal) .registerTempAllowlistChangeListener(taChangeCaptor.capture()); mTempAllowlistListener = taChangeCaptor.getValue(); @@ -488,14 +481,12 @@ public class QuotaControllerTest { now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); TimingSession two = createTimingSession( now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); - QuotaBump bump1 = new QuotaBump(now - 2 * HOUR_IN_MILLIS); TimingSession thr = createTimingSession( now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); // Overlaps 24 hour boundary. TimingSession fou = createTimingSession( now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1); // Way past the 24 hour boundary. - QuotaBump bump2 = new QuotaBump(now - 24 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); TimingSession fiv = createTimingSession( now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4); List<TimedEvent> expectedRegular = new ArrayList<>(); @@ -503,16 +494,13 @@ public class QuotaControllerTest { // Added in correct (chronological) order. expectedRegular.add(fou); expectedRegular.add(thr); - expectedRegular.add(bump1); expectedRegular.add(two); expectedRegular.add(one); expectedEJ.add(fou); expectedEJ.add(one); mQuotaController.saveTimingSession(0, "com.android.test", fiv, false); - mQuotaController.getTimingSessions(0, "com.android.test").add(bump2); mQuotaController.saveTimingSession(0, "com.android.test", fou, false); mQuotaController.saveTimingSession(0, "com.android.test", thr, false); - mQuotaController.getTimingSessions(0, "com.android.test").add(bump1); mQuotaController.saveTimingSession(0, "com.android.test", two, false); mQuotaController.saveTimingSession(0, "com.android.test", one, false); mQuotaController.saveTimingSession(0, "com.android.test", fiv, true); @@ -1847,129 +1835,6 @@ public class QuotaControllerTest { } } - /** - * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket - * window and there are valid QuotaBumps in the history. - */ - @Test - public void testGetTimeUntilQuotaConsumedLocked_QuotaBump() { - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - // Close to RARE boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS), - 30 * SECOND_IN_MILLIS, 5), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS)); - // Far away from FREQUENT boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); - // Overlap WORKING_SET boundary. - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 2 * HOUR_IN_MILLIS)); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 5), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS)); - // Close to ACTIVE boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); - - setStandbyBucket(RARE_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(3 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(4 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - - setStandbyBucket(FREQUENT_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(4 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(4 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - - setStandbyBucket(WORKING_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(8 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(10 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - - // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the - // max execution time. - setStandbyBucket(ACTIVE_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(10 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - } - - /** - * Test getTimeUntilQuotaConsumedLocked when there are valid QuotaBumps in recent history that - * provide enough additional quota to bridge gaps between sessions. - */ - @Test - public void testGetTimeUntilQuotaConsumedLocked_QuotaBump_CrucialBumps() { - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (25 * HOUR_IN_MILLIS), - 30 * MINUTE_IN_MILLIS, 25), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS)); - // Without the valid quota bumps, the app would only 3 minutes until the quota was consumed. - // The quota bumps provide enough quota to bridge the gap between the two earliest sessions. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (8 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (8 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS), - 2 * MINUTE_IN_MILLIS, 5), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS)); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 1), false); - - setStandbyBucket(FREQUENT_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(2 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(7 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - } - @Test public void testIsWithinQuotaLocked_NeverApp() { synchronized (mQuotaController.mLock) { @@ -2316,272 +2181,6 @@ public class QuotaControllerTest { } } - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_Duration() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 5 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession( - now - (HOUR_IN_MILLIS - 2 * MINUTE_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1), - false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 1); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(5 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Phase out the first session - advanceElapsedClock(5 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - } - - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_JobCount() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 20 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS), - 5 * MINUTE_IN_MILLIS, 10), false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 10); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(10, stats.jobCountLimit); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - } - - // Phase out the first session - advanceElapsedClock(3 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - } - - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_SessionCount() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 20 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 2); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS), - 5 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(2, stats.sessionCountLimit); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - // Phase out the first session - advanceElapsedClock(2 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - } @Test public void testIsWithinEJQuotaLocked_NeverApp() { @@ -3590,12 +3189,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 84 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - 93 * SECOND_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 92); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 91); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 90 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 89); assertEquals(8 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3653,11 +3246,6 @@ public class QuotaControllerTest { assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(93 * SECOND_IN_MILLIS, mQuotaController.getQuotaBumpAdditionDurationMs()); - assertEquals(92, mQuotaController.getQuotaBumpAdditionJobCount()); - assertEquals(91, mQuotaController.getQuotaBumpAdditionSessionCount()); - assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); - assertEquals(89, mQuotaController.getQuotaBumpLimit()); } @Test @@ -3710,11 +3298,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, -1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, -1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, -1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 59 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, -1); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3765,11 +3348,6 @@ public class QuotaControllerTest { assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionDurationMs()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionJobCount()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionSessionCount()); - assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); - assertEquals(0, mQuotaController.getQuotaBumpLimit()); // Invalid configurations. // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD @@ -3827,7 +3405,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3868,7 +3445,6 @@ public class QuotaControllerTest { assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); } /** Tests that TimingSessions aren't saved when the device is charging. */ diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java index 639ae30c00b9..58489f398775 100644 --- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -81,7 +81,6 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -581,8 +580,6 @@ public class HintManagerServiceTest { HintManagerService service = createService(); IBinder token = new Binder(); int threadCount = 2; - - // session 1 has 2 non-isolated tids long sessionPtr1 = 111; CountDownLatch stopLatch1 = new CountDownLatch(1); int[] tids1 = createThreads(threadCount, stopLatch1); @@ -618,7 +615,6 @@ public class HintManagerServiceTest { IBinder token = new Binder(); int threadCount = 2; - // session 1 has 2 non-isolated tids long sessionPtr1 = 111; CountDownLatch stopLatch1 = new CountDownLatch(1); int[] tids1 = createThreads(threadCount, stopLatch1); @@ -630,67 +626,23 @@ public class HintManagerServiceTest { SessionTag.OTHER, new SessionConfig()); assertNotNull(session1); - // session 2 has 2 non-isolated tids and 2 isolated tids - long sessionPtr2 = 222; - CountDownLatch stopLatch2 = new CountDownLatch(1); - // negative value used for test only to avoid conflicting with any real thread that exists - int isoProc1 = -100; - int isoProc2 = 99999999; - when(mAmInternalMock.getIsolatedProcesses(eq(UID))).thenReturn(List.of(0)); - int[] tids2 = createThreads(threadCount, stopLatch2); - int[] tids2WithIsolated = Arrays.copyOf(tids2, tids2.length + 2); - tids2WithIsolated[threadCount] = isoProc1; - tids2WithIsolated[threadCount + 1] = isoProc2; - int[] expectedTids2 = Arrays.copyOf(tids2, tids2.length + 1); - expectedTids2[tids2.length] = isoProc1; - when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID), - eq(tids2WithIsolated), eq(DEFAULT_TARGET_DURATION), anyInt(), - any(SessionConfig.class))).thenReturn(sessionPtr2); - AppHintSession session2 = (AppHintSession) service.getBinderServiceInstance() - .createHintSessionWithConfig(token, tids2WithIsolated, - DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig()); - assertNotNull(session2); - - // trigger clean up through UID state change by making the process foreground->background - // this will remove the one unexpected isolated tid from session 2 - service.mUidObserver.onUidStateChanged(UID, - ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); - service.mUidObserver.onUidStateChanged(UID, - ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); - LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos( - CLEAN_UP_UID_DELAY_MILLIS)); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any()); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any()); - // the new TIDs pending list should be updated - assertArrayEquals(expectedTids2, session2.getTidsInternal()); - reset(mNativeWrapperMock); - - // this should resume and update the threads so those never-existed invalid isolated - // processes should be cleaned up - service.mUidObserver.onUidStateChanged(UID, - ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); - // wait for the async uid state change to trigger resume and setThreads - LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000)); - verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), eq(expectedTids2)); - reset(mNativeWrapperMock); - // let all session 1 threads to exit and the cleanup should force pause the session 1 stopLatch1.countDown(); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100)); service.mUidObserver.onUidStateChanged(UID, + ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); + service.mUidObserver.onUidStateChanged(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos( CLEAN_UP_UID_DELAY_MILLIS)); verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1)); verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any()); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any()); verifyAllHintsEnabled(session1, false); - verifyAllHintsEnabled(session2, false); service.mUidObserver.onUidStateChanged(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000)); + assertArrayEquals(tids1, session1.getTidsInternal()); verifyAllHintsEnabled(session1, false); - verifyAllHintsEnabled(session2, true); reset(mNativeWrapperMock); // in foreground, set new tids for session 1 then it should be resumed and all hints allowed @@ -704,8 +656,6 @@ public class HintManagerServiceTest { // let all session 1 and 2 non isolated threads to exit stopLatch1.countDown(); - stopLatch2.countDown(); - expectedTids2 = new int[]{isoProc1}; LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100)); service.mUidObserver.onUidStateChanged(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); @@ -713,14 +663,11 @@ public class HintManagerServiceTest { CLEAN_UP_UID_DELAY_MILLIS)); verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1)); verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any()); - verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any()); // in background, set threads for session 1 then it should not be force paused next time session1.setThreads(SESSION_TIDS_A); // the new TIDs pending list should be updated assertArrayEquals(SESSION_TIDS_A, session1.getTidsInternal()); - assertArrayEquals(expectedTids2, session2.getTidsInternal()); verifyAllHintsEnabled(session1, false); - verifyAllHintsEnabled(session2, false); reset(mNativeWrapperMock); service.mUidObserver.onUidStateChanged(UID, @@ -729,10 +676,7 @@ public class HintManagerServiceTest { CLEAN_UP_UID_DELAY_MILLIS)); verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr1), eq(SESSION_TIDS_A)); - verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), - eq(expectedTids2)); verifyAllHintsEnabled(session1, true); - verifyAllHintsEnabled(session2, true); } private void verifyAllHintsEnabled(AppHintSession session, boolean verifyEnabled) { diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java index a1db18232c09..1c7fc63efd41 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java @@ -54,6 +54,7 @@ import android.os.test.TestLooper; import android.provider.Settings; import android.testing.TestableContext; import android.util.IntArray; +import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.Display; import android.view.DisplayAddress; @@ -384,6 +385,32 @@ public class NotifierTest { } @Test + public void testOnGroupRemoved_perDisplayWakeByTouchEnabled() { + createNotifier(); + // GIVEN per-display wake by touch is enabled and one display group has been defined + when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(true); + final int groupId = 313; + final int displayId1 = 3113; + final int displayId2 = 4114; + final int[] displays = new int[]{displayId1, displayId2}; + when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displays)); + when(mDisplayManagerInternal.getDisplayIdsForGroup(groupId)).thenReturn(displays); + mNotifier.onGroupWakefulnessChangeStarted( + groupId, WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_TAP, /* eventTime= */ 1000); + final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray(); + expectedDisplayInteractivities.put(displayId1, true); + expectedDisplayInteractivities.put(displayId2, true); + verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities); + + // WHEN display group is removed + when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(new SparseArray<>()); + mNotifier.onGroupRemoved(groupId); + + // THEN native input manager is informed that displays in that group no longer exist + verify(mInputManagerInternal).setDisplayInteractivities(new SparseBooleanArray()); + } + + @Test public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException { when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true); createNotifier(); 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 becb9fdabfcf..e845d80b412a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4838,7 +4838,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, mPkg, Process.myUserHandle()); verify(mPreferencesHelper, times(1)).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4856,7 +4856,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4871,7 +4871,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, mPkg, Process.myUserHandle()); verify(mPreferencesHelper, times(1)).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4891,7 +4891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4913,7 +4913,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -17062,4 +17062,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(mService.hasFlag(captor.getValue().getNotification().flags, FLAG_PROMOTED_ONGOING)).isFalse(); } + + @Test + @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testAppCannotUseReservedBundleChannels() throws Exception { + mBinderService.getBubblePreferenceForPackage(mPkg, mUid); + NotificationChannel news = mBinderService.getNotificationChannel( + mPkg, mContext.getUserId(), mPkg, NEWS_ID); + assertThat(news).isNotNull(); + + NotificationRecord nr = generateNotificationRecord(news); + mBinderService.enqueueNotificationWithTag(mPkg, mPkg, nr.getSbn().getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + assertThat(mService.mNotificationList).isEmpty(); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 404ede676f02..b92bdb5f3e6e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2547,7 +2547,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // Returns only non-deleted channels List<NotificationChannel> channels = - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList(); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList(); // Default channel + non-deleted channel + system defaults assertEquals(notificationClassification() ? 6 : 2, channels.size()); for (NotificationChannel nc : channels) { @@ -2557,7 +2557,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { } // Returns deleted channels too - channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList(); + channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList(); // Includes system channel(s) assertEquals(notificationClassification() ? 7 : 3, channels.size()); for (NotificationChannel nc : channels) { @@ -3199,7 +3199,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1); // Only default channel remains - assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size()); + assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true) + .getList().size()); } @Test @@ -3315,12 +3316,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { // user 0 records remain for (int i = 0; i < user0Uids.length; i++) { assertEquals(notificationClassification() ? 5 : 1, - mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false) + mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true) .getList().size()); } // user 1 records are gone for (int i = 0; i < user1Uids.length; i++) { - assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false) + assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false, true) .getList().size()); } } @@ -3337,7 +3338,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { new int[]{UID_N_MR1})); assertEquals(0, mHelper.getNotificationChannels( - PKG_N_MR1, UID_N_MR1, true).getList().size()); + PKG_N_MR1, UID_N_MR1, true, true).getList().size()); // Not deleted mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false, @@ -3346,7 +3347,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1})); assertEquals(notificationClassification() ? 6 : 2, - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size()); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) + .getList().size()); } @Test @@ -3405,7 +3407,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertTrue(mHelper.canShowBadge(PKG_O, UID_O)); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); - assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true).getList().size()); + assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()); assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size()); NotificationChannel channel = getChannel(); @@ -3419,7 +3421,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testRecordDefaults() throws Exception { assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); assertEquals(notificationClassification() ? 5 : 1, - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size()); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) + .getList().size()); } @Test @@ -6363,6 +6366,15 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testGetNotificationChannels_omitBundleChannels() { + // do something that triggers settings creation for an app + mHelper.setShowBadge(PKG_O, UID_O, true); + + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty(); + } + + @Test + @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testNotificationBundles() { // do something that triggers settings creation for an app mHelper.setShowBadge(PKG_O, UID_O, true); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index b7821623855c..7f5da41bdf10 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -16,8 +16,6 @@ package com.android.server.vibrator; -import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED; - import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; @@ -28,6 +26,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -89,17 +88,14 @@ import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.HapticFeedbackConstants; import android.view.InputDevice; -import android.view.flags.Flags; import androidx.test.InstrumentationRegistry; @@ -168,9 +164,7 @@ public class VibratorManagerServiceTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock; @@ -800,7 +794,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS) + @EnableFlags(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS) public void vibrate_thenDeniedAppOps_getsCancelled() throws Throwable { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); @@ -894,7 +888,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) + @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) public void vibrate_thenFgUserRequestsMute_getsCancelled() throws Throwable { mockVibrators(1); VibratorManagerService service = createSystemReadyService(); @@ -1331,6 +1325,37 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_VIBRATION_PIPELINE_ENABLED) + public void vibrate_withPipelineFlagEnabledAndShortEffect_continuesOngoingEffect() + throws Exception { + assumeTrue(mVibrationConfig.getVibrationPipelineMaxDurationMs() > 0); + + mockVibrators(1); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + fakeVibrator.setSupportedPrimitives( + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_THUD); + fakeVibrator.setPrimitiveDuration( + mVibrationConfig.getVibrationPipelineMaxDurationMs() - 1); + VibratorManagerService service = createSystemReadyService(); + + HalVibration firstVibration = vibrateWithUid(service, /* uid= */ 123, + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(), HAPTIC_FEEDBACK_ATTRS); + HalVibration secondVibration = vibrateWithUid(service, /* uid= */ 456, + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .compose(), HAPTIC_FEEDBACK_ATTRS); + secondVibration.waitForEnd(); + + assertThat(fakeVibrator.getAllEffectSegments()).hasSize(2); + assertThat(firstVibration.getStatus()).isEqualTo(Status.FINISHED); + assertThat(secondVibration.getStatus()).isEqualTo(Status.FINISHED); + } + + @Test public void vibrate_withInputDevices_vibratesInputDevices() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); @@ -1512,6 +1537,7 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API) public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception { // Deny permissions that would have been required for regular vibrations, and check that // the vibration proceed as expected to verify that haptic feedback does not need these @@ -1520,8 +1546,6 @@ public class VibratorManagerServiceTest { denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); - // Flag override to enable the scroll feedack constants to bypass interruption policies. - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); mHapticFeedbackVibrationMap.put( HapticFeedbackConstants.SCROLL_TICK, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); @@ -1544,6 +1568,10 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedbackForInputDevice_doesNotRequireVibrateOrBypassPermissions() throws Exception { // Deny permissions that would have been required for regular vibrations, and check that @@ -1553,9 +1581,6 @@ public class VibratorManagerServiceTest { denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS); denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE); denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); - // Flag override to enable the scroll feedback constants to bypass interruption policies. - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); mHapticFeedbackVibrationMapSourceRotary.put( HapticFeedbackConstants.SCROLL_TICK, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); @@ -1628,12 +1653,14 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedbackForInputDevice_restrictedConstantsWithoutPermission_doesNotVibrate() throws Exception { // Deny permission to vibrate with restricted constants denyPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS); - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); // Public constant, no permission required mHapticFeedbackVibrationMapSourceRotary.put( HapticFeedbackConstants.CONFIRM, @@ -1697,9 +1724,9 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags(android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED) public void performHapticFeedbackForInputDevice_restrictedConstantsWithPermission_playsVibration() throws Exception { - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); // Grant permission to vibrate with restricted constants grantPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS); // Public constant, no permission required @@ -1732,9 +1759,11 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); denyPermission(android.Manifest.permission.VIBRATE); mHapticFeedbackVibrationMap.put( HapticFeedbackConstants.KEYBOARD_TAP, @@ -1767,9 +1796,11 @@ public class VibratorManagerServiceTest { } @Test + @EnableFlags({ + android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API, + android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED, + }) public void performHapticFeedback_doesNotVibrateForInvalidConstant() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API); - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); denyPermission(android.Manifest.permission.VIBRATE); mockVibrators(1); VibratorManagerService service = createSystemReadyService(); @@ -1791,7 +1822,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception { // Deny permission to vibrate with vendor effects denyPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS); @@ -1816,7 +1847,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void vibrate_vendorEffectsWithPermission_successful() throws Exception { // Grant permission to vibrate with vendor effects grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS); @@ -1904,7 +1935,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void vibrate_withAdaptiveHaptics_appliesCorrectAdaptiveScales() throws Exception { // Keep user settings the same as device default so only adaptive scale is applied. setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, @@ -1947,7 +1978,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled({ + @EnableFlags({ android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED, android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS, }) @@ -2418,7 +2449,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales() { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL, @@ -2465,7 +2496,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + @DisableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone() { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL, @@ -2585,7 +2616,7 @@ public class VibratorManagerServiceTest { } @Test - @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) + @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS) public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); @@ -3105,9 +3136,20 @@ public class VibratorManagerServiceTest { return vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs); } + private HalVibration vibrateWithUid(VibratorManagerService service, int uid, + VibrationEffect effect, VibrationAttributes attrs) { + return vibrateWithUidAndDevice(service, uid, Context.DEVICE_ID_DEFAULT, + CombinedVibration.createParallel(effect), attrs); + } + private HalVibration vibrateWithDevice(VibratorManagerService service, int deviceId, CombinedVibration effect, VibrationAttributes attrs) { - HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect, + return vibrateWithUidAndDevice(service, UID, deviceId, effect, attrs); + } + + private HalVibration vibrateWithUidAndDevice(VibratorManagerService service, int uid, + int deviceId, CombinedVibration effect, VibrationAttributes attrs) { + HalVibration vib = service.vibrateWithPermissionCheck(uid, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service); if (vib != null) { mPendingVibrations.add(vib); diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java index 6dc1b10ec930..75a9cedfd8c4 100644 --- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -80,6 +80,7 @@ public final class FakeVibratorControllerProvider { private float[] mFrequenciesHz; private float[] mOutputAccelerationsGs; private long mVendorEffectDuration = EFFECT_DURATION; + private long mPrimitiveDuration = EFFECT_DURATION; void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) { mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment); @@ -171,7 +172,7 @@ public final class FakeVibratorControllerProvider { } long duration = 0; for (PrimitiveSegment primitive : primitives) { - duration += EFFECT_DURATION + primitive.getDelay(); + duration += mPrimitiveDuration + primitive.getDelay(); recordEffectSegment(vibrationId, primitive); } applyLatency(mOnLatency); @@ -381,6 +382,11 @@ public final class FakeVibratorControllerProvider { mVendorEffectDuration = durationMs; } + /** Set the duration of primitives in fake vibrator hardware. */ + public void setPrimitiveDuration(long primitiveDuration) { + mPrimitiveDuration = primitiveDuration; + } + /** * Set the maximum number of envelope effects control points supported in fake vibrator * hardware. diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index ab00bfdf41ae..1f167761fc06 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -71,6 +71,7 @@ android_test { "CtsSurfaceValidatorLib", "service-sdksandbox.impl", "com.android.window.flags.window-aconfig-java", + "android.view.inputmethod.flags-aconfig-java", "flag-junit", ], diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index d2cf03dd4b9a..ee56210e278d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -41,11 +41,13 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; import android.app.StatusBarManager; +import android.graphics.Insets; import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.InsetsSourceControl; @@ -525,6 +527,59 @@ public class InsetsPolicyTest extends WindowTestsBase { assertTrue(win1.getWindowFrames().hasInsetsChanged()); } + /** + * This test verifies that after setting {@link WindowContainer#mExcludeInsetsTypes}, the IME + * insets have a height of zero (applied in {@link InsetsPolicy#adjustVisibilityForIme}). + */ + @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER) + @SetupWindows(addWindows = W_INPUT_METHOD) + @Test + public void testExcludeImeInsets() { + final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); + final InsetsSource imeSource = new InsetsSource(ID_IME, ime()); + imeSource.setVisible(true); + mImeWindow.mHasSurface = true; + + final WindowState win = addWindow(TYPE_APPLICATION, "win1"); + win.setRequestedVisibleTypes(0, ime()); + + win.mAboveInsetsState.addSource(imeSource); + win.mHasSurface = true; + + DisplayContentTests.performLayout(mDisplayContent); + // IME should cover half of the app's window + final var winFrame = win.getFrame(); + imeSource.setFrame(winFrame.left, winFrame.bottom / 2, winFrame.right, winFrame.bottom); + imeSource.setVisibleFrame(imeSource.getFrame()); + DisplayContentTests.performLayout(mDisplayContent); + + assertTrue(mImeWindow.isVisible()); + assertTrue(win.isVisible()); + + displayPolicy.beginPostLayoutPolicyLw(); + displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null); + displayPolicy.finishPostLayoutPolicyLw(); + + final var imeInsetsShown = win.getInsetsState().calculateInsets(win.getFrame(), ime(), + true); + assertEquals(new Rect(0, 0, 0, winFrame.bottom / 2), imeInsetsShown.toRect()); + + + // Now we're setting the excludedInsetsTypes for the IME. The IME is still showing, but + // in this case, InsetsPolicy#adjustVisibilityForIme will override and dispatch IME + // insets with zero height. + win.setExcludeInsetsTypes(ime()); + + displayPolicy.beginPostLayoutPolicyLw(); + displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null); + displayPolicy.finishPostLayoutPolicyLw(); + + final var imeInsetsHidden = win.getInsetsState().calculateInsets(win.getFrame(), ime(), + true); + assertEquals(Insets.NONE, imeInsetsHidden); + } + + private WindowState addNavigationBar() { final Binder owner = new Binder(); final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index c1edae91aabb..c30f70ee2903 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -183,15 +183,32 @@ public class SizeCompatTests extends WindowTestsBase { DeviceConfig.setProperties(mInitialConstrainDisplayApisFlags); } - private void setUpApp(DisplayContent display) { - mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build(); - mActivity = mTask.getTopNonFinishingActivity(); + private ActivityRecord setUpApp(DisplayContent display) { + return setUpApp(display, null /* appBuilder */); + } + + private ActivityRecord setUpApp(DisplayContent display, ActivityBuilder appBuilder) { + // Use the real package name (com.android.frameworks.wmtests) so that + // EnableCompatChanges/DisableCompatChanges can take effect. + // Otherwise the fake WindowTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME will make + // PlatformCompat#isChangeEnabledByPackageName always return default value. + final ComponentName componentName = ComponentName.createRelative( + mContext, SizeCompatTests.class.getName()); + mTask = new TaskBuilder(mSupervisor).setDisplay(display).setComponent(componentName) + .build(); + final ActivityBuilder builder = appBuilder != null ? appBuilder : new ActivityBuilder(mAtm); + mActivity = builder.setTask(mTask).setComponent(componentName).build(); doReturn(false).when(mActivity).isImmersiveMode(any()); + return mActivity; + } + + private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh) { + return setUpDisplaySizeWithApp(dw, dh, null /* appBuilder */); } - private void setUpDisplaySizeWithApp(int dw, int dh) { + private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh, ActivityBuilder appBuilder) { final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm, dw, dh); - setUpApp(builder.build()); + return setUpApp(builder.build(), appBuilder); } @Test @@ -352,15 +369,13 @@ public class SizeCompatTests extends WindowTestsBase { .setCanRotate(false) .setNotch(cutoutHeight) .build(); - setUpApp(display); display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mWm.mAppCompatConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); mWm.mAppCompatConfiguration.setIsVerticalReachabilityEnabled(true); - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) - .build(); + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)); doReturn(true).when(activity).isImmersiveMode(any()); addWindowToActivity(activity); @@ -424,17 +439,16 @@ public class SizeCompatTests extends WindowTestsBase { @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() { - final int notchHeight = 100; - setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build()); - - final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds(); final float aspectRatio = 1.2f; - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm) .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) .setMinAspectRatio(aspectRatio) .setMaxAspectRatio(aspectRatio) - .setResizeMode(RESIZE_MODE_UNRESIZEABLE) - .build(); + .setResizeMode(RESIZE_MODE_UNRESIZEABLE); + final int notchHeight = 100; + final ActivityRecord activity = setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800) + .setNotch(notchHeight).build(), activityBuilder); + final Rect displayBounds = activity.mDisplayContent.getWindowConfiguration().getBounds(); final Rect appBounds = activity.getWindowConfiguration().getAppBounds(); // The parent configuration doesn't change since the first resolved configuration, so the @@ -1327,12 +1341,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioSmall_overridden() { final int dh = 1200; final int dw = 1000; - setUpDisplaySizeWithApp(dw, dh); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); final Rect bounds = activity.getBounds(); assertEquals(dh, bounds.height()); @@ -1346,13 +1356,9 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioSmall_notOverridden() { final int dh = 1200; final int dw = 1000; - setUpDisplaySizeWithApp(dw, dh); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)); // Activity's requested aspect ratio is larger than OVERRIDE_MIN_ASPECT_RATIO_SMALL, // so OVERRIDE_MIN_ASPECT_RATIO_SMALL is ignored. @@ -1366,12 +1372,8 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) public void testOverrideMinAspectRatioMedium() { - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1388,13 +1390,8 @@ public class SizeCompatTests extends WindowTestsBase { final int notchHeight = 200; final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh) .setNotch(notchHeight).setSystemDecorations(true).build(); - mTask = new TaskBuilder(mSupervisor).setDisplay(display).build(); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setMinAspectRatio(2f) - .build(); + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).setMinAspectRatio(2f)); // The per-package override should have no effect, because the manifest aspect ratio is // larger (2:1) @@ -1411,13 +1408,9 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatioLargerThanManifest() { - setUpDisplaySizeWithApp(1400, 1600); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setMinAspectRatio(1.1f)); // The per-package override should have no effect, because the manifest aspect ratio is // larger (2:1) @@ -1430,12 +1423,8 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatioLarge() { - setUpDisplaySizeWithApp(1500, 1600); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1500, 1600, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 16:9 aspect ratio assertEquals(1600, activity.getBounds().height()); @@ -1449,13 +1438,8 @@ public class SizeCompatTests extends WindowTestsBase { ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatio_Both() { // If multiple override aspect ratios are set, we should use the largest one - - setUpDisplaySizeWithApp(1400, 1600); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 16:9 aspect ratio assertEquals(1600, activity.getBounds().height()); @@ -1470,11 +1454,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() { // In this test, the activity's orientation isn't fixed to portrait, therefore the override // isn't applied. - - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1497,13 +1477,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() { // In this test, the activity's orientation isn't fixed to portrait, therefore the override // isn't applied. - - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1524,12 +1499,8 @@ public class SizeCompatTests extends WindowTestsBase { ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() { - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1550,10 +1521,7 @@ public class SizeCompatTests extends WindowTestsBase { ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY}) public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() { - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1200, activity.getBounds().height()); @@ -1568,13 +1536,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() { // In this test, the activity's orientation isn't fixed to portrait, therefore the override // isn't applied. - - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)); // The per-package override forces the activity into a 3:2 aspect ratio assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, @@ -1588,12 +1551,8 @@ public class SizeCompatTests extends WindowTestsBase { // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without // OVERRIDE_MIN_ASPECT_RATIO being also set. - setUpDisplaySizeWithApp(1000, 1200); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)); // The per-package override should have no effect assertEquals(1200, activity.getBounds().height()); @@ -1604,12 +1563,8 @@ public class SizeCompatTests extends WindowTestsBase { @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() { - setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800, + new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); @@ -2462,10 +2417,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() { final int displayWidth = 1400; final int displayHeight = 1600; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2483,10 +2436,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() { final int displayWidth = 1600; final int displayHeight = 1400; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2505,10 +2456,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() { final int displayWidth = 1400; final int displayHeight = 1600; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2527,10 +2476,8 @@ public class SizeCompatTests extends WindowTestsBase { public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() { final int displayWidth = 1600; final int displayHeight = 1400; - setUpDisplaySizeWithApp(displayWidth, displayHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setMinAspectRatio(1.1f) - .build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight, + new ActivityBuilder(mAtm).setMinAspectRatio(1.1f)); // Setup Letterbox Configuration activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); @@ -2549,8 +2496,7 @@ public class SizeCompatTests extends WindowTestsBase { mAtm.mDevEnableNonResizableMultiWindow = true; final int screenWidth = 1800; final int screenHeight = 1000; - setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2585,8 +2531,7 @@ public class SizeCompatTests extends WindowTestsBase { mAtm.mDevEnableNonResizableMultiWindow = true; final int screenWidth = 1000; final int screenHeight = 1800; - setUpDisplaySizeWithApp(screenWidth, screenHeight); - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight); activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate real display with top insets. @@ -2616,18 +2561,18 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN}) public void testOverrideMinAspectRatioExcludePortraitFullscreen() { - setUpDisplaySizeWithApp(2600, 1600); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); - - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + resizeDisplay(mDisplayContent, 2600, 1600); + mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); // Non-resizable portrait activity - prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + final ActivityRecord activity = setUpApp(mDisplayContent, new ActivityBuilder(mAtm) + .setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // At first, OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply, because the // display is in landscape @@ -2645,16 +2590,16 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN}) public void testOverrideMinAspectRatioExcludePortraitFullscreenNotApplied() { // In this test, the activity is not in fullscreen, so the override is not applied - setUpDisplaySizeWithApp(2600, 1600); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); + resizeDisplay(mDisplayContent, 2600, 1600); + mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f); - // Create a size compat activity on the same task. - final ActivityRecord activity = getActivityBuilderOnSameTask().build(); + final ActivityRecord activity = setUpApp(mDisplayContent); final TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm, activity.getDisplayContent()); @@ -2983,6 +2928,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION}) public void testDisplayIgnoreOrientationRequest_orientationChangedToUnspecified() { // Set up a display in landscape and ignoring orientation request. setUpDisplaySizeWithApp(2800, 1400); @@ -3437,6 +3383,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION}) public void testSupportsNonResizableInSplitScreen_letterboxForAspectRatioRestriction() { // Support non resizable in multi window mAtm.mDevEnableNonResizableMultiWindow = true; @@ -4050,9 +3997,9 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION}) public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() { + final DisplayContent display = mDisplayContent; // Set up portrait close to square display. - setUpDisplaySizeWithApp(2200, 2280); - final DisplayContent display = mActivity.mDisplayContent; + resizeDisplay(display, 2200, 2280); display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape. final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, @@ -4067,9 +4014,8 @@ public class SizeCompatTests extends WindowTestsBase { display.sendNewConfiguration(); } - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); // Activity should not be letterboxed and should have portrait app bounds even though // orientation is not respected with insets as insets have been decoupled. @@ -4085,9 +4031,9 @@ public class SizeCompatTests extends WindowTestsBase { @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() { + final DisplayContent display = mDisplayContent; // Set up portrait close to square display - setUpDisplaySizeWithApp(2200, 2280); - final DisplayContent display = mActivity.mDisplayContent; + resizeDisplay(display, 2200, 2280); display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, @@ -4102,9 +4048,8 @@ public class SizeCompatTests extends WindowTestsBase { display.sendNewConfiguration(); } - final ActivityRecord activity = getActivityBuilderOnSameTask() - .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) - .build(); + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)); final Rect bounds = activity.getBounds(); // Activity should be letterboxed and should have portrait app bounds @@ -4116,9 +4061,9 @@ public class SizeCompatTests extends WindowTestsBase { @Test @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED}) public void testFixedAspectRatioAppInPortraitCloseToSquareDisplay_notInSizeCompat() { - setUpDisplaySizeWithApp(2200, 2280); - mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - final DisplayContent dc = mActivity.mDisplayContent; + final DisplayContent dc = mDisplayContent; + resizeDisplay(dc, 2200, 2280); + dc.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "navbar"); @@ -4132,11 +4077,10 @@ public class SizeCompatTests extends WindowTestsBase { dc.sendNewConfiguration(); } - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(dc, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE) - .build(); + .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)); // To force config to update again but with the same landscape orientation. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); @@ -4174,13 +4118,10 @@ public class SizeCompatTests extends WindowTestsBase { .setCutout(0, 100, 0, 150) .build(); - setUpApp(display); - - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setMaxAspectRatio(2.1f) - .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) - .build(); + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); // The activity height is 2100 and the display's app bounds height is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display @@ -4196,13 +4137,10 @@ public class SizeCompatTests extends WindowTestsBase { .setCutout(100, 0, 150, 0) .build(); - setUpApp(display); - - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setMaxAspectRatio(2.1f) - .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) - .build(); + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); // The activity width is 2100 and the display's app bounds width is 2250, so the activity // can be aligned inside parentAppBounds assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000)); @@ -4216,12 +4154,10 @@ public class SizeCompatTests extends WindowTestsBase { .setCutout(100, 0, 150, 0) .build(); - setUpApp(display); - final ActivityRecord activity = getActivityBuilderOnSameTask() + final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm) .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setMaxAspectRatio(2.1f) - .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED) - .build(); + .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)); // The activity width is 2100 and the display's app bounds width is 2050, so the activity // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display assertEquals(activity.getBounds(), display.getBounds()); @@ -5032,17 +4968,13 @@ public class SizeCompatTests extends WindowTestsBase { */ private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode, @ScreenOrientation int screenOrientation) { - return getActivityBuilderOnSameTask() + return getActivityBuilderWithoutTask().setTask(mTask) .setResizeMode(resizeMode) .setSupportsSizeChanges(supportsSizeChanges) .setScreenOrientation(screenOrientation) .build(); } - private ActivityBuilder getActivityBuilderOnSameTask() { - return getActivityBuilderWithoutTask().setTask(mTask); - } - private ActivityBuilder getActivityBuilderWithoutTask() { return new ActivityBuilder(mAtm) .setComponent(ComponentName.createRelative(mContext, diff --git a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt index 90dff47ab706..a1e165551b5b 100644 --- a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt +++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt @@ -24,17 +24,16 @@ import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any +import com.android.test.input.MockInputManagerRule import java.util.concurrent.Executor import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue import kotlin.test.fail -import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.anyInt import org.mockito.Mockito.doAnswer @@ -61,9 +60,8 @@ class InputDeviceBatteryListenerTest { private lateinit var context: Context private lateinit var inputManager: InputManager - @Mock - private lateinit var iInputManagerMock: IInputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession + @get:Rule + val inputManagerRule = MockInputManagerRule() @Before fun setUp() { @@ -72,7 +70,6 @@ class InputDeviceBatteryListenerTest { executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null monitoredDevices.clear() - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -92,7 +89,7 @@ class InputDeviceBatteryListenerTest { monitoredDevices.add(deviceId) registeredListener = listener null - }.`when`(iInputManagerMock).registerBatteryListener(anyInt(), any()) + }.`when`(inputManagerRule.mock).registerBatteryListener(anyInt(), any()) // Handle battery listener being unregistered. doAnswer { @@ -108,14 +105,7 @@ class InputDeviceBatteryListenerTest { if (monitoredDevices.isEmpty()) { registeredListener = null } - }.`when`(iInputManagerMock).unregisterBatteryListener(anyInt(), any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterBatteryListener(anyInt(), any()) } private fun notifyBatteryStateChanged( diff --git a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java index 080186e4a2c1..3fc9ce12e718 100644 --- a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java @@ -45,15 +45,14 @@ import android.view.InputDevice; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.test.input.MockInputManagerRule; + import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.Arrays; @@ -73,23 +72,22 @@ public class InputDeviceLightsManagerTest { private static final int DEVICE_ID = 1000; private static final int PLAYER_ID = 3; - @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule(); private InputManager mInputManager; - @Mock private IInputManager mIInputManagerMock; private InputManagerGlobal.TestSession mInputManagerGlobalSession; @Before public void setUp() throws Exception { final Context context = spy( new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); - when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); + when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); - when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( + when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn( createInputDevice(DEVICE_ID)); - mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); @@ -102,7 +100,7 @@ public class InputDeviceLightsManagerTest { lightStatesById.put(lightIds[i], lightStates[i]); } return null; - }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID), + }).when(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), any(int[].class), any(LightState[].class), any(IBinder.class)); doAnswer(invocation -> { @@ -111,7 +109,7 @@ public class InputDeviceLightsManagerTest { return lightStatesById.get(lightId); } return new LightState(0); - }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt()); + }).when(mInputManagerRule.getMock()).getLightState(eq(DEVICE_ID), anyInt()); } @After @@ -130,7 +128,7 @@ public class InputDeviceLightsManagerTest { private void mockLights(Light[] lights) throws Exception { // Mock the Lights returned form InputManagerService - when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn( + when(mInputManagerRule.getMock().getLights(eq(DEVICE_ID))).thenReturn( new ArrayList(Arrays.asList(lights))); } @@ -151,7 +149,7 @@ public class InputDeviceLightsManagerTest { LightsManager lightsManager = device.getLightsManager(); List<Light> lights = lightsManager.getLights(); - verify(mIInputManagerMock).getLights(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getLights(eq(DEVICE_ID)); assertEquals(lights, Arrays.asList(mockedLights)); } @@ -185,9 +183,9 @@ public class InputDeviceLightsManagerTest { .build()); IBinder token = session.getToken(); - verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID), any(String.class), eq(token)); - verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}), + verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}), eq(states), eq(token)); // Then all 3 should turn on. @@ -204,7 +202,7 @@ public class InputDeviceLightsManagerTest { // close session session.close(); - verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token)); } @Test @@ -232,9 +230,9 @@ public class InputDeviceLightsManagerTest { .build()); IBinder token = session.getToken(); - verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID), any(String.class), eq(token)); - verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}), + verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1}), eq(states), eq(token)); // Verify the light state @@ -245,7 +243,7 @@ public class InputDeviceLightsManagerTest { // close session session.close(); - verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token)); } @Test diff --git a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java index 0e3c200699d2..3057f5ddb540 100644 --- a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java @@ -41,16 +41,13 @@ import android.view.InputDevice; import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.annotations.GuardedBy; +import com.android.test.input.MockInputManagerRule; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -70,43 +67,34 @@ public class InputDeviceSensorManagerTest { private static final int DEVICE_ID = 1000; - @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Rule + public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule(); private InputManager mInputManager; private IInputSensorEventListener mIInputSensorEventListener; private final Object mLock = new Object(); - @Mock private IInputManager mIInputManagerMock; - private InputManagerGlobal.TestSession mInputManagerGlobalSession; - @Before public void setUp() throws Exception { final Context context = spy( new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); - mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); - when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); + when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); - when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( + when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn( createInputDeviceWithSensor(DEVICE_ID)); - when(mIInputManagerMock.getSensorList(eq(DEVICE_ID))).thenReturn(new InputSensorInfo[] { - createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER), - createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)}); + when(mInputManagerRule.getMock().getSensorList(eq(DEVICE_ID))).thenReturn( + new InputSensorInfo[]{ + createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER), + createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)}); - when(mIInputManagerMock.enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt())) + when(mInputManagerRule.getMock().enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt())) .thenReturn(true); - when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true); - } - - @After - public void tearDown() { - if (mInputManagerGlobalSession != null) { - mInputManagerGlobalSession.close(); - } + when(mInputManagerRule.getMock().registerSensorListener(any())).thenReturn(true); } private class InputTestSensorEventListener implements SensorEventListener { @@ -175,13 +163,13 @@ public class InputDeviceSensorManagerTest { SensorManager sensorManager = device.getSensorManager(); List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(1, accelList.size()); assertEquals(DEVICE_ID, accelList.get(0).getId()); assertEquals(Sensor.TYPE_ACCELEROMETER, accelList.get(0).getType()); List<Sensor> gyroList = sensorManager.getSensorList(Sensor.TYPE_GYROSCOPE); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(1, gyroList.size()); assertEquals(DEVICE_ID, gyroList.get(0).getId()); assertEquals(Sensor.TYPE_GYROSCOPE, gyroList.get(0).getType()); @@ -197,11 +185,11 @@ public class InputDeviceSensorManagerTest { List<Sensor> gameRotationList = sensorManager.getSensorList( Sensor.TYPE_GAME_ROTATION_VECTOR); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(0, gameRotationList.size()); List<Sensor> gravityList = sensorManager.getSensorList(Sensor.TYPE_GRAVITY); - verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID)); + verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID)); assertEquals(0, gravityList.size()); } @@ -218,13 +206,13 @@ public class InputDeviceSensorManagerTest { mIInputSensorEventListener = invocation.getArgument(0); assertNotNull(mIInputSensorEventListener); return true; - }).when(mIInputManagerMock).registerSensorListener(any()); + }).when(mInputManagerRule.getMock()).registerSensorListener(any()); InputTestSensorEventListener listener = new InputTestSensorEventListener(); assertTrue(sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)); - verify(mIInputManagerMock).registerSensorListener(any()); - verify(mIInputManagerMock).enableSensor(eq(DEVICE_ID), eq(sensor.getType()), + verify(mInputManagerRule.getMock()).registerSensorListener(any()); + verify(mInputManagerRule.getMock()).enableSensor(eq(DEVICE_ID), eq(sensor.getType()), anyInt(), anyInt()); float[] values = new float[] {0.12f, 9.8f, 0.2f}; @@ -240,7 +228,7 @@ public class InputDeviceSensorManagerTest { } sensorManager.unregisterListener(listener); - verify(mIInputManagerMock).disableSensor(eq(DEVICE_ID), eq(sensor.getType())); + verify(mInputManagerRule.getMock()).disableSensor(eq(DEVICE_ID), eq(sensor.getType())); } } diff --git a/tests/Input/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt index 152dde94f006..4c6bb849155c 100644 --- a/tests/Input/src/android/hardware/input/InputManagerTest.kt +++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt @@ -23,18 +23,16 @@ import android.view.Display import android.view.DisplayInfo import android.view.InputDevice import androidx.test.core.app.ApplicationProvider -import org.junit.After -import org.junit.Assert.assertNotNull +import com.android.test.input.MockInputManagerRule import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.eq import org.mockito.Mockito.`when` -import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnitRunner /** @@ -54,35 +52,23 @@ class InputManagerTest { } @get:Rule - val rule = MockitoJUnit.rule()!! + val inputManagerRule = MockInputManagerRule() private lateinit var devicesChangedListener: IInputDevicesChangedListener private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>() private lateinit var context: Context private lateinit var inputManager: InputManager - @Mock - private lateinit var iInputManager: IInputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) - `when`(iInputManager.inputDeviceIds).then { + `when`(inputManagerRule.mock.inputDeviceIds).then { deviceGenerationMap.keys.toIntArray() } } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun notifyDeviceChanged( deviceId: Int, associatedDisplayId: Int, @@ -92,7 +78,7 @@ class InputManagerTest { ?: throw IllegalArgumentException("Device $deviceId was never added!") deviceGenerationMap[deviceId] = generation - `when`(iInputManager.getInputDevice(deviceId)) + `when`(inputManagerRule.mock.getInputDevice(deviceId)) .thenReturn(createInputDevice(deviceId, associatedDisplayId, usiVersion, generation)) val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) } if (::devicesChangedListener.isInitialized) { @@ -125,7 +111,7 @@ class InputManagerTest { fun testUsiVersionFallBackToDisplayConfig() { addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null) - `when`(iInputManager.getHostUsiVersionFromDisplayConfig(eq(42))) + `when`(inputManagerRule.mock.getHostUsiVersionFromDisplayConfig(eq(42))) .thenReturn(HostUsiVersion(9, 8)) val usiVersion = inputManager.getHostUsiVersion(createDisplay(42)) assertEquals(HostUsiVersion(9, 8), usiVersion) diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt index 072341dcefae..e99c81493394 100644 --- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt @@ -18,20 +18,17 @@ package android.hardware.input import android.content.Context import android.content.ContextWrapper -import android.os.Handler import android.os.IBinder -import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` @@ -69,20 +66,16 @@ class KeyGestureEventHandlerTest { @get:Rule val rule = SetFlagsRule() + @get:Rule + val inputManagerRule = MockInputManagerRule() - private val testLooper = TestLooper() private var registeredListener: IKeyGestureHandler? = null private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -97,7 +90,7 @@ class KeyGestureEventHandlerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerKeyGestureHandler(any()) + }.`when`(inputManagerRule.mock).registerKeyGestureHandler(any()) // Handle key gesture handler being unregistered. doAnswer { @@ -108,14 +101,7 @@ class KeyGestureEventHandlerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterKeyGestureHandler(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(any()) } private fun handleKeyGestureEvent(event: KeyGestureEvent) { diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt index ca9de6000a5a..cf0bfcc4f6df 100644 --- a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt @@ -26,20 +26,19 @@ import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.fail /** * Tests for [InputManager.KeyGestureEventListener]. @@ -63,21 +62,18 @@ class KeyGestureEventListenerTest { @get:Rule val rule = SetFlagsRule() + @get:Rule + val inputManagerRule = MockInputManagerRule() private val testLooper = TestLooper() private val executor = HandlerExecutor(Handler(testLooper.looper)) private var registeredListener: IKeyGestureEventListener? = null private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -92,7 +88,7 @@ class KeyGestureEventListenerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerKeyGestureEventListener(any()) + }.`when`(inputManagerRule.mock).registerKeyGestureEventListener(any()) // Handle key gesture event listener being unregistered. doAnswer { @@ -103,14 +99,7 @@ class KeyGestureEventListenerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterKeyGestureEventListener(any()) } private fun notifyKeyGestureEvent(event: KeyGestureEvent) { diff --git a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt index 23135b2550b0..d25dee1d402c 100644 --- a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt @@ -24,22 +24,20 @@ import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule +import java.util.concurrent.Executor +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` -import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnitRunner -import java.util.concurrent.Executor -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.fail /** * Tests for [InputManager.KeyboardBacklightListener]. @@ -50,23 +48,19 @@ import kotlin.test.fail @Presubmit @RunWith(MockitoJUnitRunner::class) class KeyboardBacklightListenerTest { + @get:Rule - val rule = MockitoJUnit.rule()!! + val inputManagerRule = MockInputManagerRule() private lateinit var testLooper: TestLooper private var registeredListener: IKeyboardBacklightListener? = null private lateinit var executor: Executor private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) testLooper = TestLooper() executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null @@ -84,7 +78,7 @@ class KeyboardBacklightListenerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerKeyboardBacklightListener(any()) + }.`when`(inputManagerRule.mock).registerKeyboardBacklightListener(any()) // Handle keyboard backlight listener being unregistered. doAnswer { @@ -95,14 +89,7 @@ class KeyboardBacklightListenerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterKeyboardBacklightListener(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterKeyboardBacklightListener(any()) } private fun notifyKeyboardBacklightChanged( diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt index bcd56ad0c669..1c2a0538e552 100644 --- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt +++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt @@ -27,21 +27,20 @@ import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.server.testutils.any -import org.junit.After +import com.android.test.input.MockInputManagerRule +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import kotlin.test.fail import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.doAnswer import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue -import kotlin.test.fail /** * Tests for [InputManager.StickyModifierStateListener]. @@ -59,21 +58,18 @@ class StickyModifierStateListenerTest { @get:Rule val rule = SetFlagsRule() + @get:Rule + val inputManagerRule = MockInputManagerRule() private val testLooper = TestLooper() private val executor = HandlerExecutor(Handler(testLooper.looper)) private var registeredListener: IStickyModifierStateListener? = null private lateinit var context: Context private lateinit var inputManager: InputManager - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession - - @Mock - private lateinit var iInputManagerMock: IInputManager @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -88,7 +84,7 @@ class StickyModifierStateListenerTest { } registeredListener = listener null - }.`when`(iInputManagerMock).registerStickyModifierStateListener(any()) + }.`when`(inputManagerRule.mock).registerStickyModifierStateListener(any()) // Handle sticky modifier state listener being unregistered. doAnswer { @@ -99,14 +95,7 @@ class StickyModifierStateListenerTest { } registeredListener = null null - }.`when`(iInputManagerMock).unregisterStickyModifierStateListener(any()) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + }.`when`(inputManagerRule.mock).unregisterStickyModifierStateListener(any()) } private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) { diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt index f2724e605553..044f11d6904c 100644 --- a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt +++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt @@ -27,7 +27,6 @@ import android.hardware.input.HostUsiVersion import android.hardware.input.IInputDeviceBatteryListener import android.hardware.input.IInputDeviceBatteryState import android.hardware.input.IInputDevicesChangedListener -import android.hardware.input.IInputManager import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal import android.os.Binder @@ -42,13 +41,13 @@ import com.android.server.input.BatteryController.BluetoothBatteryManager.Blueto import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS import com.android.server.input.BatteryController.UEventBatteryListener import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS +import com.android.test.input.MockInputManagerRule import org.hamcrest.Description import org.hamcrest.Matcher import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers import org.hamcrest.TypeSafeMatcher import org.hamcrest.core.IsEqual.equalTo -import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -184,12 +183,12 @@ class BatteryControllerTests { @get:Rule val rule = MockitoJUnit.rule()!! + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock private lateinit var native: NativeInputManagerService @Mock - private lateinit var iInputManager: IInputManager - @Mock private lateinit var uEventManager: UEventManager @Mock private lateinit var bluetoothBatteryManager: BluetoothBatteryManager @@ -205,10 +204,9 @@ class BatteryControllerTests { fun setup() { context = TestableContext(ApplicationProvider.getApplicationContext()) testLooper = TestLooper() - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) context.addMockSystemService(InputManager::class.java, inputManager) - `when`(iInputManager.inputDeviceIds).then { + `when`(inputManagerRule.mock.inputDeviceIds).then { deviceGenerationMap.keys.toIntArray() } addInputDevice(DEVICE_ID) @@ -218,18 +216,11 @@ class BatteryControllerTests { bluetoothBatteryManager) batteryController.systemRunning() val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java) - verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture()) + verify(inputManagerRule.mock).registerInputDevicesChangedListener(listenerCaptor.capture()) devicesChangedListener = listenerCaptor.value testLooper.dispatchAll() } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun notifyDeviceChanged( deviceId: Int, hasBattery: Boolean = true, @@ -239,7 +230,7 @@ class BatteryControllerTests { ?: throw IllegalArgumentException("Device $deviceId was never added!") deviceGenerationMap[deviceId] = generation - `when`(iInputManager.getInputDevice(deviceId)) + `when`(inputManagerRule.mock.getInputDevice(deviceId)) .thenReturn(createInputDevice(deviceId, hasBattery, supportsUsi, generation)) val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) } if (::devicesChangedListener.isInitialized) { @@ -657,9 +648,9 @@ class BatteryControllerTests { @Test fun testRegisterBluetoothListenerForMonitoredBluetoothDevices() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn("11:22:33:44:55:66") addInputDevice(BT_DEVICE_ID) testLooper.dispatchNext() @@ -686,7 +677,7 @@ class BatteryControllerTests { batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID) verify(bluetoothBatteryManager, never()).removeBatteryListener(any()) - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn(null) notifyDeviceChanged(SECOND_BT_DEVICE_ID) testLooper.dispatchNext() @@ -695,7 +686,7 @@ class BatteryControllerTests { @Test fun testNotifiesBluetoothBatteryChanges() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) addInputDevice(BT_DEVICE_ID) @@ -716,7 +707,7 @@ class BatteryControllerTests { @Test fun testBluetoothBatteryIsPrioritized() { `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device") - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98) @@ -745,7 +736,7 @@ class BatteryControllerTests { @Test fun testFallBackToNativeBatteryStateWhenBluetoothStateInvalid() { `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device") - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98) @@ -776,9 +767,9 @@ class BatteryControllerTests { @Test fun testRegisterBluetoothMetadataListenerForMonitoredBluetoothDevices() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn("11:22:33:44:55:66") addInputDevice(BT_DEVICE_ID) testLooper.dispatchNext() @@ -811,7 +802,7 @@ class BatteryControllerTests { verify(bluetoothBatteryManager) .removeMetadataListener("AA:BB:CC:DD:EE:FF", metadataListener1.value) - `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID)) .thenReturn(null) notifyDeviceChanged(SECOND_BT_DEVICE_ID) testLooper.dispatchNext() @@ -821,7 +812,7 @@ class BatteryControllerTests { @Test fun testNotifiesBluetoothMetadataBatteryChanges() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF", BluetoothDevice.METADATA_MAIN_BATTERY)) @@ -861,7 +852,7 @@ class BatteryControllerTests { @Test fun testBluetoothMetadataBatteryIsPrioritized() { - `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) + `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID)) .thenReturn("AA:BB:CC:DD:EE:FF") `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21) `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF", diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index 351ec4635977..927958eb62cc 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -93,14 +93,12 @@ class InputManagerServiceTests { ) } - @JvmField - @Rule + @get:Rule val extendedMockitoRule = ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java) .mockStatic(PermissionChecker::class.java).build()!! - @JvmField - @Rule + @get:Rule val setFlagsRule = SetFlagsRule() @get:Rule diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 4ae06a4f9812..7526737f60bf 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -46,6 +46,7 @@ import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule import junitparams.JUnitParamsRunner import junitparams.Parameters +import org.junit.After import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -128,6 +129,13 @@ class KeyGestureControllerTests { currentPid = Process.myPid() } + @After + fun teardown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + private fun setupBehaviors() { Mockito.`when`( resources.getBoolean( diff --git a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt index f74fd723d540..4f4c97bef4c0 100644 --- a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt +++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt @@ -18,16 +18,18 @@ package com.android.server.input import android.content.Context import android.content.ContextWrapper -import android.hardware.input.IInputManager import android.hardware.input.InputManager -import android.hardware.input.InputManagerGlobal import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.provider.Settings import android.view.InputDevice import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider -import org.junit.After +import com.android.test.input.MockInputManagerRule +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule @@ -35,10 +37,6 @@ import org.junit.Test import org.mockito.Mock import org.mockito.Mockito import org.mockito.junit.MockitoJUnit -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException -import java.io.InputStream private fun createKeyboard(deviceId: Int): InputDevice = InputDevice.Builder() @@ -73,15 +71,15 @@ class KeyRemapperTests { @get:Rule val rule = MockitoJUnit.rule()!! - @Mock - private lateinit var iInputManager: IInputManager + @get:Rule + val inputManagerRule = MockInputManagerRule() + @Mock private lateinit var native: NativeInputManagerService private lateinit var mKeyRemapper: KeyRemapper private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setup() { @@ -104,25 +102,17 @@ class KeyRemapperTests { dataStore, testLooper.looper ) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) - Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) - } - - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } + Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) } @Test fun testKeyRemapping_whenRemappingEnabled() { ModifierRemappingFlag(true).use { val keyboard = createKeyboard(DEVICE_ID) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard) for (i in REMAPPABLE_KEYS.indices) { val fromKeyCode = REMAPPABLE_KEYS[i] @@ -160,7 +150,7 @@ class KeyRemapperTests { fun testKeyRemapping_whenRemappingDisabled() { ModifierRemappingFlag(false).use { val keyboard = createKeyboard(DEVICE_ID) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard) mKeyRemapper.remapKey(REMAPPABLE_KEYS[0], REMAPPABLE_KEYS[1]) testLooper.dispatchAll() diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt index 59aa96c46336..58fb4e1ed103 100644 --- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -20,11 +20,9 @@ import android.animation.ValueAnimator import android.content.Context import android.content.ContextWrapper import android.graphics.Color -import android.hardware.input.IInputManager import android.hardware.input.IKeyboardBacklightListener import android.hardware.input.IKeyboardBacklightState import android.hardware.input.InputManager -import android.hardware.input.InputManagerGlobal import android.hardware.lights.Light import android.os.UEventObserver import android.os.test.TestLooper @@ -35,7 +33,11 @@ import androidx.test.core.app.ApplicationProvider import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS -import org.junit.After +import com.android.test.input.MockInputManagerRule +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotEquals @@ -52,10 +54,6 @@ import org.mockito.Mockito.eq import org.mockito.Mockito.spy import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException -import java.io.InputStream private fun createKeyboard(deviceId: Int): InputDevice = InputDevice.Builder() @@ -100,10 +98,10 @@ class KeyboardBacklightControllerTests { @get:Rule val rule = MockitoJUnit.rule()!! + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock - private lateinit var iInputManager: IInputManager - @Mock private lateinit var native: NativeInputManagerService @Mock private lateinit var uEventManager: UEventManager @@ -111,7 +109,6 @@ class KeyboardBacklightControllerTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private var lightColorMap: HashMap<Int, Int> = HashMap() private var lastBacklightState: KeyboardBacklightState? = null private var sysfsNodeChanges = 0 @@ -134,10 +131,9 @@ class KeyboardBacklightControllerTests { testLooper = TestLooper() keyboardBacklightController = KeyboardBacklightController(context, native, dataStore, testLooper.looper, FakeAnimatorFactory(), uEventManager) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) - `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) + `when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) `when`(native.setLightColor(anyInt(), anyInt(), anyInt())).then { val args = it.arguments lightColorMap.put(args[1] as Int, args[2] as Int) @@ -152,13 +148,6 @@ class KeyboardBacklightControllerTests { } } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - @Test fun testKeyboardBacklightIncrementDecrement() { KeyboardBacklightFlags( @@ -168,8 +157,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, @@ -186,8 +176,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithoutBacklight = createKeyboard(DEVICE_ID) val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithoutBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) @@ -205,8 +196,9 @@ class KeyboardBacklightControllerTests { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn( + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn( listOf( keyboardBacklight, keyboardInputLight @@ -230,8 +222,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) { dataStore.setKeyboardBacklightBrightness( @@ -263,7 +256,8 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, @@ -278,7 +272,7 @@ class KeyboardBacklightControllerTests { lightColorMap.isEmpty() ) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceChanged(DEVICE_ID) keyboardBacklightController.notifyUserActivity() testLooper.dispatchNext() @@ -300,8 +294,9 @@ class KeyboardBacklightControllerTests { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1 - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) // Register backlight listener @@ -352,8 +347,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, @@ -388,8 +384,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, @@ -482,8 +479,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) @@ -511,8 +509,9 @@ class KeyboardBacklightControllerTests { val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, @@ -531,8 +530,9 @@ class KeyboardBacklightControllerTests { val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) } val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, @@ -551,8 +551,9 @@ class KeyboardBacklightControllerTests { val suggestedLevels = intArrayOf(22, 63, 135, 196) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) // Framework will add the lowest and maximum levels if not provided via config @@ -572,8 +573,10 @@ class KeyboardBacklightControllerTests { val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, suggestedLevels) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)) + .thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) // Framework will drop out of bound levels in the config @@ -591,8 +594,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, @@ -619,8 +623,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) @@ -642,8 +647,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) sendAmbientBacklightValue(1) assertEquals( @@ -671,8 +677,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) sendAmbientBacklightValue(254) assertEquals( @@ -701,8 +708,9 @@ class KeyboardBacklightControllerTests { ).use { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)) + .thenReturn(keyboardWithBacklight) + `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) incrementKeyboardBacklight(DEVICE_ID) assertEquals( diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt index c073c7aae678..ff8a9ba94353 100644 --- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt @@ -23,9 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.content.pm.ServiceInfo -import android.hardware.input.IInputManager import android.hardware.input.InputManager -import android.hardware.input.InputManagerGlobal import android.hardware.input.KeyGlyphMap.KeyCombination import android.os.Bundle import android.os.test.TestLooper @@ -36,8 +34,8 @@ import android.view.InputDevice import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider import com.android.hardware.input.Flags +import com.android.test.input.MockInputManagerRule import com.android.test.input.R -import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull @@ -65,30 +63,24 @@ class KeyboardGlyphManagerTests { const val RECEIVER_NAME = "DummyReceiver" } - @JvmField - @Rule(order = 0) + @get:Rule val setFlagsRule = SetFlagsRule() - - @JvmField - @Rule(order = 1) + @get:Rule val mockitoRule = MockitoJUnit.rule()!! + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock private lateinit var packageManager: PackageManager - @Mock - private lateinit var iInputManager: IInputManager - private lateinit var keyboardGlyphManager: KeyboardGlyphManager private lateinit var context: Context private lateinit var testLooper: TestLooper - private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private lateinit var keyboardDevice: InputDevice @Before fun setup() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) testLooper = TestLooper() keyboardGlyphManager = KeyboardGlyphManager(context, testLooper.looper) @@ -98,21 +90,14 @@ class KeyboardGlyphManagerTests { testLooper.dispatchAll() } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun setupInputDevices() { val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "") - Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) + Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) } private fun setupBroadcastReceiver() { diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index 301c0e6a159f..d6654cceb458 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -24,11 +24,10 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.content.pm.ServiceInfo -import android.hardware.input.KeyboardLayoutSelectionResult -import android.hardware.input.IInputManager import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal import android.hardware.input.KeyboardLayout +import android.hardware.input.KeyboardLayoutSelectionResult import android.icu.util.ULocale import android.os.Bundle import android.os.test.TestLooper @@ -42,8 +41,12 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.os.KeyboardConfiguredProto import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.test.input.MockInputManagerRule import com.android.test.input.R -import org.junit.After +import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Assert.assertTrue @@ -53,10 +56,6 @@ import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito -import java.io.FileNotFoundException -import java.io.FileOutputStream -import java.io.IOException -import java.io.InputStream fun createKeyboard( deviceId: Int, @@ -120,8 +119,8 @@ class KeyboardLayoutManagerTests { val extendedMockitoRule = ExtendedMockitoRule.Builder(this) .mockStatic(FrameworkStatsLog::class.java).build()!! - @Mock - private lateinit var iInputManager: IInputManager + @get:Rule + val inputManagerRule = MockInputManagerRule() @Mock private lateinit var native: NativeInputManagerService @@ -148,7 +147,6 @@ class KeyboardLayoutManagerTests { @Before fun setup() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) dataStore = PersistentDataStore(object : PersistentDataStore.Injector() { override fun openRead(): InputStream? { throw FileNotFoundException() @@ -171,13 +169,6 @@ class KeyboardLayoutManagerTests { setupIme() } - @After - fun tearDown() { - if (this::inputManagerGlobalSession.isInitialized) { - inputManagerGlobalSession.close() - } - } - private fun setupInputDevices() { val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) @@ -191,19 +182,19 @@ class KeyboardLayoutManagerTests { DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "dvorak") englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "qwerty") - Mockito.`when`(iInputManager.inputDeviceIds) + Mockito.`when`(inputManagerRule.mock.inputDeviceIds) .thenReturn(intArrayOf( DEVICE_ID, VENDOR_SPECIFIC_DEVICE_ID, ENGLISH_DVORAK_DEVICE_ID, ENGLISH_QWERTY_DEVICE_ID )) - Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) - Mockito.`when`(iInputManager.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) + Mockito.`when`(inputManagerRule.mock.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID)) .thenReturn(vendorSpecificKeyboardDevice) - Mockito.`when`(iInputManager.getInputDevice(ENGLISH_DVORAK_DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_DVORAK_DEVICE_ID)) .thenReturn(englishDvorakKeyboardDevice) - Mockito.`when`(iInputManager.getInputDevice(ENGLISH_QWERTY_DEVICE_ID)) + Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_QWERTY_DEVICE_ID)) .thenReturn(englishQwertyKeyboardDevice) } diff --git a/tests/Input/src/com/android/test/input/MockInputManagerRule.kt b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt new file mode 100644 index 000000000000..cef985600c40 --- /dev/null +++ b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test.input + +import android.hardware.input.IInputManager +import android.hardware.input.InputManagerGlobal +import org.junit.rules.ExternalResource +import org.mockito.Mockito + +/** + * A test rule that temporarily replaces the [IInputManager] connection to the server with a mock + * to be used for testing. + */ +class MockInputManagerRule : ExternalResource() { + + private lateinit var session: InputManagerGlobal.TestSession + + val mock: IInputManager = Mockito.mock(IInputManager::class.java) + + override fun before() { + session = InputManagerGlobal.createTestSession(mock) + } + + override fun after() { + if (this::session.isInitialized) { + session.close() + } + } +} diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt index 9f4df90422eb..c61a25021949 100644 --- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt +++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt @@ -39,7 +39,6 @@ import com.android.cts.input.inputeventmatchers.withSource import junit.framework.Assert.fail import org.hamcrest.Matchers.allOf import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TestName @@ -108,7 +107,6 @@ class UinputRecordingIntegrationTests { parser = InputJsonParser(instrumentation.context) } - @Ignore("b/366602644") @Test fun testEvemuRecording() { VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>( diff --git a/tests/testables/src/android/testing/TestWithLooperRule.java b/tests/testables/src/android/testing/TestWithLooperRule.java index 10df17f991d3..6a8e142e2314 100644 --- a/tests/testables/src/android/testing/TestWithLooperRule.java +++ b/tests/testables/src/android/testing/TestWithLooperRule.java @@ -100,6 +100,9 @@ public class TestWithLooperRule implements MethodRule { case "ExpectException": next = this.getNextStatement(next, "next"); break; + case "UiThreadStatement": + next = this.getNextStatement(next, "base"); + break; default: throw new Exception( String.format("Unexpected Statement received: [%s]", diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp index 37b1687fd3f1..5983cf165839 100644 --- a/tools/aapt2/SdkConstants.cpp +++ b/tools/aapt2/SdkConstants.cpp @@ -28,8 +28,19 @@ using namespace std::literals; namespace aapt { static constexpr ApiVersion sDevelopmentSdkLevel = 10000; + +// clang-format off static constexpr StringPiece sDevelopmentSdkCodeNames[] = { - "Q"sv, "R"sv, "S"sv, "Sv2"sv, "Tiramisu"sv, "UpsideDownCake"sv, "VanillaIceCream"sv}; + "Q"sv, + "R"sv, + "S"sv, + "Sv2"sv, + "Tiramisu"sv, + "UpsideDownCake"sv, + "VanillaIceCream"sv, + "Baklava"sv, +}; +// clang-format on static constexpr auto sPrivacySandboxSuffix = "PrivacySandbox"sv; diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore deleted file mode 100644 index 6453bdef8cee..000000000000 --- a/tools/hoststubgen/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -out/ -*-out/ -*.log diff --git a/tools/hoststubgen/OWNERS b/tools/hoststubgen/OWNERS deleted file mode 100644 index 3d8888d83cf4..000000000000 --- a/tools/hoststubgen/OWNERS +++ /dev/null @@ -1 +0,0 @@ -file:platform/frameworks/base:/ravenwood/OWNERS diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING deleted file mode 100644 index 856e6eefba15..000000000000 --- a/tools/hoststubgen/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "imports": [ - { - "path": "frameworks/base/ravenwood" - } - ] -} diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore deleted file mode 100644 index 0f384074ed7f..000000000000 --- a/tools/hoststubgen/hoststubgen/.gitignore +++ /dev/null @@ -1 +0,0 @@ -framework-all-stub-out
\ No newline at end of file |