diff options
661 files changed, 15000 insertions, 9089 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9f90425d0591..250a1d1f86c3 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -4161,6 +4161,7 @@ package android.window { public static class WindowInfosListenerForTest.WindowInfo { field @NonNull public final android.graphics.Rect bounds; + field public final int displayId; field public final boolean isTrustedOverlay; field public final boolean isVisible; field @NonNull public final String name; diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index 2198fcd70637..f67c68eb044d 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -111,20 +111,20 @@ public class AnimatorInflater { float pathErrorScale) throws NotFoundException { final ConfigurationBoundResourceCache<Animator> animatorCache = resources .getAnimatorCache(); - ConfigurationBoundResourceCache.Entry<Animator> animatorEntry = - animatorCache.getInstance(id, resources, theme); - if (animatorEntry.hasValue()) { + Animator animator = animatorCache.getInstance(id, resources, theme); + if (animator != null) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); } - return animatorEntry.getValue(); + return animator; } else if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "cache miss for animator " + resources.getResourceName(id)); } + int cacheGeneration = animatorCache.getGeneration(); XmlResourceParser parser = null; try { parser = resources.getAnimation(id); - Animator animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); + animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<Animator> constantState = animator.createConstantState(); @@ -132,7 +132,7 @@ public class AnimatorInflater { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "caching animator for res " + resources.getResourceName(id)); } - animatorCache.put(id, theme, constantState, animatorEntry.getGeneration()); + animatorCache.put(id, theme, constantState, cacheGeneration); // create a new animator so that cached version is never used by the user animator = constantState.newInstance(resources, theme); } @@ -161,22 +161,22 @@ public class AnimatorInflater { final ConfigurationBoundResourceCache<StateListAnimator> cache = resources .getStateListAnimatorCache(); final Theme theme = context.getTheme(); - ConfigurationBoundResourceCache.Entry<StateListAnimator> animatorEntry = - cache.getInstance(id, resources, theme); - if (animatorEntry.hasValue()) { - return animatorEntry.getValue(); + StateListAnimator animator = cache.getInstance(id, resources, theme); + if (animator != null) { + return animator; } + int cacheGeneration = cache.getGeneration(); XmlResourceParser parser = null; try { parser = resources.getAnimation(id); - StateListAnimator animator = + animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<StateListAnimator> constantState = animator .createConstantState(); if (constantState != null) { - cache.put(id, theme, constantState, animatorEntry.getGeneration()); + cache.put(id, theme, constantState, cacheGeneration); // return a clone so that the animator in constant state is never used. animator = constantState.newInstance(resources, theme); } diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index b710644a308c..5f5a7dfe24c6 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; +import android.os.Trace; import android.util.Log; import android.util.MathUtils; import android.util.Size; @@ -144,6 +145,7 @@ public final class WallpaperColors implements Parcelable { throw new IllegalArgumentException("Drawable cannot be null"); } + Trace.beginSection("WallpaperColors#fromDrawable"); Rect initialBounds = drawable.copyBounds(); int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); @@ -165,6 +167,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); drawable.setBounds(initialBounds); + Trace.endSection(); return colors; } @@ -195,7 +198,7 @@ public final class WallpaperColors implements Parcelable { public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap, @FloatRange (from = 0f, to = 1f) float dimAmount) { Objects.requireNonNull(bitmap, "Bitmap can't be null"); - + Trace.beginSection("WallpaperColors#fromBitmap"); final int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); boolean shouldRecycle = false; if (bitmapArea > MAX_WALLPAPER_EXTRACTION_AREA) { @@ -247,6 +250,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); } + Trace.endSection(); return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints); } @@ -462,7 +466,7 @@ public final class WallpaperColors implements Parcelable { * Gets the most visually representative color of the wallpaper. * "Visually representative" means easily noticeable in the image, * probably happening at high frequency. - * + *fromBitmap * @return A color. */ public @NonNull Color getPrimaryColor() { @@ -545,6 +549,7 @@ public final class WallpaperColors implements Parcelable { return 0; } + Trace.beginSection("WallpaperColors#calculateDarkHints"); dimAmount = MathUtils.saturate(dimAmount); int[] pixels = new int[source.getWidth() * source.getHeight()]; double totalLuminance = 0; @@ -607,6 +612,7 @@ public final class WallpaperColors implements Parcelable { " maxD: " + maxDarkPixels + " numPixels: " + pixels.length); } + Trace.endSection(); return hints; } diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java index ed53967b86e4..ffc4480f81a2 100644 --- a/core/java/android/app/admin/WifiSsidPolicy.java +++ b/core/java/android/app/admin/WifiSsidPolicy.java @@ -136,7 +136,7 @@ public final class WifiSsidPolicy implements Parcelable { } /** - * Two instances of WifiSsidPolicy is considered equal if they have + * Two instances of WifiSsidPolicy are considered equal if they have * the same WifiSsidPolicyType and the same set of WifiSsids */ @Override diff --git a/core/java/android/app/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java index f17b044f7996..4475fc56b92d 100644 --- a/core/java/android/app/smartspace/SmartspaceAction.java +++ b/core/java/android/app/smartspace/SmartspaceAction.java @@ -348,6 +348,10 @@ public final class SmartspaceAction implements Parcelable { */ @NonNull public SmartspaceAction build() { + if (mIcon != null) { + mIcon.convertToAshmem(); + } + return new SmartspaceAction(mId, mIcon, mTitle, mSubtitle, mContentDescription, mPendingIntent, mIntent, mUserHandle, mExtras); } diff --git a/core/java/android/app/smartspace/uitemplatedata/Icon.java b/core/java/android/app/smartspace/uitemplatedata/Icon.java index 6bdc926e7cf3..b9d90bfdb6e4 100644 --- a/core/java/android/app/smartspace/uitemplatedata/Icon.java +++ b/core/java/android/app/smartspace/uitemplatedata/Icon.java @@ -171,6 +171,7 @@ public final class Icon implements Parcelable { */ @NonNull public Icon build() { + mIcon.convertToAshmem(); return new Icon(mIcon, mContentDescription, mShouldTint); } } diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 2e672251cf68..4dea4a7e3aca 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -36,7 +36,6 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.companion.utils.FeatureUtils; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -1227,11 +1226,6 @@ public final class CompanionDeviceManager { @Nullable public IntentSender buildPermissionTransferUserConsentIntent(int associationId) throws DeviceNotAssociatedException { - if (!FeatureUtils.isPermSyncEnabled()) { - throw new UnsupportedOperationException("Calling" - + " buildPermissionTransferUserConsentIntent, but this API is disabled by the" - + " system."); - } try { PendingIntent pendingIntent = mService.buildPermissionTransferUserConsentIntent( mContext.getOpPackageName(), @@ -1264,10 +1258,6 @@ public final class CompanionDeviceManager { @Deprecated @UserHandleAware public void startSystemDataTransfer(int associationId) throws DeviceNotAssociatedException { - if (!FeatureUtils.isPermSyncEnabled()) { - throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this API" - + " is disabled by the system."); - } try { mService.startSystemDataTransfer(mContext.getOpPackageName(), mContext.getUserId(), associationId, null); @@ -1300,10 +1290,6 @@ public final class CompanionDeviceManager { @NonNull Executor executor, @NonNull OutcomeReceiver<Void, CompanionException> result) throws DeviceNotAssociatedException { - if (!FeatureUtils.isPermSyncEnabled()) { - throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this API" - + " is disabled by the system."); - } try { mService.startSystemDataTransfer(mContext.getOpPackageName(), mContext.getUserId(), associationId, new SystemDataTransferCallbackProxy(executor, result)); diff --git a/core/java/android/companion/utils/FeatureUtils.java b/core/java/android/companion/utils/FeatureUtils.java index 157eef82d24d..a382e09ae7b2 100644 --- a/core/java/android/companion/utils/FeatureUtils.java +++ b/core/java/android/companion/utils/FeatureUtils.java @@ -16,6 +16,7 @@ package android.companion.utils; +import android.os.Binder; import android.os.Build; import android.provider.DeviceConfig; @@ -31,8 +32,19 @@ public final class FeatureUtils { private static final String PROPERTY_PERM_SYNC_ENABLED = "perm_sync_enabled"; public static boolean isPermSyncEnabled() { - return Build.isDebuggable() || DeviceConfig.getBoolean(NAMESPACE_COMPANION, - PROPERTY_PERM_SYNC_ENABLED, false); + // Permissions sync is always enabled in debuggable mode. + if (Build.isDebuggable()) { + return true; + } + + // Clear app identity to read the device config for feature flag. + final long identity = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getBoolean(NAMESPACE_COMPANION, + PROPERTY_PERM_SYNC_ENABLED, false); + } finally { + Binder.restoreCallingIdentity(identity); + } } private FeatureUtils() { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c221d724c5a2..06635eedeb9a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -481,7 +481,7 @@ public abstract class Context { * If binding from a top app and its target SDK version is at or above * {@link android.os.Build.VERSION_CODES#R}, the app needs to * explicitly use BIND_INCLUDE_CAPABILITIES flag to pass all capabilities to the service so the - * other app can have while-use-use access such as location, camera, microphone from background. + * other app can have while-in-use access such as location, camera, microphone from background. * If binding from a top app and its target SDK version is below * {@link android.os.Build.VERSION_CODES#R}, BIND_INCLUDE_CAPABILITIES is implicit. */ @@ -678,7 +678,7 @@ public abstract class Context { * </p> * * <em>This flag is NOT compatible with {@link BindServiceFlags}. If you need to use - * {@link BindServiceFlags}, you must use {@link #BIND_EXTERNAL_SERVICE_LONG} instead. + * {@link BindServiceFlags}, you must use {@link #BIND_EXTERNAL_SERVICE_LONG} instead.</em> */ public static final int BIND_EXTERNAL_SERVICE = 0x80000000; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 960d10adbfbb..048289f56a0c 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2628,15 +2628,6 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - targetCode)) { - Slog.w(TAG, "Package requires development platform " + targetCode - + ", returning current version " + Build.VERSION.SDK_INT); - return Build.VERSION.SDK_INT; - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + targetCode @@ -2708,15 +2699,6 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - minCode)) { - Slog.w(TAG, "Package requires min development platform " + minCode - + ", returning current version " + Build.VERSION.SDK_INT); - return Build.VERSION.SDK_INT; - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + minCode diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index 8cc4cdb955ca..3e1c5bb3d7ec 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -316,15 +316,6 @@ public class FrameworkParsingPackageUtils { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - minCode)) { - Slog.w(TAG, "Parsed package requires min development platform " + minCode - + ", returning current version " + Build.VERSION.SDK_INT); - return input.success(Build.VERSION.SDK_INT); - } - // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, @@ -377,27 +368,19 @@ public class FrameworkParsingPackageUtils { return input.success(targetVers); } - // If it's a pre-release SDK and the codename matches this platform, it - // definitely targets this SDK. - if (matchTargetCode(platformSdkCodenames, targetCode)) { - return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); - } - - // STOPSHIP: hack for the pre-release SDK - if (platformSdkCodenames.length == 0 - && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( - targetCode)) { - Slog.w(TAG, "Parsed package requires development platform " + targetCode - + ", returning current version " + Build.VERSION.SDK_INT); - return input.success(Build.VERSION.SDK_INT); - } - try { if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } } catch (IllegalArgumentException e) { - return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK"); + // isAtMost() throws it when encountering an older SDK codename + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage()); + } + + // If it's a pre-release SDK and the codename matches this platform, it + // definitely targets this SDK. + if (matchTargetCode(platformSdkCodenames, targetCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } // Otherwise, we're looking at an incompatible pre-release SDK. diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java index 4da3c18883bd..5e10a5768358 100644 --- a/core/java/android/content/res/ConfigurationBoundResourceCache.java +++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java @@ -37,16 +37,16 @@ public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<Cons * @param key a key that uniquely identifies the drawable resource * @param resources a Resources object from which to create new instances. * @param theme the theme where the resource will be used - * @return an Entry wrapping a new instance of the resource, or {@code null} if not in + * @return a new instance of the resource, or {@code null} if not in * the cache */ - public Entry<T> getInstance(long key, Resources resources, Resources.Theme theme) { - final Entry<ConstantState<T>> e = get(key, theme); - if (e.hasValue()) { - return new Entry<>(e.getValue().newInstance(resources, theme), e.getGeneration()); + public T getInstance(long key, Resources resources, Resources.Theme theme) { + final ConstantState<T> entry = get(key, theme); + if (entry != null) { + return entry.newInstance(resources, theme); } - return new Entry<>(null, e.getGeneration()); + return null; } @Override diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java index b51d14a3c472..d0ebe3304065 100644 --- a/core/java/android/content/res/DrawableCache.java +++ b/core/java/android/content/res/DrawableCache.java @@ -40,32 +40,14 @@ class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Drawable getInstance(long key, Resources resources, Resources.Theme theme) { - final Entry<Drawable.ConstantState> entry = get(key, theme); - if (entry.getValue() != null) { - return entry.getValue().newDrawable(resources, theme); + final Drawable.ConstantState entry = get(key, theme); + if (entry != null) { + return entry.newDrawable(resources, theme); } return null; } - /** - * If the resource is cached, creates and returns a new instance of it. - * - * @param key a key that uniquely identifies the drawable resource - * @param resources a Resources object from which to create new instances. - * @param theme the theme where the resource will be used - * @return an Entry wrapping a a new instance of the resource, or {@code null} if not in - * the cache - */ - public Entry<Drawable> getDrawable(long key, Resources resources, Resources.Theme theme) { - final Entry<Drawable.ConstantState> e = get(key, theme); - if (e.hasValue()) { - return new Entry<>(e.getValue().newDrawable(resources, theme), e.getGeneration()); - } - - return new Entry<>(null, e.getGeneration()); - } - @Override public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) { return Configuration.needNewResources(configChanges, entry.getChangingConfigurations()); diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 25154d5c1623..1fdfcd067867 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -650,21 +650,16 @@ public class ResourcesImpl { key = (((long) value.assetCookie) << 32) | value.data; } - int cacheGeneration; + int cacheGeneration = caches.getGeneration(); // First, check whether we have a cached version of this drawable // that was inflated against the specified theme. Skip the cache if // we're currently preloading or we're not using the cache. if (!mPreloading && useCache) { - final ThemedResourceCache.Entry<Drawable> cachedDrawable = - caches.getDrawable(key, wrapper, theme); - if (cachedDrawable.hasValue()) { - cachedDrawable.getValue().setChangingConfigurations( - value.changingConfigurations); - return cachedDrawable.getValue(); + Drawable cachedDrawable = caches.getInstance(key, wrapper, theme); + if (cachedDrawable != null) { + cachedDrawable.setChangingConfigurations(value.changingConfigurations); + return cachedDrawable; } - cacheGeneration = cachedDrawable.getGeneration(); - } else { - cacheGeneration = ThemedResourceCache.UNDEFINED_GENERATION; } // Next, check preloaded drawables. Preloaded drawables may contain @@ -1009,16 +1004,15 @@ public class ResourcesImpl { TypedValue value, int id) { final long key = (((long) value.assetCookie) << 32) | value.data; final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache; - ThemedResourceCache.Entry<ComplexColor> complexColorEntry = - cache.getInstance(key, wrapper, theme); - if (complexColorEntry.hasValue()) { - return complexColorEntry.getValue(); + ComplexColor complexColor = cache.getInstance(key, wrapper, theme); + if (complexColor != null) { + return complexColor; } + int cacheGeneration = cache.getGeneration(); final android.content.res.ConstantState<ComplexColor> factory = sPreloadedComplexColors.get(key); - ComplexColor complexColor = null; if (factory != null) { complexColor = factory.newInstance(wrapper, theme); } @@ -1035,8 +1029,7 @@ public class ResourcesImpl { sPreloadedComplexColors.put(key, complexColor.getConstantState()); } } else { - cache.put(key, theme, complexColor.getConstantState(), - complexColorEntry.getGeneration()); + cache.put(key, theme, complexColor.getConstantState(), cacheGeneration); } } return complexColor; diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java index e7fd2755a941..a7cd168690b4 100644 --- a/core/java/android/content/res/ThemedResourceCache.java +++ b/core/java/android/content/res/ThemedResourceCache.java @@ -41,29 +41,6 @@ abstract class ThemedResourceCache<T> { private int mGeneration; - public static class Entry<S> { - private S mValue; - private int mGeneration; - - - public S getValue() { - return mValue; - } - - public boolean hasValue() { - return mValue != null; - } - - public int getGeneration() { - return mGeneration; - } - - Entry(S value, int generation) { - this.mValue = value; - this.mGeneration = generation; - } - } - /** * Adds a new theme-dependent entry to the cache. * @@ -109,6 +86,15 @@ abstract class ThemedResourceCache<T> { } /** + * Returns the current generation of the cache + * + * @return The current generation + */ + public int getGeneration() { + return mGeneration; + } + + /** * Returns an entry from the cache. * * @param key a key that uniquely identifies the entry @@ -116,7 +102,7 @@ abstract class ThemedResourceCache<T> { * @return a cached entry, or {@code null} if not in the cache */ @Nullable - public Entry get(long key, @Nullable Theme theme) { + public T get(long key, @Nullable Theme theme) { // The themed (includes null-themed) and unthemed caches are mutually // exclusive, so we'll give priority to whichever one we think we'll // hit first. Since most of the framework drawables are themed, that's @@ -126,7 +112,7 @@ abstract class ThemedResourceCache<T> { if (themedEntries != null) { final WeakReference<T> themedEntry = themedEntries.get(key); if (themedEntry != null) { - return new Entry(themedEntry.get(), mGeneration); + return themedEntry.get(); } } @@ -134,14 +120,15 @@ abstract class ThemedResourceCache<T> { if (unthemedEntries != null) { final WeakReference<T> unthemedEntry = unthemedEntries.get(key); if (unthemedEntry != null) { - return new Entry(unthemedEntry.get(), mGeneration); + return unthemedEntry.get(); } } } - return new Entry(null, mGeneration); + return null; } + /** * Prunes cache entries that have been invalidated by a configuration * change. diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 48b5cac2a519..d352be16ae0c 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -3202,8 +3202,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * or if the camera device isn't a primary rear/front camera, the minimum required output * stream configurations are the same as for applications targeting SDK version older than * 31.</p> - * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} and {@link android.hardware.camera2.CameraDevice#createCaptureSession } for additional mandatory - * stream configurations on a per-capability basis.</p> + * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} and + * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations } + * for additional mandatory stream configurations on a per-capability basis.</p> * <p>*1: For JPEG format, the sizes may be restricted by below conditions:</p> * <ul> * <li>The HAL may choose the aspect ratio of each Jpeg size to be one of well known ones @@ -3321,12 +3322,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL } * and {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES }. * This is an app-readable conversion of the mandatory stream combination - * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p> + * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations tables}.</p> * <p>The array of * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is * generated according to the documented - * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} based on - * specific device level and capabilities. + * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations guideline} based on specific device level and capabilities. * Clients can use the array as a quick reference to find an appropriate camera stream * combination. * As per documentation, the stream combinations with given PREVIEW, RECORD and @@ -3355,12 +3355,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>An array of mandatory concurrent stream combinations. * This is an app-readable conversion of the concurrent mandatory stream combination - * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p> + * {@link android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations tables}.</p> * <p>The array of * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is * generated according to the documented - * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each - * device which has its Id present in the set returned by + * {@link android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline} for each device which has its Id present in the set returned by * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds }. * Clients can use the array as a quick reference to find an appropriate camera stream * combination. @@ -3465,7 +3464,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>If a camera device supports multi-resolution output streams for a particular format, for * each of its mandatory stream combinations, the camera device will support using a * MultiResolutionImageReader for the MAXIMUM stream of supported formats. Refer to - * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for additional details.</p> + * {@link android.hardware.camera2.CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs } + * for additional details.</p> * <p>To use multi-resolution input streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputFormats }. * A reprocessable CameraCaptureSession can then be created using an {@link android.hardware.camera2.params.InputConfiguration InputConfiguration} constructed with * the input MultiResolutionStreamInfo group, queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputInfo }.</p> @@ -3473,8 +3473,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@code YUV} output, or multi-resolution {@code PRIVATE} input and multi-resolution * {@code PRIVATE} output, {@code JPEG} and {@code YUV} are guaranteed to be supported * multi-resolution output stream formats. Refer to - * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for - * details about the additional mandatory stream combinations in this case.</p> + * {@link android.hardware.camera2.CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs }} + * for details about the additional mandatory stream combinations in this case.</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> */ @PublicKey @@ -3586,12 +3586,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@link android.hardware.camera2.CaptureRequest } has {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set * to {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }. * This is an app-readable conversion of the maximum resolution mandatory stream combination - * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p> + * {@link android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors tables}.</p> * <p>The array of * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is * generated according to the documented - * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each - * device which has the + * {@link android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors guideline} for each device which has the * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability. * Clients can use the array as a quick reference to find an appropriate camera stream @@ -3614,12 +3613,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * 10-bit output capability * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT } * This is an app-readable conversion of the 10 bit output mandatory stream combination - * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p> + * {@link android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations tables}.</p> * <p>The array of * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is * generated according to the documented - * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each - * device which has the + * {@link android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations guideline} for each device which has the * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT } * capability. * Clients can use the array as a quick reference to find an appropriate camera stream @@ -3639,12 +3637,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>An array of mandatory stream combinations which are applicable when device lists * {@code PREVIEW_STABILIZATION} in {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes}. * This is an app-readable conversion of the preview stabilization mandatory stream - * combination {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p> + * combination + * {@link android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations tables}.</p> * <p>The array of * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is * generated according to the documented - * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each - * device which supports {@code PREVIEW_STABILIZATION} + * {@link android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations guideline} for each device which supports {@code PREVIEW_STABILIZATION} * Clients can use the array as a quick reference to find an appropriate camera stream * combination. * The mandatory stream combination array will be {@code null} in case the device does not @@ -3717,8 +3715,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>The guaranteed stream combinations related to stream use case for a camera device with * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE } * capability is documented in the camera device - * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline}. The - * application is strongly recommended to use one of the guaranteed stream combinations. + * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline}. The application is strongly recommended to use one of the guaranteed stream + * combinations. * If the application creates a session with a stream combination not in the guaranteed * list, or with mixed DEFAULT and non-DEFAULT use cases within the same session, * the camera device may ignore some stream use cases due to hardware constraints @@ -3754,13 +3752,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>An array of mandatory stream combinations with stream use cases. * This is an app-readable conversion of the mandatory stream combination - * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables} with - * each stream's use case being set.</p> + * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations tables} with each stream's use case being set.</p> * <p>The array of * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is * generated according to the documented - * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for a - * camera device with + * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline} for a camera device with * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE } * capability. * The mandatory stream combination array will be {@code null} in case the device doesn't diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index ad68866571e3..99b297abe92a 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -636,8 +636,9 @@ public abstract class CameraDevice implements AutoCloseable { * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}) support at * least the following stream combinations: * + * <h5>LEGACY-level guaranteed configurations</h5> + * * <table> - * <tr><th colspan="7">LEGACY-level guaranteed configurations</th></tr> * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th> <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr> @@ -656,8 +657,9 @@ public abstract class CameraDevice implements AutoCloseable { * support at least the following stream combinations in addition to those for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} devices: * + * <h5>LIMITED-level additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="7">LIMITED-level additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code RECORD }</td> <td colspan="2" id="rb"></td> <td>High-resolution video recording with preview.</td> </tr> @@ -674,8 +676,9 @@ public abstract class CameraDevice implements AutoCloseable { * support at least the following stream combinations in addition to those for * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: * + * <h5>FULL-level additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="7">FULL-level additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution GPU processing with preview.</td> </tr> @@ -693,8 +696,9 @@ public abstract class CameraDevice implements AutoCloseable { * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} and * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: * + * <h5>RAW-capability additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="7">RAW-capability additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> * <tr> <td>{@code RAW }</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>No-preview DNG capture.</td> </tr> @@ -716,8 +720,9 @@ public abstract class CameraDevice implements AutoCloseable { * list for FULL-level devices, so this table is only relevant for LIMITED-level devices that * support the BURST_CAPTURE capability. * + * <h5>BURST-capability additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="5">BURST-capability additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution GPU processing with preview.</td> </tr> @@ -733,8 +738,9 @@ public abstract class CameraDevice implements AutoCloseable { * RAW capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}): * + * <h5>LEVEL-3 additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="11">LEVEL-3 additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>In-app viewfinder analysis with dynamic selection of output format.</td> </tr> @@ -748,8 +754,9 @@ public abstract class CameraDevice implements AutoCloseable { * <p> Note: The sizes mentioned for these concurrent streams are the maximum sizes guaranteed * to be supported. Sizes smaller than these, obtained by {@link StreamConfigurationMap#getOutputSizes} for a particular format, are supported as well. </p> * + * <h5>Concurrent stream guaranteed configurations</h5> + * * <table> - * <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> * <tr> <td>{@code YUV}</td><td id="rb">{@code s1440p}</td> <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr> @@ -794,8 +801,9 @@ public abstract class CameraDevice implements AutoCloseable { * stream combinations ({@code MULTI_RES} in the Max size column refers to a {@link * MultiResolutionImageReader} created based on the variable max resolutions supported): * + * <h5>LEGACY-level additional guaranteed combinations with MultiResolutionoutputs</h5> + * * <table> - * <tr><th colspan="7">LEGACY-level additional guaranteed combinations with MultiResolutionoutputs</th></tr> * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th> <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr> @@ -814,8 +822,10 @@ public abstract class CameraDevice implements AutoCloseable { * * <p> Devices with the ULTRA_HIGH_RESOLUTION_SENSOR capability have some additional guarantees * which clients can take advantage of : </p> + * + * <h5>Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors</h5> + * * <table> - * <tr><th colspan="10">Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors</th></tr> * <tr> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th> <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr> * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>Ultra high res still image capture with preview</td> </tr> @@ -834,8 +844,10 @@ public abstract class CameraDevice implements AutoCloseable { * <p> 10-bit output capable * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT} * devices support at least the following stream combinations: </p> + * + * <h5>10-bit output additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="7">10-bit output additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> }</td> <td colspan="4" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr> @@ -887,8 +899,9 @@ public abstract class CameraDevice implements AutoCloseable { * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}) support below additional * stream combinations: * + * <h5>STREAM_USE_CASE capability additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="10">STREAM_USE_CASE capability additional guaranteed configurations</th></tr> * <tr><th colspan="3" id="rb">Target 1</th><th colspan="3" id="rb">Target 2</th><th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th> </tr> * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple preview or in-app image processing</td> </tr> @@ -911,8 +924,9 @@ public abstract class CameraDevice implements AutoCloseable { * stream use-case in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES}, * support the additional stream combinations below: * + * <h5>STREAM_USE_CASE_CROPPED_RAW capability additional guaranteed configurations</h5> + * * <table> - * <tr><th colspan="10">STREAM_USE_CASE_CROPPED_RAW capability additional guaranteed configurations</th></tr> * <tr><th colspan="3" id="rb">Target 1</th><th colspan="3" id="rb">Target 2</th><th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th> </tr> * <tr> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code CROPPED_RAW}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Cropped RAW still capture without preview</td> </tr> @@ -926,8 +940,10 @@ public abstract class CameraDevice implements AutoCloseable { * the following stream combinations are guaranteed, * for CaptureRequests where {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE} is set to * {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION} <p> + * + * <h5>Preview stabilization guaranteed stream configurations</h5> + * * <table> - * <tr><th colspan="7">Preview stabilization guaranteed stream configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p}</td><td colspan="2" id="rb"></td> <td>Stabilized preview, GPU video processing, or no-preview stabilized video recording.</td> </tr> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 4950373449e2..dfc27caa362c 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1213,7 +1213,8 @@ public abstract class CameraMetadata<TKey> { * <ul> * <li>Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }</li> * <li>All mandatory stream combinations for this specific capability as per - * documentation {@link android.hardware.camera2.CameraDevice#createCaptureSession }</li> + * documentation + * {@link android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations }</li> * <li>In case the device is not able to capture some combination of supported * standard 8-bit and/or 10-bit dynamic range profiles within the same capture request, * then those constraints must be listed in @@ -1252,9 +1253,10 @@ public abstract class CameraMetadata<TKey> { * </ul> * <p>{@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES } * lists all of the supported stream use cases.</p> - * <p>Refer to {@link android.hardware.camera2.CameraDevice#createCaptureSession } for the - * mandatory stream combinations involving stream use cases, which can also be queried - * via {@link android.hardware.camera2.params.MandatoryStreamCombination }.</p> + * <p>Refer to + * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations } + * for the mandatory stream combinations involving stream use cases, which can also be + * queried via {@link android.hardware.camera2.params.MandatoryStreamCombination }.</p> * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19; @@ -1752,7 +1754,8 @@ public abstract class CameraMetadata<TKey> { * <p>This camera device does not have enough capabilities to qualify as a <code>FULL</code> device or * better.</p> * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code> tables in the - * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p> + * {@link android.hardware.camera2.CameraDevice#limited-level-additional-guaranteed-configurations } + * documentation are guaranteed to be supported.</p> * <p>All <code>LIMITED</code> devices support the <code>BACKWARDS_COMPATIBLE</code> capability, indicating basic * support for color image capture. The only exception is that the device may * alternatively support only the <code>DEPTH_OUTPUT</code> capability, if it can only output depth @@ -1779,7 +1782,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>This camera device is capable of supporting advanced imaging applications.</p> * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code> tables in the - * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p> + * {@link android.hardware.camera2.CameraDevice#full-level-additional-guaranteed-configurations } + * documentation are guaranteed to be supported.</p> * <p>A <code>FULL</code> device will support below capabilities:</p> * <ul> * <li><code>BURST_CAPTURE</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains @@ -1807,7 +1811,9 @@ public abstract class CameraMetadata<TKey> { /** * <p>This camera device is running in backward compatibility mode.</p> - * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are supported.</p> + * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the + * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations } + * documentation are supported.</p> * <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual * post-processing, arbitrary cropping regions, and has relaxed performance constraints. * No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a @@ -1830,7 +1836,9 @@ public abstract class CameraMetadata<TKey> { * <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to * FULL-level capabilities.</p> * <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and - * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p> + * <code>LIMITED</code> tables in the + * {@link android.hardware.camera2.CameraDevice#level-3-additional-guaranteed-configurations } + * documentation are guaranteed to be supported.</p> * <p>The following additional capabilities are guaranteed to be supported:</p> * <ul> * <li><code>YUV_REPROCESSING</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java index ac7f2ca6a427..7e42f43056e1 100644 --- a/core/java/android/hardware/camera2/TotalCaptureResult.java +++ b/core/java/android/hardware/camera2/TotalCaptureResult.java @@ -179,7 +179,7 @@ public final class TotalCaptureResult extends CaptureResult { * @return unmodifiable map between physical camera ids and their capture result metadata * * @deprecated - * <p>Please use {@link #getPhysicalCameraTotalResults() instead to get the + * <p>Please use {@link #getPhysicalCameraTotalResults()} instead to get the * physical cameras' {@code TotalCaptureResult}.</p> */ public Map<String, CaptureResult> getPhysicalCameraResults() { diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index d87226c2b895..65d4b433f132 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -77,18 +77,18 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private static final String TAG = "CameraAdvancedExtensionSessionImpl"; private final Executor mExecutor; - private final CameraDevice mCameraDevice; + private CameraDevice mCameraDevice; private final Map<String, CameraMetadataNative> mCharacteristicsMap; private final long mExtensionClientId; private final Handler mHandler; private final HandlerThread mHandlerThread; private final CameraExtensionSession.StateCallback mCallbacks; - private final IAdvancedExtenderImpl mAdvancedExtender; + private IAdvancedExtenderImpl mAdvancedExtender; // maps registered camera surfaces to extension output configs private final HashMap<Surface, CameraOutputConfig> mCameraConfigMap = new HashMap<>(); // maps camera extension output ids to camera registered image readers private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>(); - private final RequestProcessor mRequestProcessor = new RequestProcessor(); + private RequestProcessor mRequestProcessor = new RequestProcessor(); private final int mSessionId; private Surface mClientRepeatingRequestSurface; @@ -100,7 +100,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private final ExtensionSessionStatsAggregator mStatsAggregator; private boolean mInitialized; - + private boolean mSessionClosed; // Lock to synchronize cross-thread access to device public interface final Object mInterfaceLock; @@ -237,6 +237,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); mInitialized = false; + mSessionClosed = false; mInitializeHandler = new InitializeSessionHandler(); mSessionId = sessionId; mInterfaceLock = cameraDevice.mInterfaceLock; @@ -424,7 +425,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mSessionProcessor.setParameters(request); seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request, - executor, listener)); + executor, listener, mCameraDevice.getId())); } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed to enable repeating request, extension service failed to respond!"); @@ -452,7 +453,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mSessionProcessor.setParameters(request); seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request, - executor, listener), isPostviewRequested); + executor, listener, mCameraDevice.getId()), isPostviewRequested); } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " + " to submit capture request, extension service failed to respond!"); @@ -460,8 +461,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } else if ((mClientRepeatingRequestSurface != null) && request.containsTarget(mClientRepeatingRequestSurface)) { try { - seqId = mSessionProcessor.startTrigger(request, - new RequestCallbackHandler(request, executor, listener)); + seqId = mSessionProcessor.startTrigger(request, new RequestCallbackHandler( + request, executor, listener, mCameraDevice.getId())); } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " + " to submit trigger request, extension service failed to respond!"); @@ -528,6 +529,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mCaptureSession.stopRepeating(); mSessionProcessor.stopRepeating(); mSessionProcessor.onCaptureSessionEnd(); + mSessionClosed = true; } catch (RemoteException e) { Log.e(TAG, "Failed to stop the repeating request or end the session," + " , extension service does not respond!") ; @@ -560,6 +562,9 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes if (mSessionProcessor != null) { try { + if (!mSessionClosed) { + mSessionProcessor.onCaptureSessionEnd(); + } mSessionProcessor.deInitSession(); } catch (RemoteException e) { Log.e(TAG, "Failed to de-initialize session processor, extension service" @@ -584,6 +589,10 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mClientRepeatingRequestSurface = null; mClientCaptureSurface = null; + mCaptureSession = null; + mRequestProcessor = null; + mCameraDevice = null; + mAdvancedExtender = null; } if (notifyClose && !skipCloseNotification) { @@ -706,13 +715,16 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes private final CaptureRequest mClientRequest; private final Executor mClientExecutor; private final ExtensionCaptureCallback mClientCallbacks; + private final String mCameraId; private RequestCallbackHandler(@NonNull CaptureRequest clientRequest, @NonNull Executor clientExecutor, - @NonNull ExtensionCaptureCallback clientCallbacks) { + @NonNull ExtensionCaptureCallback clientCallbacks, + @NonNull String cameraId) { mClientRequest = clientRequest; mClientExecutor = clientExecutor; mClientCallbacks = clientCallbacks; + mCameraId = cameraId; } @Override @@ -784,7 +796,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp); - TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result, + TotalCaptureResult totalResult = new TotalCaptureResult(mCameraId, result, mClientRequest, requestId, timestamp, new ArrayList<>(), mSessionId, new PhysicalCaptureResultInfo[0]); final long ident = Binder.clearCallingIdentity(); @@ -1036,14 +1048,20 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes public int submitBurst(List<Request> requests, IRequestCallback callback) { int seqId = -1; try { - CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback); - ArrayList<CaptureRequest> captureRequests = new ArrayList<>(); - for (Request request : requests) { - captureRequests.add(initializeCaptureRequest(mCameraDevice, request, - mCameraConfigMap)); + synchronized (mInterfaceLock) { + if (!mInitialized) { + return seqId; + } + + CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback); + ArrayList<CaptureRequest> captureRequests = new ArrayList<>(); + for (Request request : requests) { + captureRequests.add(initializeCaptureRequest(mCameraDevice, request, + mCameraConfigMap)); + } + seqId = mCaptureSession.captureBurstRequests(captureRequests, + new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); } - seqId = mCaptureSession.captureBurstRequests(captureRequests, - new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); } catch (CameraAccessException e) { Log.e(TAG, "Failed to submit capture requests!"); } catch (IllegalStateException e) { @@ -1057,11 +1075,17 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes public int setRepeating(Request request, IRequestCallback callback) { int seqId = -1; try { - CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice, + synchronized (mInterfaceLock) { + if (!mInitialized) { + return seqId; + } + + CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice, request, mCameraConfigMap); - CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback); - seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest, - new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); + CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback); + seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest, + new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback); + } } catch (CameraAccessException e) { Log.e(TAG, "Failed to enable repeating request!"); } catch (IllegalStateException e) { @@ -1074,7 +1098,13 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes @Override public void abortCaptures() { try { - mCaptureSession.abortCaptures(); + synchronized (mInterfaceLock) { + if (!mInitialized) { + return; + } + + mCaptureSession.abortCaptures(); + } } catch (CameraAccessException e) { Log.e(TAG, "Failed during capture abort!"); } catch (IllegalStateException e) { @@ -1085,7 +1115,13 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes @Override public void stopRepeating() { try { - mCaptureSession.stopRepeating(); + synchronized (mInterfaceLock) { + if (!mInitialized) { + return; + } + + mCaptureSession.stopRepeating(); + } } catch (CameraAccessException e) { Log.e(TAG, "Failed during repeating capture stop!"); } catch (IllegalStateException e) { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index a9c4818393a8..2f9c2073cd38 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -52,8 +52,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; -import static java.lang.annotation.RetentionPolicy.SOURCE; - import android.annotation.AnyThread; import android.annotation.CallSuper; import android.annotation.DrawableRes; @@ -160,6 +158,7 @@ import com.android.internal.inputmethod.InputMethodNavButtonFlags; import com.android.internal.inputmethod.InputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.util.Preconditions; import com.android.internal.util.RingBuffer; import org.xmlpull.v1.XmlPullParserException; @@ -482,43 +481,53 @@ public class InputMethodService extends AbstractInputMethodService { public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3; /** - * Enum flag to be used for {@link #setBackDisposition(int)}. + * Enum values to be used for {@link #setBackDisposition(int)}. * * @hide */ - @Retention(SOURCE) - @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS, - BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING}, - prefix = "BACK_DISPOSITION_") + @IntDef(prefix = { "BACK_DISPOSITION_" }, value = { + BACK_DISPOSITION_DEFAULT, + BACK_DISPOSITION_WILL_NOT_DISMISS, + BACK_DISPOSITION_WILL_DISMISS, + BACK_DISPOSITION_ADJUST_NOTHING, + }) + @Retention(RetentionPolicy.SOURCE) public @interface BackDispositionMode {} /** + * Enum flags to be used for {@link #setImeWindowStatus}, representing the current state of the + * IME window visibility. + * * @hide - * The IME is active. It may or may not be visible. */ - public static final int IME_ACTIVE = 0x1; + @IntDef(flag = true, prefix = { "IME_" }, value = { + IME_ACTIVE, + IME_VISIBLE, + IME_VISIBLE_IMPERCEPTIBLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImeWindowVisibility {} /** + * The IME is active. It may or may not be visible. * @hide - * The IME is perceptibly visible to the user. */ - public static final int IME_VISIBLE = 0x2; + public static final int IME_ACTIVE = 0x1; /** + * The IME is perceptibly visible to the user. * @hide - * The IME is active and ready with views but set invisible. - * This flag cannot be combined with {@link #IME_VISIBLE}. */ - public static final int IME_INVISIBLE = 0x4; + public static final int IME_VISIBLE = 0x2; /** - * @hide * The IME is visible, but not yet perceptible to the user (e.g. fading in) * by {@link android.view.WindowInsetsController}. * * @see InputMethodManager#reportPerceptible + * @hide */ - public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x8; + public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x4; // Min and max values for back disposition. private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT; @@ -631,9 +640,18 @@ public class InputMethodService extends AbstractInputMethodService { int mStatusIcon; + /** + * Latest value reported of back disposition mode. + */ @BackDispositionMode int mBackDisposition; + /** + * Latest value reported of IME window visibility flags. + */ + @ImeWindowVisibility + private int mImeWindowVisibility; + private Object mLock = new Object(); @GuardedBy("mLock") private boolean mNotifyUserActionSent; @@ -1210,8 +1228,14 @@ public class InputMethodService extends AbstractInputMethodService { mImeSurfaceRemoverRunnable = null; } - private void setImeWindowStatus(int visibilityFlags, int backDisposition) { - mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition); + private void setImeWindowStatus(@ImeWindowVisibility int vis, + @BackDispositionMode int backDisposition) { + if (vis == mImeWindowVisibility && backDisposition == mBackDisposition) { + return; + } + mImeWindowVisibility = Preconditions.checkFlagsArgument(vis, IME_ACTIVE | IME_VISIBLE); + mBackDisposition = backDisposition; + mPrivOps.setImeWindowStatusAsync(mImeWindowVisibility, mBackDisposition); } /** Set region of the keyboard to be avoided from back gesture */ @@ -1885,15 +1909,11 @@ public class InputMethodService extends AbstractInputMethodService { * @param disposition disposition mode to be set */ public void setBackDisposition(@BackDispositionMode int disposition) { - if (disposition == mBackDisposition) { - return; - } - if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) { + if (disposition < BACK_DISPOSITION_MIN || disposition > BACK_DISPOSITION_MAX) { Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified."); return; } - mBackDisposition = disposition; - setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); + setImeWindowStatus(mImeWindowVisibility, disposition); } /** @@ -2867,14 +2887,8 @@ public class InputMethodService extends AbstractInputMethodService { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); mDecorViewWasVisible = mDecorViewVisible; mInShowWindow = true; - final int previousImeWindowStatus = - (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown() - ? (!mWindowVisible ? IME_INVISIBLE : IME_VISIBLE) : 0); startViews(prepareWindow(showInput)); - final int nextImeWindowStatus = mapToImeWindowStatus(); - if (previousImeWindowStatus != nextImeWindowStatus) { - setImeWindowStatus(nextImeWindowStatus, mBackDisposition); - } + setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); mNavigationBarController.onWindowShown(); // compute visibility @@ -4085,9 +4099,9 @@ public class InputMethodService extends AbstractInputMethodService { }; } + @ImeWindowVisibility private int mapToImeWindowStatus() { - return IME_ACTIVE - | (isInputViewShown() ? IME_VISIBLE : 0); + return IME_ACTIVE | (mDecorViewVisible ? IME_VISIBLE : 0); } private boolean isAutomotive() { diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index cacde7f4a547..9e97216ac632 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -711,6 +711,7 @@ public final class NfcAdapter { /* Stale sService pointer */ if (sIsInitialized) sIsInitialized = false; } + return null; } /* Try to initialize the service */ NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 92be4c0c7dc3..c9073fa4b72c 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -123,6 +123,7 @@ public class GraphicsEnvironment { private int mAngleOptInIndex = -1; private boolean mEnabledByGameMode = false; + private boolean mShouldUseAngle = false; /** * Set up GraphicsEnvironment @@ -141,19 +142,16 @@ public class GraphicsEnvironment { // Setup ANGLE and pass down ANGLE details to the C++ code Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle"); - boolean useAngle = false; if (setupAngle(context, coreSettings, pm, packageName)) { - if (shouldUseAngle(context, coreSettings, packageName)) { - useAngle = true; - setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE, - 0, packageName, getVulkanVersion(pm)); - } + mShouldUseAngle = true; + setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE, + 0, packageName, getVulkanVersion(pm)); } Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver"); if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) { - if (!useAngle) { + if (!mShouldUseAngle) { setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), @@ -636,7 +634,10 @@ public class GraphicsEnvironment { } /** - * Show the ANGLE in Use Dialog Box + * Show the ANGLE in use dialog box. + * The ANGLE in use dialog box will show up as long as the application + * should use ANGLE. It does not mean the application has successfully + * loaded ANGLE because this check happens before the loading completes. * @param context */ public void showAngleInUseDialogBox(Context context) { @@ -644,8 +645,7 @@ public class GraphicsEnvironment { return; } - final String packageName = context.getPackageName(); - if (!getShouldUseAngle(packageName)) { + if (!mShouldUseAngle) { return; } @@ -890,9 +890,8 @@ public class GraphicsEnvironment { private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion); - private static native void setAngleInfo(String path, String appPackage, + private static native void setAngleInfo(String path, String packageName, String devOptIn, String[] features); - private static native boolean getShouldUseAngle(String packageName); private static native boolean setInjectLayersPrSetDumpable(); private static native void nativeToggleAngleAsSystemDriver(boolean enabled); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 84f1880213b8..2d217186c9c7 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1764,10 +1764,19 @@ public class UserManager { /** * Specifies if a user is not allowed to use 2g networks. * + * <p> This is a security feature. 2g has no mutual authentication between a device and + * cellular base station and downgrading a device's connection to 2g is a common tactic for + * several types of privacy and security compromising attacks that could allow an adversary + * to intercept, inject, or modify cellular communications. + * * <p>This restriction can only be set by a device owner or a profile owner of an * organization-owned managed profile on the parent profile. - * In all cases, the setting applies globally on the device and will prevent the device from - * scanning for or connecting to 2g networks, except in the case of an emergency. + * In all cases, the setting applies globally on the device. + * + * <p> Cellular connectivity loss (where a device would have otherwise successfully + * connected to a 2g network) occurs if the device is in an area where only 2g networks are + * available. Emergency calls are an exception and are never impacted. The device will still + * scan for and connect to a 2g network for emergency calls. * * <p>Holders of the permission * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK} diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 4e086c2ad134..dbc1be141571 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -44,6 +44,7 @@ import android.app.WallpaperManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.BLASTBufferQueue; import android.graphics.Bitmap; @@ -190,6 +191,9 @@ public abstract class WallpaperService extends Service { private Handler mBackgroundHandler; private HandlerThread mBackgroundThread; + // TODO (b/287037772) remove this flag and the forceReport argument in reportVisibility + private boolean mIsWearOs; + static final class WallpaperCommand { String action; int x; @@ -2282,7 +2286,8 @@ public abstract class WallpaperService extends Service { @Override public void onDisplayChanged(int displayId) { if (mDisplay.getDisplayId() == displayId) { - boolean forceReport = mDisplay.getState() != Display.STATE_DOZE_SUSPEND; + boolean forceReport = mIsWearOs + && mDisplay.getState() != Display.STATE_DOZE_SUSPEND; reportVisibility(forceReport); } } @@ -2734,6 +2739,7 @@ public abstract class WallpaperService extends Service { mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + mIsWearOs = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); super.onCreate(); Trace.endSection(); } diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index dc066710dfe8..1ed5d3fb6268 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -23,6 +23,9 @@ import android.graphics.Region; import android.hardware.HardwareBuffer; import android.window.SurfaceSyncGroup; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + /** * Provides an interface to the root-Surface of a View Hierarchy or Window. This * is used in combination with the {@link android.view.SurfaceControl} API to enable @@ -167,4 +170,40 @@ public interface AttachedSurfaceControl { */ default void setChildBoundingInsets(@NonNull Rect insets) { } + + /** + * Add a trusted presentation listener on the SurfaceControl associated with this window. + * + * @param t Transaction that the trusted presentation listener is added on. This should + * be applied by the caller. + * @param thresholds The {@link SurfaceControl.TrustedPresentationThresholds} that will specify + * when the to invoke the callback. + * @param executor The {@link Executor} where the callback will be invoked on. + * @param listener The {@link Consumer} that will receive the callbacks when entered or + * exited the threshold. + * + * @see SurfaceControl.Transaction#setTrustedPresentationCallback(SurfaceControl, + * SurfaceControl.TrustedPresentationThresholds, Executor, Consumer) + * + * @hide b/287076178 un-hide with API bump + */ + default void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, + @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { + } + + /** + * Remove a trusted presentation listener on the SurfaceControl associated with this window. + * + * @param t Transaction that the trusted presentation listener removed on. This should + * be applied by the caller. + * @param listener The {@link Consumer} that was previously registered with + * addTrustedPresentationCallback that should be removed. + * + * @see SurfaceControl.Transaction#clearTrustedPresentationCallback(SurfaceControl) + * @hide b/287076178 un-hide with API bump + */ + default void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull Consumer<Boolean> listener) { + } } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 61a72772200c..dceae90822b8 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -178,7 +178,8 @@ public class InsetsState implements Parcelable { if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) { compatInsetsTypes &= ~statusBars(); } - if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) { + if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode) + && !alwaysConsumeSystemBars) { compatInsetsTypes = 0; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c396af6323d2..d2c6a765d9be 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -11554,4 +11554,17 @@ public final class ViewRootImpl implements ViewParent, mChildBoundingInsetsChanged = true; scheduleTraversals(); } + + @Override + public void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, + @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { + t.setTrustedPresentationCallback(getSurfaceControl(), thresholds, executor, listener); + } + + @Override + public void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull Consumer<Boolean> listener) { + t.clearTrustedPresentationCallback(getSurfaceControl()); + } } diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java index cfbeeffb91a6..7fc55a7f581b 100644 --- a/core/java/android/window/WindowInfosListenerForTest.java +++ b/core/java/android/window/WindowInfosListenerForTest.java @@ -58,6 +58,11 @@ public class WindowInfosListenerForTest { public final String name; /** + * The display id the window is on. + */ + public final int displayId; + + /** * The window's position and size in display space. */ @NonNull @@ -73,14 +78,23 @@ public class WindowInfosListenerForTest { */ public final boolean isVisible; - WindowInfo(@NonNull IBinder windowToken, @NonNull String name, @NonNull Rect bounds, - int inputConfig) { + WindowInfo(@NonNull IBinder windowToken, @NonNull String name, int displayId, + @NonNull Rect bounds, int inputConfig) { this.windowToken = windowToken; this.name = name; + this.displayId = displayId; this.bounds = bounds; this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0; this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0; } + + @Override + public String toString() { + return name + ", frame=" + bounds + + ", isVisible=" + isVisible + + ", isTrustedOverlay=" + isTrustedOverlay + + ", token=" + windowToken; + } } private static final String TAG = "WindowInfosListenerForTest"; @@ -139,8 +153,8 @@ public class WindowInfosListenerForTest { for (var handle : windowHandles) { var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight, handle.frameBottom); - windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, bounds, - handle.inputConfig)); + windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId, + bounds, handle.inputConfig)); } return windowInfos; } diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java index 2858f0a1a725..954f68633e57 100644 --- a/core/java/android/window/WindowMetricsController.java +++ b/core/java/android/window/WindowMetricsController.java @@ -145,13 +145,13 @@ public final class WindowMetricsController { for (int i = 0; i < possibleDisplayInfos.size(); i++) { currentDisplayInfo = possibleDisplayInfos.get(i); - // Calculate max bounds for natural rotation and state. - Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(), - currentDisplayInfo.getNaturalHeight()); + // Calculate max bounds for this rotation and state. + Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth, + currentDisplayInfo.logicalHeight); - // Calculate insets for the natural max bounds. + // Calculate insets for the rotated max bounds. final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0; - // Initialize insets based on Surface.ROTATION_0. Note any window-provided insets + // Initialize insets based upon display rotation. Note any window-provided insets // will not be set. windowInsets = getWindowInsetsFromServerForDisplay( currentDisplayInfo.displayId, null /* token */, diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index 66e3333acf7c..1a3804900665 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -20,6 +20,7 @@ import android.annotation.AnyThread; import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; +import android.inputmethodservice.InputMethodService; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; @@ -105,14 +106,10 @@ public final class InputMethodPrivilegedOperations { * * @param vis visibility flags * @param backDisposition disposition flags - * @see android.inputmethodservice.InputMethodService#IME_ACTIVE - * @see android.inputmethodservice.InputMethodService#IME_VISIBLE - * @see android.inputmethodservice.InputMethodService#IME_INVISIBLE - * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT - * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING */ @AnyThread - public void setImeWindowStatusAsync(int vis, int backDisposition) { + public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index fdcb87ff5e3f..bbaaa472cbbb 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -259,6 +259,8 @@ public class BatteryStatsHistory { } private TraceDelegate mTracer; + private int mTraceLastState = 0; + private int mTraceLastState2 = 0; /** * Constructor @@ -1241,7 +1243,6 @@ public class BatteryStatsHistory { */ private void recordTraceEvents(int code, HistoryTag tag) { if (code == HistoryItem.EVENT_NONE) return; - if (!mTracer.tracingEnabled()) return; final int idx = code & HistoryItem.EVENT_TYPE_MASK; final String prefix = (code & HistoryItem.EVENT_FLAG_START) != 0 ? "+" : @@ -1270,8 +1271,6 @@ public class BatteryStatsHistory { * Writes changes to a HistoryItem state bitmap to Atrace. */ private void recordTraceCounters(int oldval, int newval, BitDescription[] descriptions) { - if (!mTracer.tracingEnabled()) return; - int diff = oldval ^ newval; if (diff == 0) return; @@ -1324,6 +1323,16 @@ public class BatteryStatsHistory { } private void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { + if (mTracer != null && mTracer.tracingEnabled()) { + recordTraceEvents(cur.eventCode, cur.eventTag); + recordTraceCounters(mTraceLastState, cur.states, + BatteryStats.HISTORY_STATE_DESCRIPTIONS); + recordTraceCounters(mTraceLastState2, cur.states2, + BatteryStats.HISTORY_STATE2_DESCRIPTIONS); + mTraceLastState = cur.states; + mTraceLastState2 = cur.states2; + } + if (!mHaveBatteryLevel || !mRecordingHistory) { return; } @@ -1345,12 +1354,6 @@ public class BatteryStatsHistory { + Integer.toHexString(lastDiffStates2)); } - recordTraceEvents(cur.eventCode, cur.eventTag); - recordTraceCounters(mHistoryLastWritten.states, - cur.states, BatteryStats.HISTORY_STATE_DESCRIPTIONS); - recordTraceCounters(mHistoryLastWritten.states2, - cur.states2, BatteryStats.HISTORY_STATE2_DESCRIPTIONS); - if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0 && (diffStates2 & lastDiffStates2) == 0 diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index a95ce64ecec8..7f53cb433c98 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -771,7 +771,7 @@ public class ZygoteInit { Zygote.applyInvokeWithSystemProperty(parsedArgs); if (Zygote.nativeSupportsMemoryTagging()) { - String mode = SystemProperties.get("arm64.memtag.process.system_server", ""); + String mode = SystemProperties.get("persist.arm64.memtag.system_server", ""); if (mode.isEmpty()) { /* The system server has ASYNC MTE by default, in order to allow * system services to specify their own MTE level later, as you diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 15f70f358d6e..af1fdd79169a 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1111,8 +1111,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind : controller.getSystemBarsAppearance(); if (insets != null) { - final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags, - getResources().getConfiguration().windowConfiguration.getWindowingMode()); + mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars(); + + final boolean clearsCompatInsets = + clearsCompatInsets(attrs.type, attrs.flags, + getResources().getConfiguration().windowConfiguration + .getWindowingMode()) + && !mLastShouldAlwaysConsumeSystemBars; final Insets stableBarInsets = insets.getInsetsIgnoringVisibility( WindowInsets.Type.systemBars()); final Insets systemInsets = clearsCompatInsets @@ -1143,7 +1148,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset); mLastHasLeftStableInset = hasLeftStableInset; - mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars(); mLastSuppressScrimTypes = insets.getSuppressScrimTypes(); } diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java index 4f827cda6afa..8b9a9913183d 100644 --- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java +++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java @@ -16,6 +16,7 @@ package com.android.internal.statusbar; +import android.inputmethodservice.InputMethodService; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -31,7 +32,9 @@ public final class RegisterStatusBarResult implements Parcelable { public final int mDisabledFlags1; // switch[0] public final int mAppearance; // switch[1] public final AppearanceRegion[] mAppearanceRegions; // switch[2] + @InputMethodService.ImeWindowVisibility public final int mImeWindowVis; // switch[3] + @InputMethodService.BackDispositionMode public final int mImeBackDisposition; // switch[4] public final boolean mShowImeSwitcher; // switch[5] public final int mDisabledFlags2; // switch[6] @@ -44,10 +47,12 @@ public final class RegisterStatusBarResult implements Parcelable { public final LetterboxDetails[] mLetterboxDetails; public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1, - int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis, - int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken, - boolean navbarColorManagedByIme, int behavior, int requestedVisibleTypes, - String packageName, int transientBarTypes, LetterboxDetails[] letterboxDetails) { + int appearance, AppearanceRegion[] appearanceRegions, + @InputMethodService.ImeWindowVisibility int imeWindowVis, + @InputMethodService.BackDispositionMode int imeBackDisposition, boolean showImeSwitcher, + int disabledFlags2, IBinder imeToken, boolean navbarColorManagedByIme, int behavior, + int requestedVisibleTypes, String packageName, int transientBarTypes, + LetterboxDetails[] letterboxDetails) { mIcons = new ArrayMap<>(icons); mDisabledFlags1 = disabledFlags1; mAppearance = appearance; diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 416d991bd64c..d6c9f92cd8cd 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -163,10 +163,9 @@ bool NativeInputWindowHandle::updateInfo() { mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>( env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode)); - mInfo.ownerPid = env->GetIntField(obj, - gInputWindowHandleClassInfo.ownerPid); - mInfo.ownerUid = env->GetIntField(obj, - gInputWindowHandleClassInfo.ownerUid); + mInfo.ownerPid = env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid); + mInfo.ownerUid = gui::Uid{ + static_cast<uid_t>(env->GetIntField(obj, gInputWindowHandleClassInfo.ownerUid))}; mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>"); mInfo.displayId = env->GetIntField(obj, gInputWindowHandleClassInfo.displayId); @@ -309,7 +308,8 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.touchOcclusionMode, static_cast<int32_t>(windowInfo.touchOcclusionMode)); env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid, windowInfo.ownerPid); - env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerUid, windowInfo.ownerUid); + env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerUid, + windowInfo.ownerUid.val()); ScopedLocalRef<jstring> packageName(env, env->NewStringUTF(windowInfo.packageName.data())); env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.packageName, packageName.get()); diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 01dbceb38d3a..d94b9828808b 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -49,10 +49,10 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, appPackageNameChars.c_str(), vulkanVersion); } -void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring packageName, jstring devOptIn, jobjectArray featuresObj) { ScopedUtfChars pathChars(env, path); - ScopedUtfChars appNameChars(env, appName); + ScopedUtfChars packageNameChars(env, packageName); ScopedUtfChars devOptInChars(env, devOptIn); std::vector<std::string> features; @@ -73,15 +73,10 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appNa } } - android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(), + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), packageNameChars.c_str(), devOptInChars.c_str(), features); } -bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) { - ScopedUtfChars appNameChars(env, appName); - return android::GraphicsEnv::getInstance().shouldUseAngle(appNameChars.c_str()); -} - void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) { android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader( env, classLoader); @@ -126,8 +121,6 @@ const JNINativeMethod g_methods[] = { {"setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V", reinterpret_cast<void*>(setAngleInfo_native)}, - {"getShouldUseAngle", "(Ljava/lang/String;)Z", - reinterpret_cast<void*>(shouldUseAngle_native)}, {"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native)}, {"setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native)}, diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto index 7fe2467032cb..73dd8dd23bf5 100644 --- a/core/proto/android/companion/telecom.proto +++ b/core/proto/android/companion/telecom.proto @@ -88,6 +88,8 @@ message Telecom { string name = 1; // Unique identifier for this facilitator, such as a package name. string identifier = 2; + // Extended identifier for this facilitator. + string extended_identifier = 3; } enum Control { diff --git a/core/res/res/drawable/ic_bluetooth_share_icon.xml b/core/res/res/drawable/ic_bluetooth_share_icon.xml deleted file mode 100644 index 6acfd57e669f..000000000000 --- a/core/res/res/drawable/ic_bluetooth_share_icon.xml +++ /dev/null @@ -1,27 +0,0 @@ -<!-- - Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<!-- This drawable should only be used by the Bluetooth application for its share target icon. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="@*android:color/accent_device_default_light"> - - <path - android:fillColor="@android:color/white" - android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" /> -</vector> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e643240932f1..f27535c65fe4 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5702,6 +5702,9 @@ TODO(b/255532890) Enable when ignoreOrientationRequest is set --> <bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool> + <!-- Whether per-app user aspect ratio override settings is enabled --> + <bool name="config_appCompatUserAppAspectRatioSettingsIsEnabled">false</bool> + <!-- Whether sending compat fake focus for split screen resumed activities is enabled. Needed because some game engines wait to get focus before drawing the content of the app which isn't guaranteed by default in multi-window modes. --> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 4ae54a052859..08c40ba0e823 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -67,6 +67,15 @@ <bool name="auto_data_switch_ping_test_before_switch">true</bool> <java-symbol type="bool" name="auto_data_switch_ping_test_before_switch" /> + <!-- Define the tolerated gap of score for auto data switch decision, larger than which the + device will switch to the SIM with higher score. The score is used in conjunction with the + score table defined in + CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY. + If 0, the device always switch to the higher score SIM. + If < 0, the network type and signal strength based auto switch is disabled. --> + <integer name="auto_data_switch_score_tolerance">3000</integer> + <java-symbol type="integer" name="auto_data_switch_score_tolerance" /> + <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec tunnels across service restart. If iwlan tunnels are not persisted across restart, Framework will clean up dangling data connections when service restarts --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 11f50dfdf84b..2e5da3353752 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4430,9 +4430,6 @@ <!-- Set to true to make assistant show in front of the dream/screensaver. --> <java-symbol type="bool" name="config_assistantOnTopOfDream"/> - <!-- Set to true to enable letterboxing on translucent activities. --> - <java-symbol type="bool" name="config_letterboxIsEnabledForTranslucentActivities" /> - <java-symbol type="string" name="config_overrideComponentUiPackage" /> <java-symbol type="string" name="notification_channel_network_status" /> @@ -4528,6 +4525,12 @@ <java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" /> <java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" /> <java-symbol type="bool" name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled" /> + <!-- Set to true to enable letterboxing on translucent activities. --> + <java-symbol type="bool" name="config_letterboxIsEnabledForTranslucentActivities" /> + + <!-- Whether per-app user aspect ratio override settings is enabled --> + <java-symbol type="bool" name="config_appCompatUserAppAspectRatioSettingsIsEnabled" /> + <java-symbol type="bool" name="config_isCompatFakeFocusEnabled" /> <java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" /> <java-symbol type="bool" name="config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled" /> diff --git a/core/tests/coretests/res/values/overlayable_icons_test.xml b/core/tests/coretests/res/values/overlayable_icons_test.xml index 7ea1848a723e..6a50e9ae58f7 100644 --- a/core/tests/coretests/res/values/overlayable_icons_test.xml +++ b/core/tests/coretests/res/values/overlayable_icons_test.xml @@ -21,7 +21,6 @@ <item>@*android:drawable/ic_audio_alarm</item> <item>@*android:drawable/ic_audio_alarm_mute</item> <item>@*android:drawable/ic_battery_80_24dp</item> - <item>@*android:drawable/ic_bluetooth_share_icon</item> <item>@*android:drawable/ic_bt_headphones_a2dp</item> <item>@*android:drawable/ic_bt_headset_hfp</item> <item>@*android:drawable/ic_bt_hearing_aid</item> diff --git a/core/tests/coretests/src/android/app/KeyguardManagerTest.java b/core/tests/coretests/src/android/app/KeyguardManagerTest.java index 7231fbd3b7eb..958906c36d05 100644 --- a/core/tests/coretests/src/android/app/KeyguardManagerTest.java +++ b/core/tests/coretests/src/android/app/KeyguardManagerTest.java @@ -19,6 +19,7 @@ package android.app; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; @@ -26,6 +27,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -209,6 +211,42 @@ public class KeyguardManagerTest { verifyDeviceLockedAndRemoveLock(); } + @Test + public void createConfirmDeviceCredentialForRemoteValidationIntent() { + RemoteLockscreenValidationSession remoteLockscreenValidationSession = + new RemoteLockscreenValidationSession.Builder() + .setSourcePublicKey("sourcePublicKey".getBytes()) + .build(); + ComponentName componentName = new ComponentName("pkg", "cls"); + String title = "title"; + String description = "description"; + String checkboxLabel = "checkboxLabel"; + String alternateButtonLabel = "alternateButtonLabel"; + + Intent intent = mKeyguardManager.createConfirmDeviceCredentialForRemoteValidationIntent( + remoteLockscreenValidationSession, + componentName, + title, + description, + checkboxLabel, + alternateButtonLabel + ); + + assertNotNull(intent); + assertEquals(KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL, intent.getAction()); + assertEquals(remoteLockscreenValidationSession, + intent.getParcelableExtra( + KeyguardManager.EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION, + RemoteLockscreenValidationSession.class)); + assertEquals(componentName, + intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class)); + assertEquals(title, intent.getStringExtra(KeyguardManager.EXTRA_TITLE)); + assertEquals(description, intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION)); + assertEquals(checkboxLabel, intent.getStringExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL)); + assertEquals(alternateButtonLabel, + intent.getStringExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL)); + } + private void verifyDeviceLockedAndRemoveLock() { assertTrue(mKeyguardManager.isDeviceSecure()); assertTrue("Failed to remove new password that was set in the test case.", diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java index b5f18c26dc97..6ffdee1c4b4f 100644 --- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java +++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java @@ -45,7 +45,7 @@ public class ConfigurationBoundResourceCacheTest @SmallTest public void testGetEmpty() { final Resources res = getActivity().getResources(); - assertNull(mCache.getInstance(-1, res, null).getValue()); + assertNull(mCache.getInstance(-1, res, null)); } @SmallTest @@ -53,9 +53,9 @@ public class ConfigurationBoundResourceCacheTest mCache.put(1, null, new DummyFloatConstantState(5f), ThemedResourceCache.UNDEFINED_GENERATION); final Resources res = getActivity().getResources(); - assertEquals(5f, mCache.getInstance(1, res, null).getValue()); - assertNotSame(5f, mCache.getInstance(1, res, null).getValue()); - assertEquals(false, mCache.getInstance(1, res, getActivity().getTheme()).hasValue()); + assertEquals(5f, mCache.getInstance(1, res, null)); + assertNotSame(5f, mCache.getInstance(1, res, null)); + assertEquals(null, mCache.getInstance(1, res, getActivity().getTheme())); } @SmallTest @@ -63,9 +63,9 @@ public class ConfigurationBoundResourceCacheTest mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f), ThemedResourceCache.UNDEFINED_GENERATION); final Resources res = getActivity().getResources(); - assertEquals(false, mCache.getInstance(1, res, null).hasValue()); - assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); - assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); + assertEquals(null, mCache.getInstance(1, res, null)); + assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme())); + assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme())); } @SmallTest @@ -75,10 +75,10 @@ public class ConfigurationBoundResourceCacheTest mCache.put(1, null, new DummyFloatConstantState(10f), ThemedResourceCache.UNDEFINED_GENERATION); final Resources res = getActivity().getResources(); - assertEquals(10f, mCache.getInstance(1, res, null).getValue()); - assertNotSame(10f, mCache.getInstance(1, res, null).getValue()); - assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); - assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); + assertEquals(10f, mCache.getInstance(1, res, null)); + assertNotSame(10f, mCache.getInstance(1, res, null)); + assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme())); + assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme())); } @SmallTest @@ -98,9 +98,9 @@ public class ConfigurationBoundResourceCacheTest Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; int changes = calcConfigChanges(res, newCnf); - assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()).getValue()); + assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme())); mCache.onConfigurationChange(changes); - assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()).getValue()); + assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme())); } @SmallTest @@ -123,9 +123,9 @@ public class ConfigurationBoundResourceCacheTest : Configuration.ORIENTATION_LANDSCAPE; int changes = calcConfigChanges(res, newCnf); assertEquals(changingDim, - mCache.getInstance(key, res, getActivity().getTheme()).getValue()); + mCache.getInstance(key, res, getActivity().getTheme())); mCache.onConfigurationChange(changes); - assertNull(mCache.get(key, getActivity().getTheme()).getValue()); + assertNull(mCache.get(key, getActivity().getTheme())); } @SmallTest @@ -152,15 +152,15 @@ public class ConfigurationBoundResourceCacheTest : Configuration.ORIENTATION_LANDSCAPE; int changes = calcConfigChanges(res, newCnf); assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res, - getActivity().getTheme()).getValue()); + getActivity().getTheme())); assertEquals(changingDim, mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - getActivity().getTheme()).getValue()); + getActivity().getTheme())); mCache.onConfigurationChange(changes); assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res, - getActivity().getTheme()).getValue()); + getActivity().getTheme())); assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - getActivity().getTheme()).getValue()); + getActivity().getTheme())); } @SmallTest @@ -198,18 +198,18 @@ public class ConfigurationBoundResourceCacheTest for (int i = 0; i < 2; i++) { final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; assertEquals(staticDim, - mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme).getValue()); + mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme)); assertEquals(changingDim, mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - theme).getValue()); + theme)); } mCache.onConfigurationChange(changes); for (int i = 0; i < 2; i++) { final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; assertEquals(staticDim, - mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme).getValue()); + mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme)); assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - theme).getValue()); + theme)); } } diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java index b2e42ba90152..0941a2b6d263 100644 --- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java @@ -25,7 +25,6 @@ import static com.google.common.truth.Truth.assertThat; import android.app.Activity; import android.compat.testing.PlatformCompatChangeRule; import android.os.Bundle; -import android.platform.test.annotations.IwTest; import android.platform.test.annotations.PlatinumTest; import android.platform.test.annotations.Presubmit; import android.provider.Settings; @@ -74,7 +73,6 @@ public class FontScaleConverterActivityTest { } @PlatinumTest(focusArea = "accessibility") - @IwTest(focusArea = "accessibility") @Test public void testFontsScaleNonLinearly() { final ActivityScenario<TestActivity> scenario = rule.getScenario(); @@ -106,7 +104,6 @@ public class FontScaleConverterActivityTest { } @PlatinumTest(focusArea = "accessibility") - @IwTest(focusArea = "accessibility") @Test public void testOnConfigurationChanged_doesNotCrash() { final ActivityScenario<TestActivity> scenario = rule.getScenario(); @@ -121,7 +118,6 @@ public class FontScaleConverterActivityTest { } @PlatinumTest(focusArea = "accessibility") - @IwTest(focusArea = "accessibility") @Test public void testUpdateConfiguration_doesNotCrash() { final ActivityScenario<TestActivity> scenario = rule.getScenario(); @@ -141,7 +137,7 @@ public class FontScaleConverterActivityTest { ); }); - PollingCheck.waitFor(/* timeout= */ 5000, () -> { + PollingCheck.waitFor(/* timeout= */ 7000, () -> { AtomicBoolean isActivityAtCorrectScale = new AtomicBoolean(false); rule.getScenario().onActivity(activity -> isActivityAtCorrectScale.set( @@ -150,12 +146,7 @@ public class FontScaleConverterActivityTest { .fontScale == fontScale ) ); - return isActivityAtCorrectScale.get() && InstrumentationRegistry - .getInstrumentation() - .getContext() - .getResources() - .getConfiguration() - .fontScale == fontScale; + return isActivityAtCorrectScale.get(); }); } diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl index 54f8808e2285..5d7fd85fc178 100644 --- a/data/keyboards/Vendor_0957_Product_0001.kl +++ b/data/keyboards/Vendor_0957_Product_0001.kl @@ -45,6 +45,7 @@ key 11 0 # custom keys key usage 0x000c01BB TV_INPUT +key usage 0x000c0186 MACRO_1 key usage 0x000c0185 TV_TELETEXT key usage 0x000c0061 CAPTIONS @@ -77,4 +78,4 @@ key usage 0x000c009D CHANNEL_DOWN key usage 0x000c0077 BUTTON_3 WAKE #YouTube key usage 0x000c0078 BUTTON_4 WAKE #Netflix key usage 0x000c0079 BUTTON_6 WAKE -key usage 0x000c007A BUTTON_7 WAKE
\ No newline at end of file +key usage 0x000c007A BUTTON_7 WAKE diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index 96190c4bc00a..1e6e50359cf3 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -28,6 +28,7 @@ import android.util.DisplayMetrics; import android.util.Pair; import android.view.Display; import android.view.DisplayAddress; +import android.view.Surface; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -36,6 +37,7 @@ import androidx.window.extensions.core.util.function.Consumer; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.util.Objects; @@ -229,27 +231,43 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, * @since {@link WindowExtensions#VENDOR_API_LEVEL_3} */ @Override + @NonNull public DisplayMetrics getRearDisplayMetrics() { - DisplayMetrics metrics = null; + DisplayMetrics rearDisplayMetrics = null; // DISPLAY_CATEGORY_REAR displays are only available when you are in the concurrent // display state, so we have to look through all displays to match the address - Display[] displays = mDisplayManager.getDisplays( + final Display[] displays = mDisplayManager.getDisplays( DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); + + for (int i = 0; i < displays.length; i++) { DisplayAddress.Physical address = (DisplayAddress.Physical) displays[i].getAddress(); if (mRearDisplayAddress == address.getPhysicalDisplayId()) { - metrics = new DisplayMetrics(); - displays[i].getRealMetrics(metrics); + rearDisplayMetrics = new DisplayMetrics(); + final Display rearDisplay = displays[i]; + + // We must always retrieve the metrics for the rear display regardless of if it is + // the default display or not. + rearDisplay.getRealMetrics(rearDisplayMetrics); + + // TODO(b/287170025): This should be something like if (!rearDisplay.isEnabled) + // instead. Currently when the rear display is disabled, its state is STATE_OFF. + if (rearDisplay.getDisplayId() != Display.DEFAULT_DISPLAY) { + final Display defaultDisplay = mDisplayManager + .getDisplay(Display.DEFAULT_DISPLAY); + rotateRearDisplayMetricsIfNeeded(defaultDisplay.getRotation(), + rearDisplay.getRotation(), rearDisplayMetrics); + } break; } } synchronized (mLock) { // Update the rear display metrics with our latest value if one was received - if (metrics != null) { - mRearDisplayMetrics = metrics; + if (rearDisplayMetrics != null) { + mRearDisplayMetrics = rearDisplayMetrics; } return Objects.requireNonNullElseGet(mRearDisplayMetrics, DisplayMetrics::new); @@ -540,6 +558,34 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return mLastReportedRearDisplayPresentationStatus; } + @VisibleForTesting + static void rotateRearDisplayMetricsIfNeeded( + @Surface.Rotation int defaultDisplayRotation, + @Surface.Rotation int rearDisplayRotation, + @NonNull DisplayMetrics inOutMetrics) { + // If the rear display has a non-zero rotation, it means the backing DisplayContent / + // DisplayRotation is fresh. + if (rearDisplayRotation != Surface.ROTATION_0) { + return; + } + + // If the default display is 0 or 180, the rear display must also be 0 or 180. + if (defaultDisplayRotation == Surface.ROTATION_0 + || defaultDisplayRotation == Surface.ROTATION_180) { + return; + } + + final int heightPixels = inOutMetrics.heightPixels; + final int widthPixels = inOutMetrics.widthPixels; + inOutMetrics.widthPixels = heightPixels; + inOutMetrics.heightPixels = widthPixels; + + final int noncompatHeightPixels = inOutMetrics.noncompatHeightPixels; + final int noncompatWidthPixels = inOutMetrics.noncompatWidthPixels; + inOutMetrics.noncompatWidthPixels = noncompatHeightPixels; + inOutMetrics.noncompatHeightPixels = noncompatWidthPixels; + } + /** * Callback for the {@link DeviceStateRequest} to be notified of when the request has been * activated or cancelled. This callback provides information to the client library diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java new file mode 100644 index 000000000000..ccb4ebe9199e --- /dev/null +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java @@ -0,0 +1,96 @@ +/* + * 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 androidx.window.extensions.area; + +import static org.junit.Assert.assertEquals; + +import android.platform.test.annotations.Presubmit; +import android.util.DisplayMetrics; +import android.view.Surface; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WindowAreaComponentImplTests { + + private final DisplayMetrics mTestDisplayMetrics = new DisplayMetrics(); + + @Before + public void setup() { + mTestDisplayMetrics.widthPixels = 1; + mTestDisplayMetrics.heightPixels = 2; + mTestDisplayMetrics.noncompatWidthPixels = 3; + mTestDisplayMetrics.noncompatHeightPixels = 4; + } + + /** + * Cases where the rear display metrics does not need to be transformed. + */ + @Test + public void testRotateRearDisplayMetrics_noTransformNeeded() { + final DisplayMetrics originalMetrics = new DisplayMetrics(); + originalMetrics.setTo(mTestDisplayMetrics); + + WindowAreaComponentImpl.rotateRearDisplayMetricsIfNeeded( + Surface.ROTATION_0, Surface.ROTATION_0, mTestDisplayMetrics); + assertEquals(originalMetrics, mTestDisplayMetrics); + + WindowAreaComponentImpl.rotateRearDisplayMetricsIfNeeded( + Surface.ROTATION_180, Surface.ROTATION_180, mTestDisplayMetrics); + assertEquals(originalMetrics, mTestDisplayMetrics); + + WindowAreaComponentImpl.rotateRearDisplayMetricsIfNeeded( + Surface.ROTATION_0, Surface.ROTATION_180, mTestDisplayMetrics); + assertEquals(originalMetrics, mTestDisplayMetrics); + + WindowAreaComponentImpl.rotateRearDisplayMetricsIfNeeded( + Surface.ROTATION_180, Surface.ROTATION_0, mTestDisplayMetrics); + assertEquals(originalMetrics, mTestDisplayMetrics); + } + + /** + * Cases where the rear display metrics need to be transformed. + */ + @Test + public void testRotateRearDisplayMetrics_transformNeeded() { + DisplayMetrics originalMetrics = new DisplayMetrics(); + originalMetrics.setTo(mTestDisplayMetrics); + + DisplayMetrics expectedMetrics = new DisplayMetrics(); + expectedMetrics.setTo(mTestDisplayMetrics); + expectedMetrics.widthPixels = mTestDisplayMetrics.heightPixels; + expectedMetrics.heightPixels = mTestDisplayMetrics.widthPixels; + expectedMetrics.noncompatWidthPixels = mTestDisplayMetrics.noncompatHeightPixels; + expectedMetrics.noncompatHeightPixels = mTestDisplayMetrics.noncompatWidthPixels; + + WindowAreaComponentImpl.rotateRearDisplayMetricsIfNeeded( + Surface.ROTATION_90, Surface.ROTATION_0, mTestDisplayMetrics); + assertEquals(expectedMetrics, mTestDisplayMetrics); + + mTestDisplayMetrics.setTo(originalMetrics); + WindowAreaComponentImpl.rotateRearDisplayMetricsIfNeeded( + Surface.ROTATION_270, Surface.ROTATION_0, mTestDisplayMetrics); + assertEquals(expectedMetrics, mTestDisplayMetrics); + } +} diff --git a/libs/WindowManager/Shell/res/color/taskbar_background_dark.xml b/libs/WindowManager/Shell/res/color/taskbar_background_dark.xml index 01df006f1bd2..f75d842c0d57 100644 --- a/libs/WindowManager/Shell/res/color/taskbar_background_dark.xml +++ b/libs/WindowManager/Shell/res/color/taskbar_background_dark.xml @@ -16,5 +16,5 @@ --> <!-- Should be the same as in packages/apps/Launcher3/res/color-night-v31/taskbar_background.xml --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@android:color/system_neutral1_500" android:lStar="20" /> + <item android:color="@android:color/system_neutral1_500" android:lStar="6" /> </selector>
\ No newline at end of file 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 504839fedf06..7e09c989e1b3 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 @@ -422,6 +422,7 @@ public class Bubble implements BubbleViewProvider { } if (mBubbleBarExpandedView != null) { mBubbleBarExpandedView.cleanUpExpandedState(); + mBubbleBarExpandedView = null; } if (mIntent != null) { mIntent.unregisterCancelListener(mIntentCancelListener); @@ -549,10 +550,10 @@ public class Bubble implements BubbleViewProvider { /** * Set visibility of bubble in the expanded state. * - * @param visibility {@code true} if the expanded bubble should be visible on the screen. - * - * Note that this contents visibility doesn't affect visibility at {@link android.view.View}, + * <p>Note that this contents visibility doesn't affect visibility at {@link android.view.View}, * and setting {@code false} actually means rendering the expanded view in transparent. + * + * @param visibility {@code true} if the expanded bubble should be visible on the screen. */ @Override public void setTaskViewVisibility(boolean visibility) { 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 c48f2fdaf42f..cb03c096f7f1 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 @@ -64,7 +64,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.service.notification.NotificationListenerService; @@ -92,6 +91,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; +import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -143,16 +143,6 @@ public class BubbleController implements ConfigurationChangeListener, private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav"; - // TODO(b/256873975) Should use proper flag when available to shell/launcher - /** - * Whether bubbles are showing in the bubble bar from launcher. This is only available - * on large screens and {@link BubbleController#isShowingAsBubbleBar()} should be used - * to check all conditions that indicate if the bubble bar is in use. - */ - private static final boolean BUBBLE_BAR_ENABLED = - SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false); - - /** * Common interface to send updates to bubble views. */ @@ -195,6 +185,7 @@ public class BubbleController implements ConfigurationChangeListener, private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; private final IWindowManager mWmService; + private final BubbleProperties mBubbleProperties; // Used to post to main UI thread private final ShellExecutor mMainExecutor; @@ -291,7 +282,8 @@ public class BubbleController implements ConfigurationChangeListener, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue, - IWindowManager wmService) { + IWindowManager wmService, + BubbleProperties bubbleProperties) { mContext = context; mShellCommandHandler = shellCommandHandler; mShellController = shellController; @@ -328,6 +320,7 @@ public class BubbleController implements ConfigurationChangeListener, mDragAndDropController = dragAndDropController; mSyncQueue = syncQueue; mWmService = wmService; + mBubbleProperties = bubbleProperties; shellInit.addInitCallback(this::onInit, this); } @@ -518,11 +511,14 @@ public class BubbleController implements ConfigurationChangeListener, /** * Sets a listener to be notified of bubble updates. This is used by launcher so that * it may render bubbles in itself. Only one listener is supported. + * + * <p>If bubble bar is supported, bubble views will be updated to switch to bar mode. */ public void registerBubbleStateListener(Bubbles.BubbleStateListener listener) { - if (isShowingAsBubbleBar()) { - // Only set the listener if bubble bar is showing. + if (canShowAsBubbleBar() && listener != null) { + // Only set the listener if we can show the bubble bar. mBubbleStateListener = listener; + setUpBubbleViewsForMode(); sendInitialListenerUpdate(); } else { mBubbleStateListener = null; @@ -531,9 +527,15 @@ public class BubbleController implements ConfigurationChangeListener, /** * Unregisters the {@link Bubbles.BubbleStateListener}. + * + * <p>If there's an existing listener, then we're switching back to stack mode and bubble views + * will be updated accordingly. */ public void unregisterBubbleStateListener() { - mBubbleStateListener = null; + if (mBubbleStateListener != null) { + mBubbleStateListener = null; + setUpBubbleViewsForMode(); + } } /** @@ -645,8 +647,12 @@ public class BubbleController implements ConfigurationChangeListener, /** Whether bubbles are showing in the bubble bar. */ public boolean isShowingAsBubbleBar() { - // TODO(b/269670598): should also check that we're in gesture nav - return BUBBLE_BAR_ENABLED && mBubblePositioner.isLargeScreen(); + return canShowAsBubbleBar() && mBubbleStateListener != null; + } + + /** Whether the current configuration supports showing as bubble bar. */ + private boolean canShowAsBubbleBar() { + return mBubbleProperties.isBubbleBarEnabled() && mBubblePositioner.isLargeScreen(); } /** Whether this userId belongs to the current user. */ @@ -774,12 +780,12 @@ public class BubbleController implements ConfigurationChangeListener, try { mAddedToWindowManager = true; registerBroadcastReceiver(); - mBubbleData.getOverflow().initialize(this); + mBubbleData.getOverflow().initialize(this, isShowingAsBubbleBar()); // (TODO: b/273314541) some duplication in the inset listener if (isShowingAsBubbleBar()) { mWindowManager.addView(mLayerView, mWmLayoutParams); mLayerView.setOnApplyWindowInsetsListener((view, windowInsets) -> { - if (!windowInsets.equals(mWindowInsets)) { + if (!windowInsets.equals(mWindowInsets) && mLayerView != null) { mWindowInsets = windowInsets; mBubblePositioner.update(); mLayerView.onDisplaySizeChanged(); @@ -789,7 +795,7 @@ public class BubbleController implements ConfigurationChangeListener, } else { mWindowManager.addView(mStackView, mWmLayoutParams); mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> { - if (!windowInsets.equals(mWindowInsets)) { + if (!windowInsets.equals(mWindowInsets) && mStackView != null) { mWindowInsets = windowInsets; mBubblePositioner.update(); mStackView.onDisplaySizeChanged(); @@ -1066,9 +1072,18 @@ public class BubbleController implements ConfigurationChangeListener, * Expands and selects the provided bubble as long as it already exists in the stack or the * overflow. * - * This is used by external callers (launcher). + * <p>This is used by external callers (launcher). */ - public void expandStackAndSelectBubbleFromLauncher(String key) { + @VisibleForTesting + public void expandStackAndSelectBubbleFromLauncher(String key, boolean onLauncherHome) { + mBubblePositioner.setShowingInBubbleBar(onLauncherHome); + + if (BubbleOverflow.KEY.equals(key)) { + mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); + mLayerView.showExpandedView(mBubbleData.getOverflow()); + return; + } + Bubble b = mBubbleData.getAnyBubbleWithkey(key); if (b == null) { return; @@ -1286,6 +1301,50 @@ public class BubbleController implements ConfigurationChangeListener, }); } + void setUpBubbleViewsForMode() { + mBubbleViewCallback = isShowingAsBubbleBar() + ? mBubbleBarViewCallback + : mBubbleStackViewCallback; + + // reset the overflow so that it can be re-added later if needed. + if (mStackView != null) { + mStackView.resetOverflowView(); + mStackView.removeAllViews(); + } + // cleanup existing bubble views so they can be recreated later if needed. + mBubbleData.getBubbles().forEach(Bubble::cleanupViews); + + // remove the current bubble container from window manager, null it out, and create a new + // container based on the current mode. + removeFromWindowManagerMaybe(); + mLayerView = null; + mStackView = null; + ensureBubbleViewsAndWindowCreated(); + + // inflate bubble views + BubbleViewInfoTask.Callback callback = null; + if (!isShowingAsBubbleBar()) { + callback = b -> { + if (mStackView != null) { + mStackView.addBubble(b); + mStackView.setSelectedBubble(b); + } else { + Log.w(TAG, "Tried to add a bubble to the stack but the stack is null"); + } + }; + } + for (int i = mBubbleData.getBubbles().size() - 1; i >= 0; i--) { + Bubble bubble = mBubbleData.getBubbles().get(i); + bubble.inflate(callback, + mContext, + this, + mStackView, + mLayerView, + mBubbleIconFactory, + false /* skipInflation */); + } + } + /** * Adds or updates a bubble associated with the provided notification entry. * @@ -1746,7 +1805,7 @@ public class BubbleController implements ConfigurationChangeListener, // Update the cached state for queries from SysUI mImpl.mCachedState.update(update); - if (isShowingAsBubbleBar() && mBubbleStateListener != null) { + if (isShowingAsBubbleBar()) { BubbleBarUpdate bubbleBarUpdate = update.toBubbleBarUpdate(); // Some updates aren't relevant to the bubble bar so check first. if (bubbleBarUpdate.anythingChanged()) { @@ -1868,10 +1927,17 @@ public class BubbleController implements ConfigurationChangeListener, } @VisibleForTesting + @Nullable public BubbleStackView getStackView() { return mStackView; } + @VisibleForTesting + @Nullable + public BubbleBarLayerView getLayerView() { + return mLayerView; + } + /** * Check if notification panel is in an expanded state. * Makes a call to System UI process and delivers the result via {@code callback} on the @@ -2010,22 +2076,18 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void registerBubbleListener(IBubblesListener listener) { - mMainExecutor.execute(() -> { - mListener.register(listener); - }); + mMainExecutor.execute(() -> mListener.register(listener)); } @Override public void unregisterBubbleListener(IBubblesListener listener) { - mMainExecutor.execute(() -> mListener.unregister()); + mMainExecutor.execute(mListener::unregister); } @Override public void showBubble(String key, boolean onLauncherHome) { - mMainExecutor.execute(() -> { - mBubblePositioner.setShowingInBubbleBar(onLauncherHome); - mController.expandStackAndSelectBubbleFromLauncher(key); - }); + mMainExecutor.execute( + () -> mController.expandStackAndSelectBubbleFromLauncher(key, onLauncherHome)); } @Override @@ -2037,11 +2099,6 @@ public class BubbleController implements ConfigurationChangeListener, public void collapseBubbles() { mMainExecutor.execute(() -> mController.collapseStack()); } - - @Override - public void onTaskbarStateChanged(int newState) { - // TODO (b/269670598) - } } private class BubblesImpl implements Bubbles { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt index e37c785f15f5..942dcd9db54c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt @@ -33,6 +33,7 @@ import com.android.wm.shell.common.ShellExecutor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch import kotlinx.coroutines.yield @@ -45,7 +46,7 @@ internal class BubbleDataRepository( private val volatileRepository = BubbleVolatileRepository(launcherApps) private val persistentRepository = BubblePersistentRepository(context) - private val ioScope = CoroutineScope(Dispatchers.IO) + private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var job: Job? = null // For use in Bubble construction. @@ -131,7 +132,7 @@ internal class BubbleDataRepository( */ private fun persistToDisk() { val prev = job - job = ioScope.launch { + job = coroutineScope.launch { // if there was an ongoing disk I/O operation, they can be cancelled prev?.cancelAndJoin() // check for cancellation before disk I/O @@ -148,7 +149,7 @@ internal class BubbleDataRepository( * bubbles. */ @SuppressLint("WrongConstant") - fun loadBubbles(userId: Int, cb: (List<Bubble>) -> Unit) = ioScope.launch { + fun loadBubbles(userId: Int, cb: (List<Bubble>) -> Unit) = coroutineScope.launch { /** * Load BubbleEntity from disk. * e.g. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index e1a3f3a1ac5d..67185655f2d2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -948,9 +948,9 @@ public class BubbleExpandedView extends LinearLayout { mTaskView.onLocationChanged(); } if (mIsOverflow) { - post(() -> { - mOverflowView.show(); - }); + // post this to the looper so that the view has a chance to be laid out before it can + // calculate row and column sizes correctly. + post(() -> mOverflowView.show()); } } 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 df7f5b4f0150..df19757203eb 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 @@ -44,6 +44,7 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl private val inflater: LayoutInflater = LayoutInflater.from(context) private var expandedView: BubbleExpandedView? + private var bubbleBarExpandedView: BubbleBarExpandedView? = null private var overflowBtn: BadgedImageView? init { @@ -53,19 +54,26 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl } /** Call before use and again if cleanUpExpandedState was called. */ - fun initialize(controller: BubbleController) { - createExpandedView() - getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */) + fun initialize(controller: BubbleController, forBubbleBar: Boolean) { + if (forBubbleBar) { + createBubbleBarExpandedView().initialize(controller, true /* isOverflow */) + } else { + createExpandedView() + .initialize(controller, controller.stackView, true /* isOverflow */) + } } fun cleanUpExpandedState() { expandedView?.cleanUpExpandedState() expandedView = null + bubbleBarExpandedView?.cleanUpExpandedState() + bubbleBarExpandedView = null } fun update() { updateResources() getExpandedView()?.applyThemeAttrs() + getBubbleBarExpandedView()?.applyThemeAttrs() // Apply inset and new style to fresh icon drawable. getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button) updateBtnTheme() @@ -151,26 +159,39 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl overflowBtn?.updateDotVisibility(true /* animate */) } - fun createExpandedView(): BubbleExpandedView? { - expandedView = + /** Creates the expanded view for bubbles showing in the stack view. */ + private fun createExpandedView(): BubbleExpandedView { + val view = inflater.inflate( R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */ ) as BubbleExpandedView - expandedView?.applyThemeAttrs() + view.applyThemeAttrs() + expandedView = view updateResources() - return expandedView + return view } override fun getExpandedView(): BubbleExpandedView? { return expandedView } - override fun getBubbleBarExpandedView(): BubbleBarExpandedView? { - return null + /** Creates the expanded view for bubbles showing in the bubble bar. */ + private fun createBubbleBarExpandedView(): BubbleBarExpandedView { + val view = + inflater.inflate( + R.layout.bubble_bar_expanded_view, + null, /* root */ + false /* attachToRoot*/ + ) as BubbleBarExpandedView + view.applyThemeAttrs() + bubbleBarExpandedView = view + return view } + override fun getBubbleBarExpandedView(): BubbleBarExpandedView? = bubbleBarExpandedView + override fun getDotColor(): Int { return dotColor } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index d101b0c4d7e8..cb08f93a9efa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -732,17 +732,25 @@ public class BubblePositioner { /** * How wide the expanded view should be when showing from the bubble bar. */ - public int getExpandedViewWidthForBubbleBar() { - return mExpandedViewLargeScreenWidth; + public int getExpandedViewWidthForBubbleBar(boolean isOverflow) { + return isOverflow ? mOverflowWidth : mExpandedViewLargeScreenWidth; } /** * How tall the expanded view should be when showing from the bubble bar. */ - public int getExpandedViewHeightForBubbleBar() { + public int getExpandedViewHeightForBubbleBar(boolean isOverflow) { + return isOverflow + ? mOverflowHeight + : getExpandedViewBottomForBubbleBar() - mInsets.top - mExpandedViewPadding; + } + + /** The bottom position of the expanded view when showing above the bubble bar. */ + public int getExpandedViewBottomForBubbleBar() { return getAvailableRect().height() + + mInsets.top - mBubbleBarSize - - mExpandedViewPadding * 2 + - mExpandedViewPadding - getBubbleBarHomeAdjustment(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 9860b076264b..282db9e18d81 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -309,6 +309,7 @@ public class BubbleStackView extends FrameLayout String bubblesOnScreen = BubbleDebugConfig.formatBubblesString( getBubblesOnScreen(), getExpandedBubble()); + pw.print(" stack visibility : "); pw.println(getVisibility()); pw.print(" bubbles on screen: "); pw.println(bubblesOnScreen); pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress); pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing()); @@ -970,6 +971,8 @@ public class BubbleStackView extends FrameLayout mBubbleContainer.bringToFront(); mBubbleOverflow = mBubbleData.getOverflow(); + + resetOverflowView(); mBubbleContainer.addView(mBubbleOverflow.getIconView(), mBubbleContainer.getChildCount() /* index */, new FrameLayout.LayoutParams(mPositioner.getBubbleSize(), @@ -1497,9 +1500,6 @@ public class BubbleStackView extends FrameLayout getViewTreeObserver().removeOnPreDrawListener(mViewUpdater); getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater); getViewTreeObserver().removeOnComputeInternalInsetsListener(this); - if (mBubbleOverflow != null) { - mBubbleOverflow.cleanUpExpandedState(); - } } @Override @@ -3414,6 +3414,19 @@ public class BubbleStackView extends FrameLayout } /** + * Removes the overflow view from the stack. This allows for re-adding it later to a new stack. + */ + void resetOverflowView() { + BadgedImageView overflowIcon = mBubbleOverflow.getIconView(); + if (overflowIcon != null) { + PhysicsAnimationLayout parent = (PhysicsAnimationLayout) overflowIcon.getParent(); + if (parent != null) { + parent.removeViewNoAnimation(overflowIcon); + } + } + } + + /** * Holds some commonly queried information about the stack. */ public static class StackViewState { 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 80e29998e8d3..66e69300f45f 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 @@ -176,44 +176,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask LayoutInflater inflater = LayoutInflater.from(c); info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); - info.bubbleBarExpandedView.initialize(controller); + info.bubbleBarExpandedView.initialize(controller, false /* isOverflow */); } - if (b.getShortcutInfo() != null) { - info.shortcutInfo = b.getShortcutInfo(); - } - - // App name & app icon - PackageManager pm = BubbleController.getPackageManagerForUser(c, - b.getUser().getIdentifier()); - ApplicationInfo appInfo; - Drawable badgedIcon; - Drawable appIcon; - try { - appInfo = pm.getApplicationInfo( - b.getPackageName(), - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE); - if (appInfo != null) { - info.appName = String.valueOf(pm.getApplicationLabel(appInfo)); - } - appIcon = pm.getApplicationIcon(b.getPackageName()); - badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser()); - } catch (PackageManager.NameNotFoundException exception) { - // If we can't find package... don't think we should show the bubble. - Log.w(TAG, "Unable to find package: " + b.getPackageName()); + if (!populateCommonInfo(info, c, b, iconFactory)) { + // if we failed to update common fields return null return null; } - info.rawBadgeBitmap = iconFactory.getBadgeBitmap(badgedIcon, false).icon; - float[] bubbleBitmapScale = new float[1]; - info.bubbleBitmap = iconFactory.getBubbleBitmap( - iconFactory.getBubbleDrawable(c, info.shortcutInfo, - b.getIcon()), bubbleBitmapScale); - - return info; } @@ -236,66 +206,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask info.expandedView.initialize(controller, stackView, false /* isOverflow */); } - if (b.getShortcutInfo() != null) { - info.shortcutInfo = b.getShortcutInfo(); - } - - // App name & app icon - PackageManager pm = BubbleController.getPackageManagerForUser(c, - b.getUser().getIdentifier()); - ApplicationInfo appInfo; - Drawable badgedIcon; - Drawable appIcon; - try { - appInfo = pm.getApplicationInfo( - b.getPackageName(), - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE); - if (appInfo != null) { - info.appName = String.valueOf(pm.getApplicationLabel(appInfo)); - } - appIcon = pm.getApplicationIcon(b.getPackageName()); - badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser()); - } catch (PackageManager.NameNotFoundException exception) { - // If we can't find package... don't think we should show the bubble. - Log.w(TAG, "Unable to find package: " + b.getPackageName()); + if (!populateCommonInfo(info, c, b, iconFactory)) { + // if we failed to update common fields return null return null; } - // Badged bubble image - Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, - b.getIcon()); - if (bubbleDrawable == null) { - // Default to app icon - bubbleDrawable = appIcon; - } - - BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon, - b.isImportantConversation()); - info.badgeBitmap = badgeBitmapInfo.icon; - // Raw badge bitmap never includes the important conversation ring - info.rawBadgeBitmap = b.isImportantConversation() - ? iconFactory.getBadgeBitmap(badgedIcon, false).icon - : badgeBitmapInfo.icon; - - float[] bubbleBitmapScale = new float[1]; - info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale); - - // Dot color & placement - Path iconPath = PathParser.createPathFromPathData( - c.getResources().getString(com.android.internal.R.string.config_icon_mask)); - Matrix matrix = new Matrix(); - float scale = bubbleBitmapScale[0]; - float radius = DEFAULT_PATH_SIZE / 2f; - matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */, - radius /* pivot y */); - iconPath.transform(matrix); - info.dotPath = iconPath; - info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color, - Color.WHITE, WHITE_SCRIM_ALPHA); - // Flyout info.flyoutMessage = b.getFlyoutMessage(); if (info.flyoutMessage != null) { @@ -306,6 +221,75 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask } } + /** + * Modifies the given {@code info} object and populates common fields in it. + * + * <p>This method returns {@code true} if the update was successful and {@code false} otherwise. + * Callers should assume that the info object is unusable if the update was unsuccessful. + */ + private static boolean populateCommonInfo( + BubbleViewInfo info, Context c, Bubble b, BubbleIconFactory iconFactory) { + if (b.getShortcutInfo() != null) { + info.shortcutInfo = b.getShortcutInfo(); + } + + // App name & app icon + PackageManager pm = BubbleController.getPackageManagerForUser(c, + b.getUser().getIdentifier()); + ApplicationInfo appInfo; + Drawable badgedIcon; + Drawable appIcon; + try { + appInfo = pm.getApplicationInfo( + b.getPackageName(), + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (appInfo != null) { + info.appName = String.valueOf(pm.getApplicationLabel(appInfo)); + } + appIcon = pm.getApplicationIcon(b.getPackageName()); + badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser()); + } catch (PackageManager.NameNotFoundException exception) { + // If we can't find package... don't think we should show the bubble. + Log.w(TAG, "Unable to find package: " + b.getPackageName()); + return false; + } + + // Badged bubble image + Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, b.getIcon()); + if (bubbleDrawable == null) { + // Default to app icon + bubbleDrawable = appIcon; + } + + BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon, + b.isImportantConversation()); + info.badgeBitmap = badgeBitmapInfo.icon; + // Raw badge bitmap never includes the important conversation ring + info.rawBadgeBitmap = b.isImportantConversation() // is this needed for bar? + ? iconFactory.getBadgeBitmap(badgedIcon, false).icon + : badgeBitmapInfo.icon; + + float[] bubbleBitmapScale = new float[1]; + info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale); + + // Dot color & placement + Path iconPath = PathParser.createPathFromPathData( + c.getResources().getString(com.android.internal.R.string.config_icon_mask)); + Matrix matrix = new Matrix(); + float scale = bubbleBitmapScale[0]; + float radius = DEFAULT_PATH_SIZE / 2f; + matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */, + radius /* pivot y */); + iconPath.transform(matrix); + info.dotPath = iconPath; + info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color, + Color.WHITE, WHITE_SCRIM_ALPHA); + return true; + } + @Nullable static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) { Objects.requireNonNull(context); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 4d329dd5d446..759246eb285d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -60,9 +60,11 @@ public interface Bubbles { DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, - DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED}) + DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED, + DISMISS_SWITCH_TO_STACK}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) - @interface DismissReason {} + @interface DismissReason { + } int DISMISS_USER_GESTURE = 1; int DISMISS_AGED = 2; @@ -80,6 +82,7 @@ public interface Bubbles { int DISMISS_NO_BUBBLE_UP = 14; int DISMISS_RELOAD_FROM_DISK = 15; int DISMISS_USER_REMOVED = 16; + int DISMISS_SWITCH_TO_STACK = 17; /** Returns a binder that can be passed to an external process to manipulate Bubbles. */ default IBubbles createExternalInterface() { @@ -120,8 +123,8 @@ public interface Bubbles { /** * This method has different behavior depending on: - * - if an app bubble exists - * - if an app bubble is expanded + * - if an app bubble exists + * - if an app bubble is expanded * * If no app bubble exists, this will add and expand a bubble with the provided intent. The * intent must be explicit (i.e. include a package name or fully qualified component class name) @@ -135,13 +138,13 @@ public interface Bubbles { * the bubble or bubble stack. * * Some notes: - * - Only one app bubble is supported at a time, regardless of users. Multi-users support is - * tracked in b/273533235. - * - Calling this method with a different intent than the existing app bubble will do nothing + * - Only one app bubble is supported at a time, regardless of users. Multi-users support is + * tracked in b/273533235. + * - Calling this method with a different intent than the existing app bubble will do nothing * * @param intent the intent to display in the bubble expanded view. - * @param user the {@link UserHandle} of the user to start this activity for. - * @param icon the {@link Icon} to use for the bubble view. + * @param user the {@link UserHandle} of the user to start this activity for. + * @param icon the {@link Icon} to use for the bubble view. */ void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon); @@ -172,13 +175,12 @@ public interface Bubbles { * {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add * {@link BubbleData#addSummaryToSuppress}. * - * @param entry the notification of the BubbleEntry should be removed. - * @param children the list of child notification of the BubbleEntry from 1st param entry, - * this will be null if entry does have no children. + * @param entry the notification of the BubbleEntry should be removed. + * @param children the list of child notification of the BubbleEntry from 1st param entry, + * this will be null if entry does have no children. * @param removeCallback the remove callback for SystemUI side to remove notification, the int * number should be list position of children list and use -1 for * removing the parent notification. - * * @return true if we want to intercept the dismissal of the entry, else false. */ boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children, @@ -200,9 +202,9 @@ public interface Bubbles { /** * Called when new notification entry updated. * - * @param entry the {@link BubbleEntry} by the notification. + * @param entry the {@link BubbleEntry} by the notification. * @param shouldBubbleUp {@code true} if this notification should bubble up. - * @param fromSystem {@code true} if this update is from NotificationManagerService. + * @param fromSystem {@code true} if this update is from NotificationManagerService. */ void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem); @@ -218,7 +220,7 @@ public interface Bubbles { * filtering and sorting. This is used to dismiss or create bubbles based on changes in * permissions on the notification channel or the global setting. * - * @param rankingMap the updated ranking map from NotificationListenerService + * @param rankingMap the updated ranking map from NotificationListenerService * @param entryDataByKey a map of ranking key to bubble entry and whether the entry should * bubble up */ @@ -230,9 +232,9 @@ public interface Bubbles { * Called when a notification channel is modified, in response to * {@link NotificationListenerService#onNotificationChannelModified}. * - * @param pkg the package the notification channel belongs to. - * @param user the user the notification channel belongs to. - * @param channel the channel being modified. + * @param pkg the package the notification channel belongs to. + * @param user the user the notification channel belongs to. + * @param channel the channel being modified. * @param modificationType the type of modification that occurred to the channel. */ void onNotificationChannelModified( @@ -300,7 +302,7 @@ public interface Bubbles { * Called when the expansion state of the bubble stack changes. * * @param isExpanding whether it's expanding or collapsing - * @param key the notification key associated with bubble being expanded + * @param key the notification key associated with bubble being expanded */ void onBubbleExpandChanged(boolean isExpanding, String key); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 862e818a998b..20ae8469f431 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -35,6 +35,4 @@ interface IBubbles { oneway void collapseBubbles() = 5; - oneway void onTaskbarStateChanged(in int newState) = 6; - }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java index 55052e614458..f3cc514d2972 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java @@ -327,6 +327,12 @@ public class PhysicsAnimationLayout extends FrameLayout { addViewInternal(child, index, params, false /* isReorder */); } + /** Removes the child view immediately. */ + public void removeViewNoAnimation(View view) { + super.removeView(view); + view.setTag(R.id.physics_animator_tag, null); + } + @Override public void removeView(View view) { if (mController != null) { @@ -523,6 +529,7 @@ public class PhysicsAnimationLayout extends FrameLayout { */ @Nullable private SpringAnimation getSpringAnimationFromView( DynamicAnimation.ViewProperty property, View view) { + if (view == null) return null; return (SpringAnimation) view.getTag(getTagIdForProperty(property)); } @@ -531,11 +538,13 @@ public class PhysicsAnimationLayout extends FrameLayout { * system. */ @Nullable private ViewPropertyAnimator getViewPropertyAnimatorFromView(View view) { + if (view == null) return null; return (ViewPropertyAnimator) view.getTag(R.id.reorder_animator_tag); } /** Retrieves the target animator from the view via the view tag system. */ @Nullable private ObjectAnimator getTargetAnimatorFromView(View view) { + if (view == null) return null; return (ObjectAnimator) view.getTag(R.id.target_animator_tag); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 23f65f943aa4..f729d029a818 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -27,6 +27,7 @@ import android.widget.FrameLayout; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleViewProvider; import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix; @@ -215,9 +216,10 @@ public class BubbleBarAnimationHelper { } BubbleBarExpandedView bbev = mExpandedBubble.getBubbleBarExpandedView(); + boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY); final int padding = mPositioner.getBubbleBarExpandedViewPadding(); - final int width = mPositioner.getExpandedViewWidthForBubbleBar(); - final int height = mPositioner.getExpandedViewHeightForBubbleBar(); + final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded); + final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bbev.getLayoutParams(); lp.width = width; lp.height = height; @@ -227,7 +229,8 @@ public class BubbleBarAnimationHelper { } else { bbev.setX(mPositioner.getAvailableRect().width() - width - padding); } - bbev.setY(mPositioner.getInsets().top + padding); + bbev.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height); bbev.updateLocation(); + bbev.maybeShowOverflow(); } } 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 32ed10258eee..396aa0e56cf6 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 @@ -25,6 +25,7 @@ import android.graphics.Color; import android.graphics.Outline; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewOutlineProvider; import android.widget.FrameLayout; @@ -33,6 +34,7 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.bubbles.BubbleOverflowContainerView; import com.android.wm.shell.bubbles.BubbleTaskViewHelper; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.taskview.TaskView; @@ -51,6 +53,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private static final int INVALID_TASK_ID = -1; private BubbleController mController; + private boolean mIsOverflow; private BubbleTaskViewHelper mBubbleTaskViewHelper; private BubbleBarMenuViewController mMenuViewController; private @Nullable Supplier<Rect> mLayerBoundsSupplier; @@ -58,6 +61,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext()); private @Nullable TaskView mTaskView; + private @Nullable BubbleOverflowContainerView mOverflowView; private int mHandleHeight; private int mBackgroundColor; @@ -114,15 +118,25 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } /** Set the BubbleController on the view, must be called before doing anything else. */ - public void initialize(BubbleController controller) { + public void initialize(BubbleController controller, boolean isOverflow) { mController = controller; - mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController, - /* listener= */ this, - /* viewParent= */ this); - mTaskView = mBubbleTaskViewHelper.getTaskView(); - addView(mTaskView); - mTaskView.setEnableSurfaceClipping(true); - mTaskView.setCornerRadius(mCornerRadius); + mIsOverflow = isOverflow; + + if (mIsOverflow) { + mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate( + R.layout.bubble_overflow_container, null /* root */); + mOverflowView.setBubbleController(mController); + addView(mOverflowView); + } else { + + mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController, + /* listener= */ this, + /* viewParent= */ this); + mTaskView = mBubbleTaskViewHelper.getTaskView(); + addView(mTaskView); + mTaskView.setEnableSurfaceClipping(true); + mTaskView.setCornerRadius(mCornerRadius); + } mMenuViewController = new BubbleBarMenuViewController(mContext, this); mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() { @Override @@ -156,7 +170,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } // TODO (b/275087636): call this when theme/config changes - void applyThemeAttrs() { + /** Updates the view based on the current theme. */ + public void applyThemeAttrs() { boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows( mContext.getResources()); final TypedArray ta = mContext.obtainStyledAttributes(new int[]{ @@ -257,8 +272,18 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView * Call when the location or size of the view has changed to update TaskView. */ public void updateLocation() { - if (mTaskView == null) return; - mTaskView.onLocationChanged(); + if (mTaskView != null) { + mTaskView.onLocationChanged(); + } + } + + /** Shows the expanded view for the overflow if it exists. */ + void maybeShowOverflow() { + if (mOverflowView != null) { + // post this to the looper so that the view has a chance to be laid out before it can + // calculate row and column sizes correctly. + post(() -> mOverflowView.show()); + } } /** Sets the alpha of the task view. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index bc622e7a7a3e..d20b33edf2c9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -29,6 +29,7 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleViewProvider; @@ -146,8 +147,9 @@ public class BubbleBarLayerView extends FrameLayout if (mExpandedView == null) { mExpandedBubble = b; mExpandedView = expandedView; - final int width = mPositioner.getExpandedViewWidthForBubbleBar(); - final int height = mPositioner.getExpandedViewHeightForBubbleBar(); + boolean isOverflowExpanded = b.getKey().equals(BubbleOverflow.KEY); + final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded); + final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded); mExpandedView.setVisibility(GONE); mExpandedView.setUnBubbleConversationCallback(mUnBubbleConversationCallback); mExpandedView.setLayerBoundsSupplier(() -> new Rect(0, 0, getWidth(), getHeight())); @@ -156,6 +158,7 @@ public class BubbleBarLayerView extends FrameLayout mUnBubbleConversationCallback.accept(bubbleKey); } }); + mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height); addView(mExpandedView, new FrameLayout.LayoutParams(width, height)); } @@ -184,9 +187,10 @@ public class BubbleBarLayerView extends FrameLayout /** Updates the expanded view size and position. */ private void updateExpandedView() { if (mExpandedView == null) return; + boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY); final int padding = mPositioner.getBubbleBarExpandedViewPadding(); - final int width = mPositioner.getExpandedViewWidthForBubbleBar(); - final int height = mPositioner.getExpandedViewHeightForBubbleBar(); + final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded); + final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded); FrameLayout.LayoutParams lp = (LayoutParams) mExpandedView.getLayoutParams(); lp.width = width; lp.height = height; @@ -196,7 +200,7 @@ public class BubbleBarLayerView extends FrameLayout } else { mExpandedView.setX(mPositioner.getAvailableRect().width() - width - padding); } - mExpandedView.setY(mPositioner.getInsets().top + padding); + mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height); mExpandedView.updateLocation(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt new file mode 100644 index 000000000000..85aaa8ef585c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.bubbles.properties + +/** + * An interface for exposing bubble properties via flags which can be controlled easily in tests. + */ +interface BubbleProperties { + /** + * Whether bubble bar is enabled. + * + * When this is `true`, depending on additional factors, such as screen size and taskbar state, + * bubbles will be displayed in the bubble bar instead of floating. + * + * When this is `false`, bubbles will be floating. + */ + val isBubbleBarEnabled: Boolean +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfoModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt index 72a44bd198f2..9d8b9a6f3260 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfoModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -14,25 +14,14 @@ * limitations under the License. */ -package com.android.keyguard.clock; +package com.android.wm.shell.bubbles.properties -import java.util.List; +import android.os.SystemProperties -import dagger.Module; -import dagger.Provides; +/** Provides bubble properties in production. */ +object ProdBubbleProperties : BubbleProperties { -/** - * Dagger Module for clock package. - * - * @deprecated Migrate to ClockRegistry - */ -@Module -@Deprecated -public abstract class ClockInfoModule { - - /** */ - @Provides - public static List<ClockInfo> provideClockInfoList(ClockManager clockManager) { - return clockManager.getClockInfos(); - } + // TODO(b/256873975) Should use proper flag when available to shell/launcher + override val isBubbleBarEnabled = + SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false) } 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 f663d4d758f7..03f92aaf9ce9 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 @@ -36,6 +36,7 @@ import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubblePositioner; +import com.android.wm.shell.bubbles.properties.ProdBubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; @@ -64,7 +65,6 @@ import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; -import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; @@ -188,7 +188,7 @@ public abstract class WMShellModule { statusBarService, windowManager, windowManagerShellWrapper, userManager, launcherApps, logger, taskStackListener, organizer, positioner, displayController, oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, - taskViewTransitions, syncQueue, wmService); + taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE); } // @@ -220,12 +220,12 @@ public abstract class WMShellModule { desktopTasksController); } return new CaptionWindowDecorViewModel( - context, - mainHandler, - mainChoreographer, - taskOrganizer, - displayController, - syncQueue); + context, + mainHandler, + mainChoreographer, + taskOrganizer, + displayController, + syncQueue); } // @@ -585,13 +585,13 @@ public abstract class WMShellModule { animators.add(fullscreenAnimator); return new UnfoldAnimationController( - shellInit, - transactionPool, - progressProvider.get(), - animators, - unfoldTransitionHandler, - mainExecutor - ); + shellInit, + transactionPool, + progressProvider.get(), + animators, + unfoldTransitionHandler, + mainExecutor + ); } @Provides @@ -735,28 +735,6 @@ public abstract class WMShellModule { } // - // Kids mode - // - @WMSingleton - @Provides - static KidsModeTaskOrganizer provideKidsModeTaskOrganizer( - Context context, - ShellInit shellInit, - ShellCommandHandler shellCommandHandler, - SyncTransactionQueue syncTransactionQueue, - DisplayController displayController, - DisplayInsetsController displayInsetsController, - Optional<UnfoldAnimationController> unfoldAnimationController, - Optional<RecentTasksController> recentTasksOptional, - @ShellMainThread ShellExecutor mainExecutor, - @ShellMainThread Handler mainHandler - ) { - return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler, - syncTransactionQueue, displayController, displayInsetsController, - unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler); - } - - // // Misc // @@ -767,7 +745,6 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DefaultMixedHandler defaultMixedHandler, - KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional<DesktopModeController> desktopModeController) { return new Object(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 2b763ad8f14d..65a35b2ffcb3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -33,6 +33,7 @@ import android.os.IBinder import android.os.SystemProperties import android.util.DisplayMetrics.DENSITY_DEFAULT import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN @@ -267,16 +268,24 @@ class DesktopTasksController( */ fun cancelMoveToFreeform(task: RunningTaskInfo, position: Point) { KtProtoLog.v( - WM_SHELL_DESKTOP_MODE, - "DesktopTasksController: cancelMoveToFreeform taskId=%d", - task.taskId + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: cancelMoveToFreeform taskId=%d", + task.taskId ) val wct = WindowContainerTransaction() - addMoveToFullscreenChanges(wct, task) + wct.setBounds(task.token, null) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { - enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, position, - mOnAnimationFinishedCallback) + enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode( + wct, position) { t -> + val callbackWCT = WindowContainerTransaction() + visualIndicator?.releaseVisualIndicator(t) + visualIndicator = null + addMoveToFullscreenChanges(callbackWCT, task) + shellTaskOrganizer.applyTransaction(callbackWCT) + } } else { + addMoveToFullscreenChanges(wct, task) shellTaskOrganizer.applyTransaction(wct) releaseVisualIndicator() } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index 3733b919e366..3e175f3a9ae2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -17,7 +17,6 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -200,7 +199,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition } if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE - && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM && mPosition != null) { // This Transition animates a task to fullscreen after being dragged from the status // bar and then released back into the status bar area diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index cef7e1666331..56bd188a0d7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -156,6 +156,8 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler "start keyguard %s transition, info = %s", description, info); try { + mStartedTransitions.put(transition, + new StartedTransition(info, finishTransaction, remoteHandler)); remoteHandler.startAnimation(transition, info, startTransaction, new IRemoteTransitionFinishedCallback.Stub() { @Override @@ -164,14 +166,13 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler if (sct != null) { finishTransaction.merge(sct); } - mMainExecutor.execute(() -> { + // Post our finish callback to let startAnimation finish first. + mMainExecutor.executeDelayed(() -> { mStartedTransitions.remove(transition); finishCallback.onTransitionFinished(wct, null); - }); + }, 0); } }); - mStartedTransitions.put(transition, - new StartedTransition(info, finishTransaction, remoteHandler)); } catch (RemoteException e) { Log.wtf(TAG, "RemoteException thrown from local IRemoteTransition", e); return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeSettingsObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeSettingsObserver.java deleted file mode 100644 index 65cb7ac1e5f7..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeSettingsObserver.java +++ /dev/null @@ -1,92 +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.wm.shell.kidsmode; - -import android.annotation.NonNull; -import android.app.ActivityManager; -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.os.UserHandle; -import android.provider.Settings; - -import java.util.Collection; - -/** - * A ContentObserver for listening kids mode relative setting keys: - * - {@link Settings.Secure#NAVIGATION_MODE} - * - {@link Settings.Secure#NAV_BAR_KIDS_MODE} - * - * @hide - */ -public class KidsModeSettingsObserver extends ContentObserver { - private Context mContext; - private Runnable mOnChangeRunnable; - - public KidsModeSettingsObserver(Handler handler, Context context) { - super(handler); - mContext = context; - } - - public void setOnChangeRunnable(Runnable r) { - mOnChangeRunnable = r; - } - - /** - * Registers the observer. - */ - public void register() { - final ContentResolver r = mContext.getContentResolver(); - r.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.NAVIGATION_MODE), - false, this, UserHandle.USER_ALL); - r.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), - false, this, UserHandle.USER_ALL); - } - - /** - * Unregisters the observer. - */ - public void unregister() { - mContext.getContentResolver().unregisterContentObserver(this); - } - - @Override - public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, int flags, int userId) { - if (userId != ActivityManager.getCurrentUser()) { - return; - } - - if (mOnChangeRunnable != null) { - mOnChangeRunnable.run(); - } - } - - /** - * Returns true only when it's in three button nav mode and the kid nav bar mode is enabled. - * Otherwise, return false. - */ - public boolean isEnabled() { - return Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.NAVIGATION_MODE, 0, UserHandle.USER_CURRENT) == 0 - && Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java deleted file mode 100644 index 6d46a9c3d0e7..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java +++ /dev/null @@ -1,436 +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.wm.shell.kidsmode; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; -import static android.view.Display.DEFAULT_DISPLAY; - -import android.app.ActivityManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.view.Display; -import android.view.InsetsSource; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.WindowInsets; -import android.window.ITaskOrganizerController; -import android.window.TaskAppearedInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import androidx.annotation.NonNull; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.sysui.ShellCommandHandler; -import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.unfold.UnfoldAnimationController; - -import java.io.PrintWriter; -import java.util.List; -import java.util.Optional; - -/** - * A dedicated task organizer when kids mode is enabled. - * - Creates a root task with bounds that exclude the navigation bar area - * - Launch all task into the root task except for Launcher - */ -public class KidsModeTaskOrganizer extends ShellTaskOrganizer { - private static final String TAG = "KidsModeTaskOrganizer"; - - private static final int[] CONTROLLED_ACTIVITY_TYPES = - {ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_HOME}; - private static final int[] CONTROLLED_WINDOWING_MODES = - {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}; - - private final Handler mMainHandler; - private final Context mContext; - private final ShellCommandHandler mShellCommandHandler; - private final SyncTransactionQueue mSyncQueue; - private final DisplayController mDisplayController; - private final DisplayInsetsController mDisplayInsetsController; - - /** - * The value of the {@link R.bool.config_reverseDefaultRotation} property which defines how - * {@link Display#getRotation} values are mapped to screen orientations - */ - private final boolean mReverseDefaultRotationEnabled; - - @VisibleForTesting - ActivityManager.RunningTaskInfo mLaunchRootTask; - @VisibleForTesting - SurfaceControl mLaunchRootLeash; - @VisibleForTesting - final IBinder mCookie = new Binder(); - - private final InsetsState mInsetsState = new InsetsState(); - private int mDisplayWidth; - private int mDisplayHeight; - - private KidsModeSettingsObserver mKidsModeSettingsObserver; - private boolean mEnabled; - - private ActivityManager.RunningTaskInfo mHomeTask; - - private final BroadcastReceiver mUserSwitchIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - updateKidsModeState(); - } - }; - - DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener = - new DisplayController.OnDisplaysChangedListener() { - @Override - public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - final DisplayLayout displayLayout = - mDisplayController.getDisplayLayout(DEFAULT_DISPLAY); - if (displayLayout == null) { - return; - } - final int displayWidth = displayLayout.width(); - final int displayHeight = displayLayout.height(); - if (displayWidth == mDisplayWidth || displayHeight == mDisplayHeight) { - return; - } - mDisplayWidth = displayWidth; - mDisplayHeight = displayHeight; - updateBounds(); - } - }; - - DisplayInsetsController.OnInsetsChangedListener mOnInsetsChangedListener = - new DisplayInsetsController.OnInsetsChangedListener() { - @Override - public void insetsChanged(InsetsState insetsState) { - final boolean[] navigationBarChanged = {false}; - InsetsState.traverse(insetsState, mInsetsState, new InsetsState.OnTraverseCallbacks() { - @Override - public void onIdMatch(InsetsSource source1, InsetsSource source2) { - if (source1.getType() == WindowInsets.Type.navigationBars() - && !source1.equals(source2)) { - navigationBarChanged[0] = true; - } - } - - @Override - public void onIdNotFoundInState1(int index2, InsetsSource source2) { - if (source2.getType() == WindowInsets.Type.navigationBars()) { - navigationBarChanged[0] = true; - } - } - - @Override - public void onIdNotFoundInState2(int index1, InsetsSource source1) { - if (source1.getType() == WindowInsets.Type.navigationBars()) { - navigationBarChanged[0] = true; - } - } - }); - if (!navigationBarChanged[0]) { - return; - } - // Update bounds only when the insets of navigation bar or task bar is changed. - mInsetsState.set(insetsState); - updateBounds(); - } - }; - - @VisibleForTesting - KidsModeTaskOrganizer( - Context context, - ShellInit shellInit, - ShellCommandHandler shellCommandHandler, - ITaskOrganizerController taskOrganizerController, - SyncTransactionQueue syncTransactionQueue, - DisplayController displayController, - DisplayInsetsController displayInsetsController, - Optional<UnfoldAnimationController> unfoldAnimationController, - Optional<RecentTasksController> recentTasks, - KidsModeSettingsObserver kidsModeSettingsObserver, - ShellExecutor mainExecutor, - Handler mainHandler) { - // Note: we don't call super with the shell init because we will be initializing manually - super(/* shellInit= */ null, /* shellCommandHandler= */ null, taskOrganizerController, - /* compatUI= */ null, unfoldAnimationController, recentTasks, mainExecutor); - mContext = context; - mShellCommandHandler = shellCommandHandler; - mMainHandler = mainHandler; - mSyncQueue = syncTransactionQueue; - mDisplayController = displayController; - mDisplayInsetsController = displayInsetsController; - mKidsModeSettingsObserver = kidsModeSettingsObserver; - shellInit.addInitCallback(this::onInit, this); - mReverseDefaultRotationEnabled = context.getResources().getBoolean( - R.bool.config_reverseDefaultRotation); - } - - public KidsModeTaskOrganizer( - Context context, - ShellInit shellInit, - ShellCommandHandler shellCommandHandler, - SyncTransactionQueue syncTransactionQueue, - DisplayController displayController, - DisplayInsetsController displayInsetsController, - Optional<UnfoldAnimationController> unfoldAnimationController, - Optional<RecentTasksController> recentTasks, - ShellExecutor mainExecutor, - Handler mainHandler) { - // Note: we don't call super with the shell init because we will be initializing manually - super(/* shellInit= */ null, /* taskOrganizerController= */ null, /* compatUI= */ null, - unfoldAnimationController, recentTasks, mainExecutor); - mContext = context; - mShellCommandHandler = shellCommandHandler; - mMainHandler = mainHandler; - mSyncQueue = syncTransactionQueue; - mDisplayController = displayController; - mDisplayInsetsController = displayInsetsController; - shellInit.addInitCallback(this::onInit, this); - mReverseDefaultRotationEnabled = context.getResources().getBoolean( - R.bool.config_reverseDefaultRotation); - } - - /** - * Initializes kids mode status. - */ - public void onInit() { - if (mShellCommandHandler != null) { - mShellCommandHandler.addDumpCallback(this::dump, this); - } - if (mKidsModeSettingsObserver == null) { - mKidsModeSettingsObserver = new KidsModeSettingsObserver(mMainHandler, mContext); - } - mKidsModeSettingsObserver.setOnChangeRunnable(() -> updateKidsModeState()); - updateKidsModeState(); - mKidsModeSettingsObserver.register(); - - final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiverForAllUsers(mUserSwitchIntentReceiver, filter, null, mMainHandler); - } - - @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - if (mEnabled && mLaunchRootTask == null && taskInfo.launchCookies != null - && taskInfo.launchCookies.contains(mCookie)) { - mLaunchRootTask = taskInfo; - mLaunchRootLeash = leash; - updateTask(); - } - super.onTaskAppeared(taskInfo, leash); - - // Only allow home to draw under system bars. - if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) { - final WindowContainerTransaction wct = getWindowContainerTransaction(); - wct.setBounds(taskInfo.token, new Rect(0, 0, mDisplayWidth, mDisplayHeight)); - mSyncQueue.queue(wct); - mHomeTask = taskInfo; - } - mSyncQueue.runInSync(t -> { - // Reset several properties back to fullscreen (PiP, for example, leaves all these - // properties in a bad state). - t.setCrop(leash, null); - t.setPosition(leash, 0, 0); - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - }); - } - - @Override - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - if (mLaunchRootTask != null && mLaunchRootTask.taskId == taskInfo.taskId - && !taskInfo.equals(mLaunchRootTask)) { - mLaunchRootTask = taskInfo; - } - - if (mHomeTask != null && mHomeTask.taskId == taskInfo.taskId - && !taskInfo.equals(mHomeTask)) { - mHomeTask = taskInfo; - } - - super.onTaskInfoChanged(taskInfo); - } - - @VisibleForTesting - void updateKidsModeState() { - final boolean enabled = mKidsModeSettingsObserver.isEnabled(); - if (mEnabled == enabled) { - return; - } - mEnabled = enabled; - if (mEnabled) { - enable(); - } else { - disable(); - } - } - - @VisibleForTesting - void enable() { - // Needed since many Kids apps aren't optimised to support both orientations and it will be - // hard for kids to understand the app compat mode. - // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once possible. - if (mReverseDefaultRotationEnabled) { - setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true, - /* fromOrientations */ - new int[]{SCREEN_ORIENTATION_LANDSCAPE, SCREEN_ORIENTATION_REVERSE_LANDSCAPE}, - /* toOrientations */ - new int[]{SCREEN_ORIENTATION_SENSOR_LANDSCAPE, - SCREEN_ORIENTATION_SENSOR_LANDSCAPE}); - } else { - setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ true, - /* fromOrientations */ null, /* toOrientations */ null); - } - final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY); - if (displayLayout != null) { - mDisplayWidth = displayLayout.width(); - mDisplayHeight = displayLayout.height(); - } - mInsetsState.set(mDisplayController.getInsetsState(DEFAULT_DISPLAY)); - mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, - mOnInsetsChangedListener); - mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener); - List<TaskAppearedInfo> taskAppearedInfos = registerOrganizer(); - for (int i = 0; i < taskAppearedInfos.size(); i++) { - final TaskAppearedInfo info = taskAppearedInfos.get(i); - onTaskAppeared(info.getTaskInfo(), info.getLeash()); - } - createRootTask(DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN, mCookie); - updateTask(); - } - - @VisibleForTesting - void disable() { - setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled */ false, - /* fromOrientations */ null, /* toOrientations */ null); - mDisplayInsetsController.removeInsetsChangedListener(DEFAULT_DISPLAY, - mOnInsetsChangedListener); - mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener); - updateTask(); - final WindowContainerToken token = mLaunchRootTask.token; - if (token != null) { - deleteRootTask(token); - } - mLaunchRootTask = null; - mLaunchRootLeash = null; - if (mHomeTask != null && mHomeTask.token != null) { - final WindowContainerToken homeToken = mHomeTask.token; - final WindowContainerTransaction wct = getWindowContainerTransaction(); - wct.setBounds(homeToken, null); - mSyncQueue.queue(wct); - } - mHomeTask = null; - unregisterOrganizer(); - } - - private void updateTask() { - updateTask(getWindowContainerTransaction()); - } - - private void updateTask(WindowContainerTransaction wct) { - if (mLaunchRootTask == null || mLaunchRootLeash == null) { - return; - } - final Rect taskBounds = calculateBounds(); - final WindowContainerToken rootToken = mLaunchRootTask.token; - wct.setBounds(rootToken, mEnabled ? taskBounds : null); - wct.setLaunchRoot(rootToken, - mEnabled ? CONTROLLED_WINDOWING_MODES : null, - mEnabled ? CONTROLLED_ACTIVITY_TYPES : null); - wct.reparentTasks( - mEnabled ? null : rootToken /* currentParent */, - mEnabled ? rootToken : null /* newParent */, - CONTROLLED_WINDOWING_MODES, - CONTROLLED_ACTIVITY_TYPES, - true /* onTop */); - wct.reorder(rootToken, mEnabled /* onTop */); - mSyncQueue.queue(wct); - if (mEnabled) { - final SurfaceControl rootLeash = mLaunchRootLeash; - mSyncQueue.runInSync(t -> { - t.setPosition(rootLeash, taskBounds.left, taskBounds.top); - t.setWindowCrop(rootLeash, mDisplayWidth, mDisplayHeight); - }); - } - } - - private Rect calculateBounds() { - final Rect bounds = new Rect(0, 0, mDisplayWidth, mDisplayHeight); - bounds.inset(mInsetsState.calculateInsets( - bounds, WindowInsets.Type.navigationBars(), false /* ignoreVisibility */)); - return bounds; - } - - private void updateBounds() { - if (mLaunchRootTask == null) { - return; - } - final WindowContainerTransaction wct = getWindowContainerTransaction(); - final Rect taskBounds = calculateBounds(); - wct.setBounds(mLaunchRootTask.token, taskBounds); - wct.setBounds(mHomeTask.token, new Rect(0, 0, mDisplayWidth, mDisplayHeight)); - mSyncQueue.queue(wct); - final SurfaceControl finalLeash = mLaunchRootLeash; - mSyncQueue.runInSync(t -> { - t.setPosition(finalLeash, taskBounds.left, taskBounds.top); - t.setWindowCrop(finalLeash, mDisplayWidth, mDisplayHeight); - }); - } - - @VisibleForTesting - WindowContainerTransaction getWindowContainerTransaction() { - return new WindowContainerTransaction(); - } - - @Override - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - pw.println(prefix + TAG); - pw.println(innerPrefix + " mEnabled=" + mEnabled); - pw.println(innerPrefix + " mLaunchRootTask=" + mLaunchRootTask); - pw.println(innerPrefix + " mLaunchRootLeash=" + mLaunchRootLeash); - pw.println(innerPrefix + " mDisplayWidth=" + mDisplayWidth); - pw.println(innerPrefix + " mDisplayHeight=" + mDisplayHeight); - pw.println(innerPrefix + " mInsetsState=" + mInsetsState); - super.dump(pw, innerPrefix); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 07d11cf9f63c..b14c3c10846b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -447,7 +447,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * Callback when launcher finishes swipe-pip-to-home operation. + * Callback when launcher finishes preparation of swipe-pip-to-home operation. * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards. */ public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, @@ -534,8 +534,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } - final Rect displayBounds = mPipBoundsState.getDisplayBounds(); - final Rect destinationBounds = new Rect(displayBounds); + final Rect destinationBounds = new Rect(getExitDestinationBounds()); final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit) ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_LEAVE_PIP; @@ -1146,7 +1145,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void setPipVisibility(boolean visible) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "setPipVisibility: %s, state=%s visible=%s", - mTaskInfo.topActivity, mPipTransitionState, visible); + (mTaskInfo != null ? mTaskInfo.topActivity : null), mPipTransitionState, visible); if (!isInPip()) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 86b0f33ad54c..b8407c465741 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -351,7 +351,7 @@ public class PipTransition extends PipTransitionController { if (taskInfo != null) { startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(), mPipBoundsState.getBounds(), mPipBoundsState.getBounds(), - new Rect(mExitDestinationBounds), Surface.ROTATION_0); + new Rect(mExitDestinationBounds), Surface.ROTATION_0, null /* startT */); } mExitDestinationBounds.setEmpty(); mCurrentPipTaskToken = null; @@ -604,14 +604,8 @@ public class PipTransition extends PipTransitionController { } } - // Set the initial frame as scaling the end to the start. final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds()); destinationBounds.offset(-offset.x, -offset.y); - startTransaction.setWindowCrop(pipLeash, destinationBounds.width(), - destinationBounds.height()); - mSurfaceTransactionHelper.scale(startTransaction, pipLeash, destinationBounds, - currentBounds); - startTransaction.apply(); // Check if it is fixed rotation. final int rotationDelta; @@ -641,7 +635,7 @@ public class PipTransition extends PipTransitionController { rotationDelta = Surface.ROTATION_0; } startExpandAnimation(taskInfo, pipLeash, currentBounds, currentBounds, destinationBounds, - rotationDelta); + rotationDelta, startTransaction); } private void startExpandAndRotationAnimation(@NonNull TransitionInfo info, @@ -697,7 +691,7 @@ public class PipTransition extends PipTransitionController { private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, final Rect baseBounds, final Rect startBounds, final Rect endBounds, - final int rotationDelta) { + final int rotationDelta, @Nullable SurfaceControl.Transaction startTransaction) { final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( taskInfo.pictureInPictureParams, endBounds); final PipAnimationController.PipTransitionAnimator animator = @@ -705,9 +699,14 @@ public class PipTransition extends PipTransitionController { endBounds, sourceHintRect, TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) - .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(mEnterExitAnimationDuration); + if (startTransaction != null) { + animator.setPipTransactionHandler(mTransactionConsumer).applySurfaceControlTransaction( + leash, startTransaction, PipAnimationController.FRACTION_START); + startTransaction.apply(); + } + animator.setPipAnimationCallback(mPipAnimationCallback) .setPipTransactionHandler(mPipOrganizer.getPipTransactionHandler()) - .setDuration(mEnterExitAnimationDuration) .start(); } @@ -954,7 +953,8 @@ public class PipTransition extends PipTransitionController { if (swipePipToHomeOverlay != null) { // Launcher fade in the overlay on top of the fullscreen Task. It is possible we // reparent the PIP activity to a new PIP task (in case there are other activities - // in the original Task), so we should also reparent the overlay to the PIP task. + // in the original Task, in other words multi-activity apps), so we should also reparent + // the overlay to the final PIP task. startTransaction.reparent(swipePipToHomeOverlay, leash) .setLayer(swipePipToHomeOverlay, Integer.MAX_VALUE); mPipOrganizer.mSwipePipToHomeOverlay = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java index 7f62c629c7f2..5d858fa9aa3f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java @@ -410,6 +410,7 @@ public class PipTouchState { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mAllowTouches=" + mAllowTouches); + pw.println(innerPrefix + "mAllowInputEvents=" + mAllowInputEvents); pw.println(innerPrefix + "mActivePointerId=" + mActivePointerId); pw.println(innerPrefix + "mLastTouchDisplayId=" + mLastTouchDisplayId); pw.println(innerPrefix + "mDownTouch=" + mDownTouch); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index a612f5f09e88..843e5af67af9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -313,7 +313,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { private Pair<int[], TaskSnapshot[]> getSnapshotsForPausingTasks() { int[] taskIds = null; TaskSnapshot[] snapshots = null; - if (mPausingTasks.size() > 0) { + if (mPausingTasks != null && mPausingTasks.size() > 0) { taskIds = new int[mPausingTasks.size()]; snapshots = new TaskSnapshot[mPausingTasks.size()]; try { @@ -424,6 +424,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // the change count). This lets us categorize things into above/below/between // while maintaining their relative ordering. final int belowLayers = info.getChanges().size(); + final int middleLayers = info.getChanges().size() * 2; final int aboveLayers = info.getChanges().size() * 3; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -441,14 +442,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { belowLayers - i, info, t, mLeashMap); apps.add(target); if (TransitionUtil.isClosingType(change.getMode())) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " adding pausing leaf taskId=%d", taskInfo.taskId); - // raise closing (pausing) task to "above" layer so it isn't covered - t.setLayer(target.leash, aboveLayers - i); mPausingTasks.add(new TaskState(change, target.leash)); if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " adding pausing leaf home taskId=%d", taskInfo.taskId); // This can only happen if we have a separate recents/home (3p launcher) mPausingSeparateHome = true; + } else { + final int layer = aboveLayers - i; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " adding pausing leaf taskId=%d at layer=%d", + taskInfo.taskId, layer); + // raise closing (pausing) task to "above" layer so it isn't covered + t.setLayer(target.leash, layer); } if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { @@ -456,8 +462,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { - // There's a 3p launcher, so make sure recents goes above that. - t.setLayer(target.leash, aboveLayers - i); + final int layer = middleLayers - i; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " setting recents activity layer=%d", layer); + // There's a 3p launcher, so make sure recents goes above that, but under + // the pausing apps. + t.setLayer(target.leash, layer); } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { // do nothing } else if (TransitionUtil.isOpeningType(change.getMode())) { @@ -468,16 +478,18 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } else if (taskInfo != null && TransitionInfo.isIndependent(change, info)) { // Root tasks if (TransitionUtil.isClosingType(change.getMode())) { + final int layer = aboveLayers - i; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " adding pausing taskId=%d", taskInfo.taskId); + " adding pausing taskId=%d at layer=%d", taskInfo.taskId, layer); // raise closing (pausing) task to "above" layer so it isn't covered - t.setLayer(change.getLeash(), aboveLayers - i); + t.setLayer(change.getLeash(), layer); mPausingTasks.add(new TaskState(change, null /* leash */)); } else if (TransitionUtil.isOpeningType(change.getMode())) { + final int layer = belowLayers - i; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " adding opening taskId=%d", taskInfo.taskId); + " adding opening taskId=%d at layer=%d", taskInfo.taskId, layer); // Put into the "below" layer space. - t.setLayer(change.getLeash(), belowLayers - i); + t.setLayer(change.getLeash(), layer); mOpeningTasks.add(new TaskState(change, null /* leash */)); } } else if (TransitionUtil.isDividerBar(change)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index b5d4a9d7e8e1..acc1c5eb74b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -23,7 +23,6 @@ import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVIT import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.RemoteAnimationTarget.MODE_OPENING; @@ -378,6 +377,27 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return mMainStage.isActive(); } + /** @return whether the transition-request implies entering pip from split. */ + public boolean requestImpliesSplitToPip(TransitionRequestInfo request) { + if (!isSplitActive() || !mMixedHandler.requestHasPipEnter(request)) { + return false; + } + + if (request.getTriggerTask() != null && getSplitPosition( + request.getTriggerTask().taskId) != SPLIT_POSITION_UNDEFINED) { + return true; + } + + // If one of the splitting tasks support auto-pip, wm-core might reparent the task to TDA + // and file a TRANSIT_PIP transition when finishing transitions. + // @see com.android.server.wm.RootWindowContainer#moveActivityToPinnedRootTask + if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { + return true; + } + + return false; + } + /** Checks if `transition` is a pending enter-split transition. */ public boolean isPendingEnter(IBinder transition) { return mSplitTransitions.isPendingEnter(transition); @@ -2357,6 +2377,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out); } } + + // When split in the background, it should be only opening/dismissing transition and + // would keep out not empty. Prevent intercepting all transitions for split screen when + // it is in the background and not identify to handle it. + return (!out.isEmpty() || isSplitScreenVisible()) ? out : null; } else { if (isOpening && getStageOfTask(triggerTask) != null) { // One task is appearing into split, prepare to enter split screen. @@ -2365,8 +2390,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering); } + return out; } - return out; } /** @@ -2428,6 +2453,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setFreezeDividerWindow(false); final StageChangeRecord record = new StageChangeRecord(); + final int transitType = info.getType(); + boolean hasEnteringPip = false; for (int iC = 0; iC < info.getChanges().size(); ++iC) { final TransitionInfo.Change change = info.getChanges().get(iC); if (change.getMode() == TRANSIT_CHANGE @@ -2435,6 +2462,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.update(startTransaction); } + if (mMixedHandler.isEnteringPip(change, transitType)) { + hasEnteringPip = true; + } + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo == null) continue; if (taskInfo.token.equals(mRootTaskInfo.token)) { @@ -2483,6 +2514,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } } + + if (hasEnteringPip) { + mMixedHandler.animatePendingEnterPipFromSplit(transition, info, + startTransaction, finishTransaction, finishCallback); + return true; + } + final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage(); if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0 || dismissStages.size() == 1) { @@ -2498,8 +2536,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, && getStageType(dismissStages.valueAt(0)) == STAGE_TYPE_MAIN) || mMainStage.getChildCount() == 0 ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; // If there is a fullscreen opening change, we should not bring stage to top. - prepareExitSplitScreen(record.mContainShowFullscreenChange - ? STAGE_TYPE_UNDEFINED : dismissTop, wct); + prepareExitSplitScreen( + !record.mContainShowFullscreenChange && isSplitScreenVisible() + ? dismissTop : STAGE_TYPE_UNDEFINED, wct); mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_APP_FINISHED); // This can happen in some pathological cases. For example: @@ -2814,19 +2853,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - mRecentTasks.ifPresent(recentTasks -> { + if (shouldBreakPairedTaskInRecents(dismissReason)) { // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again - if (shouldBreakPairedTaskInRecents(dismissReason)) { - for (TransitionInfo.Change change : info.getChanges()) { - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo != null - && taskInfo.getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW) { - recentTasks.removeSplitPair(taskInfo.taskId); - } + mRecentTasks.ifPresent(recentTasks -> { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + if (taskInfo != null && (getStageOfTask(taskInfo) != null + || getSplitItemPosition(change.getLastParent()) + != SPLIT_POSITION_UNDEFINED)) { + recentTasks.removeSplitPair(taskInfo.taskId); } - } - }); + } + }); + } mSplitRequest = null; // Update local states. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 6013754b97d1..a28ce55e8c94 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -42,10 +42,10 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; -import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.phone.PipTouchHandler; @@ -179,9 +179,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive() - && request.getTriggerTask() != null && mSplitHandler.getSplitItemPosition( - request.getTriggerTask().token) != SPLIT_POSITION_UNDEFINED) { + if (mSplitHandler.requestImpliesSplitToPip(request)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while " + "Split-Screen is active, so treat it as Mixed."); if (request.getRemoteTransition() != null) { @@ -343,6 +341,19 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction, finishTransaction, finishCallback); } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + // Pip auto-entering info might be appended to recent transition like pressing + // home-key in 3-button navigation. This offers split handler the opportunity to + // handle split to pip animation. + if (mPipHandler.isEnteringPip(change, info.getType()) + && mSplitHandler.getSplitItemPosition(change.getLastParent()) + != SPLIT_POSITION_UNDEFINED) { + return animateEnterPipFromSplit(mixed, info, startTransaction, + finishTransaction, finishCallback); + } + } + return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { @@ -459,7 +470,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB); }; - if (isGoingHome) { + if (isGoingHome || mSplitHandler.getSplitItemPosition(pipChange.getLastParent()) + != SPLIT_POSITION_UNDEFINED) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed " + "since entering-PiP caused us to leave split and return home."); // We need to split the transition into 2 parts: the pip part (animated by pip) @@ -528,10 +540,27 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } /** + * This is intended to be called by SplitCoordinator as a helper to mix a split handling + * transition with an entering-pip change. The use-case for this is when an auto-pip change + * gets collected into the transition which has already claimed by + * StageCoordinator.handleRequest. This happens when launching a fullscreen app while having an + * auto-pip activity in the foreground split pair. + */ + // TODO(b/287704263): Remove when split/mixed are reversed. + public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + Transitions.TransitionFinishCallback finishCallback) { + final MixedTransition mixed = new MixedTransition( + MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition); + mActiveTransitions.add(mixed); + return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback); + } + + /** * This is intended to be called by SplitCoordinator as a helper to mix an already-pending * split transition with a display-change. The use-case for this is when a display * change/rotation gets collected into a split-screen enter/exit transition which has already - * been claimed by StageCoordinator.handleRequest . This happens during launcher tests. + * been claimed by StageCoordinator.handleRequest. This happens during launcher tests. */ public boolean animatePendingSplitWithDisplayChange(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @@ -620,18 +649,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, finishCallback.onTransitionFinished(wct, wctCB); } }; - if (!mKeyguardHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) { - return false; - } mixed.mInFlightSubAnimations++; // Sync pip state. if (mPipHandler != null) { - // We don't know when to apply `startTransaction` so use a separate transaction here. - // This should be fine because these surface properties are independent. - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mPipHandler.syncPipSurfaceState(info, t, finishTransaction); - t.apply(); + mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); + } + if (!mKeyguardHandler.startAnimation( + mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) { + mixed.mInFlightSubAnimations--; + return false; } return true; } @@ -672,19 +698,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, finishCallback.onTransitionFinished(wct, wctCB); }; mixed.mInFlightSubAnimations = 1; - if (!mUnfoldHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) { - return false; - } // Sync pip state. if (mPipHandler != null) { - // We don't know when to apply `startTransaction` so use a separate transaction here. - // This should be fine because these surface properties are independent. - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mPipHandler.syncPipSurfaceState(info, t, finishTransaction); - t.apply(); + mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction); } - return true; + return mUnfoldHandler.startAnimation( + mixed.mTransition, info, startTransaction, finishTransaction, finishCB); } /** Use to when split use intent to enter, check if this enter transition should be mixed or @@ -698,6 +717,18 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return false; } + /** @return whether the transition-request represents a pip-entry. */ + public boolean requestHasPipEnter(TransitionRequestInfo request) { + return mPipHandler.requestHasPipEnter(request); + } + + /** Whether a particular change is a window that is entering pip. */ + // TODO(b/287704263): Remove when split/mixed are reversed. + public boolean isEnteringPip(TransitionInfo.Change change, + @WindowManager.TransitionType int transitType) { + return mPipHandler.isEnteringPip(change, transitType); + } + @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, 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 b565cb77324f..f148412205bf 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 @@ -20,6 +20,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; +import android.app.ActivityManager; import android.os.IBinder; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -178,6 +179,18 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull TransitionFinishCallback finishCallback) { if (info.getType() == TRANSIT_CHANGE) { + // TODO (b/286928742) unfold transition handler should be part of mixed handler to + // handle merges better. + for (int i = 0; i < info.getChanges().size(); ++i) { + final TransitionInfo.Change change = info.getChanges().get(i); + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + if (taskInfo != null + && taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) { + // Tasks that are always on top (e.g. bubbles), will handle their own transition + // as they are on top of everything else. So skip merging transitions here. + return; + } + } // Apply changes happening during the unfold animation immediately t.apply(); finishCallback.onTransitionFinished(null, null); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index c93a11d85f7e..331835cca142 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -814,6 +815,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return false; } return DesktopModeStatus.isProto2Enabled() + && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && mDisplayController.getDisplayContext(taskInfo.displayId) .getResources().getConfiguration().smallestScreenWidthDp >= 600; 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 e3cb8af31049..bc89385a0d13 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 @@ -23,7 +23,6 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.content.res.TypedArray; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -39,6 +38,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.window.WindowContainerTransaction; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; @@ -182,11 +182,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; - final TypedArray ta = mContext.obtainStyledAttributes( - new int[]{android.R.attr.dialogCornerRadius}); - mRelayoutParams.mCornerRadius = ta.getDimensionPixelSize(0, 0); - ta.recycle(); - + mRelayoutParams.mCornerRadius = + (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext); relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 68b63e62f09b..e5fc66afbdfc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -56,7 +56,6 @@ import com.android.internal.view.BaseIWindow; */ class DragResizeInputListener implements AutoCloseable { private static final String TAG = "DragResizeInputListener"; - private static final float TOP_CORNER_PADDING = 1.5f; private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession(); private final Handler mHandler; private final Choreographer mChoreographer; @@ -130,12 +129,7 @@ class DragResizeInputListener implements AutoCloseable { } /** - * Updates geometry of this drag resize handler. Needs to be called every time there is a size - * change to notify the input event receiver it's ready to take the next input event. Otherwise - * it'll keep batching move events and the drag resize process is stalled. - * - * This is also used to update the touch regions of this handler every event dispatched here is - * a potential resize request. + * Updates the geometry (the touch region) of this drag resize handler. * * @param taskWidth The width of the task. * @param taskHeight The height of the task. @@ -440,10 +434,7 @@ class DragResizeInputListener implements AutoCloseable { } double distanceFromCenter = Math.hypot(x - centerX, y - centerY); - // TODO(b/286461778): Remove this when input in top corner gap no longer goes to header - float cornerPadding = (ctrlType & CTRL_TYPE_TOP) != 0 ? TOP_CORNER_PADDING : 1; - - if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness * cornerPadding + if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness && distanceFromCenter >= mTaskCornerRadius) { return ctrlType; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 4407f2ec3167..ddc7fef0599f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -237,7 +237,12 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId); final int captionWidth = taskBounds.width(); + + // We use mDecorationContainerSurface to define input window for task resizing; by layering + // it in front of mCaptionContainerSurface, we can allow it to handle input prior to + // caption view itself, treating corner inputs as resize events rather than repositioning. startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight) + .setLayer(mCaptionContainerSurface, -1) .show(mCaptionContainerSurface); if (ViewRootImpl.CAPTION_ON_SHELL) { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt index e06e074ee98a..0f3e0f5ef043 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker import android.app.Instrumentation import android.tools.device.flicker.junit.FlickerBuilderProvider import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation abstract class BaseBenchmarkTest @JvmOverloads constructor( - protected open val flicker: FlickerTest, + protected open val flicker: LegacyFlickerTest, protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), protected val tapl: LauncherInstrumentation = LauncherInstrumentation() ) { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt index c98c5a0ad1a6..d2fe9fec460c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt @@ -18,7 +18,7 @@ package com.android.wm.shell.flicker import android.app.Instrumentation import android.tools.common.traces.component.ComponentNameMatcher -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation @@ -30,7 +30,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation abstract class BaseTest @JvmOverloads constructor( - override val flicker: FlickerTest, + override val flicker: LegacyFlickerTest, instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), tapl: LauncherInstrumentation = LauncherInstrumentation() ) : BaseBenchmarkTest(flicker, instrumentation, tapl), ICommonAssertions diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 798cc95c020f..9cc03a56e9a2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -23,18 +23,18 @@ import android.tools.common.datatypes.Region import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject import android.tools.common.flicker.subject.layers.LayersTraceSubject import android.tools.common.traces.component.IComponentMatcher -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.helpers.WindowUtils -fun FlickerTest.appPairsDividerIsVisibleAtEnd() { +fun LegacyFlickerTest.appPairsDividerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) } } -fun FlickerTest.appPairsDividerIsInvisibleAtEnd() { +fun LegacyFlickerTest.appPairsDividerIsInvisibleAtEnd() { assertLayersEnd { this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT) } } -fun FlickerTest.appPairsDividerBecomesVisible() { +fun LegacyFlickerTest.appPairsDividerBecomesVisible() { assertLayers { this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT) .then() @@ -42,7 +42,7 @@ fun FlickerTest.appPairsDividerBecomesVisible() { } } -fun FlickerTest.splitScreenEntered( +fun LegacyFlickerTest.splitScreenEntered( component1: IComponentMatcher, component2: IComponentMatcher, fromOtherApp: Boolean, @@ -69,7 +69,7 @@ fun FlickerTest.splitScreenEntered( splitScreenDividerIsVisibleAtEnd() } -fun FlickerTest.splitScreenDismissed( +fun LegacyFlickerTest.splitScreenDismissed( component1: IComponentMatcher, component2: IComponentMatcher, toHome: Boolean @@ -87,27 +87,27 @@ fun FlickerTest.splitScreenDismissed( splitScreenDividerIsInvisibleAtEnd() } -fun FlickerTest.splitScreenDividerIsVisibleAtStart() { +fun LegacyFlickerTest.splitScreenDividerIsVisibleAtStart() { assertLayersStart { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } -fun FlickerTest.splitScreenDividerIsVisibleAtEnd() { +fun LegacyFlickerTest.splitScreenDividerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } -fun FlickerTest.splitScreenDividerIsInvisibleAtStart() { +fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtStart() { assertLayersStart { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } -fun FlickerTest.splitScreenDividerIsInvisibleAtEnd() { +fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtEnd() { assertLayersEnd { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } -fun FlickerTest.splitScreenDividerBecomesVisible() { +fun LegacyFlickerTest.splitScreenDividerBecomesVisible() { layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } -fun FlickerTest.splitScreenDividerBecomesInvisible() { +fun LegacyFlickerTest.splitScreenDividerBecomesInvisible() { assertLayers { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) .then() @@ -115,23 +115,23 @@ fun FlickerTest.splitScreenDividerBecomesInvisible() { } } -fun FlickerTest.layerBecomesVisible(component: IComponentMatcher) { +fun LegacyFlickerTest.layerBecomesVisible(component: IComponentMatcher) { assertLayers { this.isInvisible(component).then().isVisible(component) } } -fun FlickerTest.layerBecomesInvisible(component: IComponentMatcher) { +fun LegacyFlickerTest.layerBecomesInvisible(component: IComponentMatcher) { assertLayers { this.isVisible(component).then().isInvisible(component) } } -fun FlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) { +fun LegacyFlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) { assertLayersEnd { this.isVisible(component) } } -fun FlickerTest.layerKeepVisible(component: IComponentMatcher) { +fun LegacyFlickerTest.layerKeepVisible(component: IComponentMatcher) { assertLayers { this.isVisible(component) } } -fun FlickerTest.splitAppLayerBoundsBecomesVisible( +fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisible( component: IComponentMatcher, landscapePosLeft: Boolean, portraitPosTop: Boolean @@ -150,7 +150,7 @@ fun FlickerTest.splitAppLayerBoundsBecomesVisible( } } -fun FlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) { +fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) { assertLayers { this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true) .then() @@ -161,7 +161,7 @@ fun FlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMat } } -fun FlickerTest.splitAppLayerBoundsBecomesInvisible( +fun LegacyFlickerTest.splitAppLayerBoundsBecomesInvisible( component: IComponentMatcher, landscapePosLeft: Boolean, portraitPosTop: Boolean @@ -180,7 +180,7 @@ fun FlickerTest.splitAppLayerBoundsBecomesInvisible( } } -fun FlickerTest.splitAppLayerBoundsIsVisibleAtEnd( +fun LegacyFlickerTest.splitAppLayerBoundsIsVisibleAtEnd( component: IComponentMatcher, landscapePosLeft: Boolean, portraitPosTop: Boolean @@ -195,7 +195,7 @@ fun FlickerTest.splitAppLayerBoundsIsVisibleAtEnd( } } -fun FlickerTest.splitAppLayerBoundsKeepVisible( +fun LegacyFlickerTest.splitAppLayerBoundsKeepVisible( component: IComponentMatcher, landscapePosLeft: Boolean, portraitPosTop: Boolean @@ -210,7 +210,7 @@ fun FlickerTest.splitAppLayerBoundsKeepVisible( } } -fun FlickerTest.splitAppLayerBoundsChanges( +fun LegacyFlickerTest.splitAppLayerBoundsChanges( component: IComponentMatcher, landscapePosLeft: Boolean, portraitPosTop: Boolean @@ -304,7 +304,7 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider( } } -fun FlickerTest.appWindowBecomesVisible(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowBecomesVisible(component: IComponentMatcher) { assertWm { this.isAppWindowInvisible(component) .then() @@ -316,39 +316,39 @@ fun FlickerTest.appWindowBecomesVisible(component: IComponentMatcher) { } } -fun FlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) { assertWm { this.isAppWindowVisible(component).then().isAppWindowInvisible(component) } } -fun FlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) { assertWmStart { this.isAppWindowVisible(component) } } -fun FlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) { assertWmEnd { this.isAppWindowVisible(component) } } -fun FlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) { assertWmStart { this.isAppWindowInvisible(component) } } -fun FlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) { assertWmEnd { this.isAppWindowInvisible(component) } } -fun FlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) { assertWmStart { this.notContains(component) } } -fun FlickerTest.appWindowKeepVisible(component: IComponentMatcher) { +fun LegacyFlickerTest.appWindowKeepVisible(component: IComponentMatcher) { assertWm { this.isAppWindowVisible(component) } } -fun FlickerTest.dockedStackDividerIsVisibleAtEnd() { +fun LegacyFlickerTest.dockedStackDividerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) } } -fun FlickerTest.dockedStackDividerBecomesVisible() { +fun LegacyFlickerTest.dockedStackDividerBecomesVisible() { assertLayers { this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT) .then() @@ -356,7 +356,7 @@ fun FlickerTest.dockedStackDividerBecomesVisible() { } } -fun FlickerTest.dockedStackDividerBecomesInvisible() { +fun LegacyFlickerTest.dockedStackDividerBecomesInvisible() { assertLayers { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) .then() @@ -364,11 +364,11 @@ fun FlickerTest.dockedStackDividerBecomesInvisible() { } } -fun FlickerTest.dockedStackDividerNotExistsAtEnd() { +fun LegacyFlickerTest.dockedStackDividerNotExistsAtEnd() { assertLayersEnd { this.notContains(DOCKED_STACK_DIVIDER_COMPONENT) } } -fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd( +fun LegacyFlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd( rotation: Rotation, primaryComponent: IComponentMatcher ) { @@ -380,7 +380,7 @@ fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd( } } -fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd( +fun LegacyFlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd( rotation: Rotation, primaryComponent: IComponentMatcher ) { @@ -392,7 +392,7 @@ fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd( } } -fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd( +fun LegacyFlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd( rotation: Rotation, secondaryComponent: IComponentMatcher ) { @@ -404,7 +404,7 @@ fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd( } } -fun FlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd( +fun LegacyFlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd( rotation: Rotation, secondaryComponent: IComponentMatcher ) { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt index 02d9a056afbf..7b3290125bae 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt @@ -18,7 +18,7 @@ package com.android.wm.shell.flicker import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.entireScreenCovered import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd @@ -32,7 +32,7 @@ import org.junit.Assume import org.junit.Test interface ICommonAssertions { - val flicker: FlickerTest + val flicker: LegacyFlickerTest /** Checks that all parts of the screen are covered during the transition */ @Presubmit @Test fun entireScreenCovered() = flicker.entireScreenCovered() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt index 3000008628fe..0f9579d58929 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt @@ -20,20 +20,20 @@ import android.content.Context import android.system.helpers.CommandsHelper import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import com.android.server.wm.flicker.helpers.setRotation +import android.tools.device.flicker.legacy.FlickerTestData +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.helpers.LetterboxAppHelper -import android.tools.device.flicker.legacy.IFlickerTestData +import com.android.server.wm.flicker.helpers.setRotation import com.android.wm.shell.flicker.BaseTest -import com.android.wm.shell.flicker.appWindowIsVisibleAtStart import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd +import com.android.wm.shell.flicker.appWindowIsVisibleAtStart import com.android.wm.shell.flicker.appWindowKeepVisible import com.android.wm.shell.flicker.layerKeepVisible import org.junit.After import org.junit.Assume import org.junit.Before -abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { +abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected val context: Context = instrumentation.context protected val letterboxApp = LetterboxAppHelper(instrumentation) lateinit var cmdHelper: CommandsHelper @@ -47,9 +47,7 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { letterboxApp.launchViaIntent(wmHelper) setEndRotation() } - teardown { - letterboxApp.exit(wmHelper) - } + teardown { letterboxApp.exit(wmHelper) } } @Before @@ -100,9 +98,9 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { return res != null && res.contains("true") } - fun IFlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation) + fun FlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation) - fun IFlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation) + fun FlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation) /** Checks that app entering letterboxed state have rounded corners */ fun assertLetterboxAppAtStartHasRoundedCorners() { @@ -131,13 +129,13 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { } fun assertAppLetterboxedAtEnd() = - flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) } + flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) } fun assertAppLetterboxedAtStart() = - flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) } + flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) } fun assertAppStaysLetterboxed() = - flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) } + flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) } fun assertLetterboxAppLayerKeepVisible() = flicker.layerKeepVisible(letterboxApp) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt index 3d83455932f5..a7bd2584ba23 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt @@ -17,11 +17,12 @@ package com.android.wm.shell.flicker.appcompat import android.platform.test.annotations.Postsubmit +import android.tools.common.flicker.assertions.FlickerTest import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.Test import org.junit.runner.RunWith @@ -46,7 +47,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) { +class OpenAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -71,9 +72,7 @@ class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) @Test fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners() - @Postsubmit - @Test - fun appIsLetterboxedAtEnd() = assertAppLetterboxedAtEnd() + @Postsubmit @Test fun appIsLetterboxedAtEnd() = assertAppLetterboxedAtEnd() /** * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't @@ -96,13 +95,13 @@ class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) /** * Creates the test configurations. * - * See [FlickerTestFactory.rotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() + return LegacyFlickerTestFactory.rotationTests() } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt index c3355ede525e..e875aae431a1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt @@ -18,29 +18,29 @@ package com.android.wm.shell.flicker.appcompat import android.platform.test.annotations.Postsubmit import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils - import androidx.test.filters.RequiresDevice import org.junit.Test - import org.junit.runner.RunWith import org.junit.runners.Parameterized /** * Test launching a fixed portrait letterboxed app in landscape and repositioning to the right. * - * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` - * Actions: + * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` Actions: + * * ``` * Launch a fixed portrait app in landscape to letterbox app * Double tap to the right to reposition app and wait for app to move * ``` * - * Notes: + * Notes: + * * ``` * Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [BaseTest] @@ -49,7 +49,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class RepositionFixedPortraitAppTest(flicker: FlickerTest) : BaseAppCompat(flicker) { +class RepositionFixedPortraitAppTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation).bounds /** {@inheritDoc} */ @@ -73,31 +73,25 @@ class RepositionFixedPortraitAppTest(flicker: FlickerTest) : BaseAppCompat(flick @Test fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners() - @Postsubmit - @Test - fun letterboxAppLayerKeepVisible() = assertLetterboxAppLayerKeepVisible() + @Postsubmit @Test fun letterboxAppLayerKeepVisible() = assertLetterboxAppLayerKeepVisible() - @Postsubmit - @Test - fun appStaysLetterboxed() = assertAppStaysLetterboxed() + @Postsubmit @Test fun appStaysLetterboxed() = assertAppStaysLetterboxed() - @Postsubmit - @Test - fun appKeepVisible() = assertLetterboxAppKeepVisible() + @Postsubmit @Test fun appKeepVisible() = assertLetterboxAppKeepVisible() companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + return LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_90) ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt index c2057d23a2c4..a18a144b4bf1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt @@ -17,10 +17,11 @@ package com.android.wm.shell.flicker.appcompat import android.platform.test.annotations.Postsubmit +import android.tools.common.flicker.assertions.FlickerTest import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import org.junit.Test @@ -47,7 +48,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) { +class RestartAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -70,13 +71,9 @@ class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flick } } - @Postsubmit - @Test - fun appLayerKeepVisible() = assertLetterboxAppLayerKeepVisible() + @Postsubmit @Test fun appLayerKeepVisible() = assertLetterboxAppLayerKeepVisible() - @Postsubmit - @Test - fun appIsLetterboxedAtStart() = assertAppLetterboxedAtStart() + @Postsubmit @Test fun appIsLetterboxedAtStart() = assertAppLetterboxedAtStart() @Postsubmit @Test @@ -94,13 +91,13 @@ class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flick /** * Creates the test configurations. * - * See [FlickerTestFactory.rotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() + return LegacyFlickerTestFactory.rotationTests() } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt index bab81d79c804..5c7d1d8df2e8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt @@ -23,9 +23,9 @@ import android.content.pm.PackageManager import android.os.ServiceManager import android.tools.common.Rotation import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory -import android.tools.device.flicker.legacy.IFlickerTestData +import android.tools.device.flicker.legacy.FlickerTestData +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.SYSTEMUI_PACKAGE import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 @@ -35,7 +35,7 @@ import com.android.wm.shell.flicker.BaseTest import org.junit.runners.Parameterized /** Base configurations for Bubble flicker tests */ -abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) { +abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected val context: Context = instrumentation.context protected val testApp = LaunchBubbleHelper(instrumentation) @@ -72,28 +72,31 @@ abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) { uid, NotificationManager.BUBBLE_PREFERENCE_NONE ) - testApp.exit() + device.wait( + Until.gone(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), + FIND_OBJECT_TIMEOUT + ) + testApp.exit(wmHelper) } extraSpec(this) } } - protected fun IFlickerTestData.waitAndGetAddBubbleBtn(): UiObject2? = + protected fun FlickerTestData.waitAndGetAddBubbleBtn(): UiObject2? = device.wait(Until.findObject(By.text("Add Bubble")), FIND_OBJECT_TIMEOUT) - protected fun IFlickerTestData.waitAndGetCancelAllBtn(): UiObject2? = + protected fun FlickerTestData.waitAndGetCancelAllBtn(): UiObject2? = device.wait(Until.findObject(By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT) companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } - const val FIND_OBJECT_TIMEOUT = 2000L + const val FIND_OBJECT_TIMEOUT = 4000L const val SYSTEM_UI_PACKAGE = SYSTEMUI_PACKAGE const val BUBBLE_RES_NAME = "bubble_view" } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt index 2474ecf74cf9..bc565bc5fd42 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt @@ -21,7 +21,7 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 @@ -44,7 +44,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FlakyTest(bugId = 217777115) -open class ChangeActiveActivityFromBubbleTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) { +open class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) : + BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = buildTransition { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt index bdfdad59c600..abc6b9f9a746 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.bubble import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class ChangeActiveActivityFromBubbleTestCfArm(flicker: FlickerTest) : +open class ChangeActiveActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) : ChangeActiveActivityFromBubbleTest(flicker) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt index 8474ce0e64e5..3f28ae848d1f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt @@ -19,9 +19,10 @@ package com.android.wm.shell.flicker.bubble import android.content.Context import android.graphics.Point import android.platform.test.annotations.Presubmit +import android.tools.common.flicker.subject.layers.LayersTraceSubject import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.util.DisplayMetrics import android.view.WindowManager import androidx.test.filters.RequiresDevice @@ -44,7 +45,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class DragToDismissBubbleScreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) { +open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager private val displaySize = DisplayMetrics() @@ -73,4 +74,14 @@ open class DragToDismissBubbleScreenTest(flicker: FlickerTest) : BaseBubbleScree open fun testAppIsAlwaysVisible() { flicker.assertLayers { this.isVisible(testApp) } } + + @Presubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + flicker.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(testApp) + ) + } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt index 62fa7b4516c7..ee55eca31072 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.bubble import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class DragToDismissBubbleScreenTestCfArm(flicker: FlickerTest) : +class DragToDismissBubbleScreenTestCfArm(flicker: LegacyFlickerTest) : DragToDismissBubbleScreenTest(flicker) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt index 889e1771593d..26aca1830889 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt @@ -21,7 +21,7 @@ import android.platform.test.annotations.Postsubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.view.WindowInsets import android.view.WindowManager import androidx.test.filters.RequiresDevice @@ -48,7 +48,8 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class OpenActivityFromBubbleOnLocksreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) { +class OpenActivityFromBubbleOnLocksreenTest(flicker: LegacyFlickerTest) : + BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -80,7 +81,7 @@ class OpenActivityFromBubbleOnLocksreenTest(flicker: FlickerTest) : BaseBubbleSc instrumentation.uiAutomation.syncInputTransactions() val showBubble = device.wait( - Until.findObject(By.res("com.android.systemui", "bubble_view")), + Until.findObject(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT ) showBubble?.click() ?: error("Bubble notify not found") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt index 07ba41333071..508539411aa0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.bubble import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until @@ -42,7 +42,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class OpenActivityFromBubbleTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) { +open class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt index 6c61710d6284..6a46d23ad2a1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt @@ -17,10 +17,11 @@ package com.android.wm.shell.flicker.bubble import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class OpenActivityFromBubbleTestCfArm(flicker: FlickerTest) : OpenActivityFromBubbleTest(flicker) +class OpenActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) : + OpenActivityFromBubbleTest(flicker) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt index 29f76d01af83..a926bb7d85c3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.bubble import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until @@ -41,7 +41,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class SendBubbleNotificationTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) { +open class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -55,6 +55,7 @@ open class SendBubbleNotificationTest(flicker: FlickerTest) : BaseBubbleScreen(f FIND_OBJECT_TIMEOUT ) ?: error("No bubbles found") + device.waitForIdle() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt index e323ebf3b5c8..a401cb494822 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.bubble import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class SendBubbleNotificationTestCfArm(flicker: FlickerTest) : +open class SendBubbleNotificationTestCfArm(flicker: LegacyFlickerTest) : SendBubbleNotificationTest(flicker) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt index f7ce87088040..36bbafb4c05e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt @@ -24,8 +24,8 @@ import android.tools.common.Rotation import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.filters.RequiresDevice @@ -69,17 +69,17 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) : - AutoEnterPipOnGoToHomeTest(flicker) { +class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : + AutoEnterPipOnGoToHomeTest(flicker) { private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) /** Second app used to enter split screen mode */ protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation) fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper = - SimpleAppHelper( - instrumentation, - ActivityOptions.SplitScreen.Primary.LABEL, - ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent() - ) + SimpleAppHelper( + instrumentation, + ActivityOptions.SplitScreen.Primary.LABEL, + ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent() + ) /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit @@ -91,11 +91,11 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) : enterSplitScreen() // wait until split screen is established wmHelper - .StateSyncBuilder() - .withWindowSurfaceAppeared(pipApp) - .withWindowSurfaceAppeared(secondAppForSplitScreen) - .withSplitDividerVisible() - .waitForAndVerify() + .StateSyncBuilder() + .withWindowSurfaceAppeared(pipApp) + .withWindowSurfaceAppeared(secondAppForSplitScreen) + .withSplitDividerVisible() + .waitForAndVerify() pipApp.enableAutoEnterForPipActivity() } teardown { @@ -120,8 +120,8 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) : // contains more than 3 task views. We need to use uiautomator directly to find the // second task to split. tapl.workspace.switchToOverview().overviewActions.clickSplit() - val snapshots = tapl.device.wait(Until.findObjects(overviewSnapshotSelector), - TIMEOUT_MS) + val snapshots = + tapl.device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS) if (snapshots == null || snapshots.size < 1) { error("Fail to find a overview snapshot to split.") } @@ -137,12 +137,12 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) : snapshots[0].click() } else { tapl.workspace - .switchToOverview() - .currentTask - .tapMenu() - .tapSplitMenuItem() - .currentTask - .open() + .switchToOverview() + .currentTask + .tapMenu() + .tapSplitMenuItem() + .currentTask + .open() } SystemClock.sleep(TIMEOUT_MS) } @@ -190,11 +190,10 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( - // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index b95732e43357..2f7a25ea586d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import org.junit.Assume import org.junit.FixMethodOrder @@ -53,10 +53,9 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) { - override val thisTransition: FlickerBuilder.() -> Unit = { - transitions { tapl.goHome() } - } +open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : + EnterPipViaAppUiButtonTest(flicker) { + override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } override val defaultEnterPip: FlickerBuilder.() -> Unit = { setup { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt index afcc1729ed16..68bc9a28967e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt @@ -20,7 +20,7 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test @@ -53,7 +53,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipBySwipingDownTest(flicker: FlickerTest) : ClosePipTransition(flicker) { +open class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { val pipRegion = wmHelper.getWindowRegion(pipApp).bounds diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt index 02f60100d069..7a668897fbbe 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,20 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ClosePipBySwipingDownTestCfArm(flicker: FlickerTest) : ClosePipBySwipingDownTest(flicker) { +class ClosePipBySwipingDownTestCfArm(flicker: LegacyFlickerTest) : + ClosePipBySwipingDownTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt index e52b71e602f9..a17144b7cef3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt @@ -20,14 +20,14 @@ import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import com.android.server.wm.flicker.helpers.setRotation import org.junit.Test import org.junit.runners.Parameterized /** Base class for exiting pip (closing pip window) without returning to the app */ -abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) { +abstract class ClosePipTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { setup { this.setRotation(flicker.scenario.startRotation) } teardown { this.setRotation(Rotation.ROTATION_0) } @@ -74,15 +74,14 @@ abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt index 86fe583c94e6..dc48696f3197 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test @@ -53,7 +53,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipWithDismissButtonTest(flicker: FlickerTest) : ClosePipTransition(flicker) { +open class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.closePipWindow(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt index 05262feceba5..718b14babc4f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,21 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipWithDismissButtonTestCfArm(flicker: FlickerTest) : +open class ClosePipWithDismissButtonTestCfArm(flicker: LegacyFlickerTest) : ClosePipWithDismissButtonTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt index 01d67cc35a14..5e392628aa6a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import org.junit.Assume import org.junit.FixMethodOrder @@ -44,10 +44,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipOnUserLeaveHintTest(flicker: FlickerTest) : EnterPipTransition(flicker) { - override val thisTransition: FlickerBuilder.() -> Unit = { - transitions { tapl.goHome() } - } +open class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { + override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } override val defaultEnterPip: FlickerBuilder.() -> Unit = { setup { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt index 90f99c0c4cae..2b3e76a964c4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -27,4 +27,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipOnUserLeaveHintTestCfArm(flicker: FlickerTest) : EnterPipOnUserLeaveHintTest(flicker) +class EnterPipOnUserLeaveHintTestCfArm(flicker: LegacyFlickerTest) : + EnterPipOnUserLeaveHintTest(flicker) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index 5480144ba1ce..ec35837bc8dd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt @@ -21,11 +21,12 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.entireScreenCovered @@ -68,15 +69,13 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flicker) { +open class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val testApp = FixedOrientationAppHelper(instrumentation) private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) override val thisTransition: FlickerBuilder.() -> Unit = { - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } transitions { // Enter PiP, and assert that the PiP is within bounds now that the device is back // in portrait @@ -95,14 +94,13 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic setup { // Launch a portrait only app on the fullscreen stack testApp.launchViaIntent( - wmHelper, - stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()) + wmHelper, + stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()) ) // Launch the PiP activity fixed as landscape, but don't enter PiP pipApp.launchViaIntent( - wmHelper, - stringExtras = - mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) + wmHelper, + stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) ) } } @@ -207,13 +205,13 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + return LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt index 58416660826f..92642197e9be 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt @@ -17,9 +17,10 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -29,19 +30,19 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipToOtherOrientationCfArm(flicker: FlickerTest) : +open class EnterPipToOtherOrientationCfArm(flicker: LegacyFlickerTest) : EnterPipToOtherOrientation(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + return LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt index cdbdb85a9195..6d20740e239c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt @@ -20,16 +20,14 @@ import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.Test import org.junit.runners.Parameterized -abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) { +abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val defaultEnterPip: FlickerBuilder.() -> Unit = { - setup { - pipApp.launchViaIntent(wmHelper) - } + setup { pipApp.launchViaIntent(wmHelper) } } /** Checks [pipApp] window remains visible throughout the animation */ @@ -126,15 +124,14 @@ abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt index 95725b64a48a..76c811cbbeea 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt @@ -18,7 +18,7 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -50,7 +50,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipViaAppUiButtonTest(flicker: FlickerTest) : EnterPipTransition(flicker) { +open class EnterPipViaAppUiButtonTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.clickEnterPipButton(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt index 4390f0bb70b2..78e80497747c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,20 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipViaAppUiButtonTestCfArm(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) { +class EnterPipViaAppUiButtonTestCfArm(flicker: LegacyFlickerTest) : + EnterPipViaAppUiButtonTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt index 5ac9829b6c8f..dfffba831dc3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import com.android.server.wm.flicker.helpers.SimpleAppHelper import org.junit.Test import org.junit.runners.Parameterized /** Base class for pip expand tests */ -abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flicker) { +abstract class ExitPipToAppTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) { protected val testApp = SimpleAppHelper(instrumentation) /** @@ -130,15 +130,14 @@ abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flic /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt index 0b3d16a8087d..b80b7483ba4d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt @@ -18,7 +18,7 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -52,7 +52,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExitPipToAppViaExpandButtonTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) { +open class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) : + ExitPipToAppTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { setup { // launch an app behind the pip one diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt index eccb85d98798..e25c0d6eddc0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,21 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExitPipToAppViaExpandButtonTestCfArm(flicker: FlickerTest) : +class ExitPipToAppViaExpandButtonTestCfArm(flicker: LegacyFlickerTest) : ExitPipToAppViaExpandButtonTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt index bb2d40becdc9..f003ed8a77e0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt @@ -18,7 +18,7 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -51,7 +51,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExitPipToAppViaIntentTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) { +open class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { setup { // launch an app behind the pip one diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt index 6ab6a1f0bb73..be19f3cd1970 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,20 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExitPipToAppViaIntentTestCfArm(flicker: FlickerTest) : ExitPipToAppViaIntentTest(flicker) { +class ExitPipToAppViaIntentTestCfArm(flicker: LegacyFlickerTest) : + ExitPipToAppViaIntentTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt index fd16b6ea6ada..a1d3a117482e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt @@ -21,8 +21,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test @@ -55,7 +55,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flicker) { +open class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.doubleClickPipWindow(wmHelper) } } @@ -142,15 +142,14 @@ open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flic /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt index c09623490041..3095cac94598 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,21 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExpandPipOnDoubleClickTestTestCfArm(flicker: FlickerTest) : +class ExpandPipOnDoubleClickTestTestCfArm(flicker: LegacyFlickerTest) : ExpandPipOnDoubleClickTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt index 253aa4cae5c7..8c8d280aea9a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test @@ -34,7 +34,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicker) { +open class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) } } @@ -55,15 +55,14 @@ open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicke /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt index e064bf2ee921..1a1ce6823f3b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,20 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExpandPipOnPinchOpenTestCfArm(flicker: FlickerTest) : ExpandPipOnPinchOpenTest(flicker) { +class ExpandPipOnPinchOpenTestCfArm(flicker: LegacyFlickerTest) : + ExpandPipOnPinchOpenTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt index 094060f86691..4f88184e3c20 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.Direction import org.junit.FixMethodOrder @@ -55,7 +55,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class MovePipDownOnShelfHeightChange(flicker: FlickerTest) : MovePipShelfHeightTransition(flicker) { +class MovePipDownOnShelfHeightChange(flicker: LegacyFlickerTest) : + MovePipShelfHeightTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { teardown { testApp.exit(wmHelper) } transitions { testApp.launchViaIntent(wmHelper) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt index ff51c27bf116..dffc822e7aec 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt @@ -18,11 +18,12 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ImeAppHelper @@ -38,7 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransition(flicker) { +open class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val imeApp = ImeAppHelper(instrumentation) override val thisTransition: FlickerBuilder.() -> Unit = { @@ -80,7 +81,7 @@ open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransitio @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + return LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt index d3d77d20662e..63292a4f2ca3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt @@ -17,9 +17,10 @@ package com.android.wm.shell.flicker.pip import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,7 +29,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class MovePipOnImeVisibilityChangeTestCfArm(flicker: FlickerTest) : +class MovePipOnImeVisibilityChangeTestCfArm(flicker: LegacyFlickerTest) : MovePipOnImeVisibilityChangeTest(flicker) { companion object { private const val TAG_IME_VISIBLE = "imeIsVisible" @@ -36,7 +37,7 @@ class MovePipOnImeVisibilityChangeTestCfArm(flicker: FlickerTest) : @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + return LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt index 109354ab5c79..9a2fa095c8cb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt @@ -19,15 +19,15 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.common.flicker.subject.region.RegionSubject -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper import com.android.wm.shell.flicker.Direction import org.junit.Test import org.junit.runners.Parameterized /** Base class for pip tests with Launcher shelf height change */ -abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransition(flicker) { +abstract class MovePipShelfHeightTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) { protected val testApp = FixedOrientationAppHelper(instrumentation) /** Checks [pipApp] window remains visible throughout the animation */ @@ -111,15 +111,14 @@ abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransitio /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt index 27b061b67a85..afb4af6c5b21 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt @@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.Direction import org.junit.FixMethodOrder @@ -55,14 +55,13 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class MovePipUpOnShelfHeightChangeTest(flicker: FlickerTest) : +open class MovePipUpOnShelfHeightChangeTest(flicker: LegacyFlickerTest) : MovePipShelfHeightTransition(flicker) { - override val thisTransition: FlickerBuilder.() -> Unit = - { - setup { testApp.launchViaIntent(wmHelper) } - transitions { tapl.pressHome() } - teardown { testApp.exit(wmHelper) } - } + override val thisTransition: FlickerBuilder.() -> Unit = { + setup { testApp.launchViaIntent(wmHelper) } + transitions { tapl.pressHome() } + teardown { testApp.exit(wmHelper) } + } /** Checks that the visible region of [pipApp] window always moves up during the animation. */ @Presubmit @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt index 9f81ba8eee87..7085d559f3f2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.RequiresDevice import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import com.android.server.wm.flicker.testapp.ActivityOptions import org.junit.FixMethodOrder import org.junit.Test @@ -34,7 +34,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) { +class PipDragTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { private var isDraggedLeft: Boolean = true override val thisTransition: FlickerBuilder.() -> Unit = { @@ -81,13 +81,11 @@ class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt index 9fe9f52fd4af..2b87766daab3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Postsubmit import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) { +class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { // represents the direction in which the pip window should be snapping private var willSnapRight: Boolean = true @@ -99,15 +99,14 @@ class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt index 60bf5ffdc7af..adc5ee32cdd3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Postsubmit import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test @@ -36,7 +36,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 270677470) -class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) { +class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) } } @@ -57,15 +57,14 @@ class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index 17a178f78de3..096af39488e9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -22,7 +22,7 @@ import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome import android.tools.device.helpers.WindowUtils import com.android.server.wm.flicker.helpers.PipAppHelper @@ -32,7 +32,7 @@ import com.android.wm.shell.flicker.BaseTest import com.google.common.truth.Truth import org.junit.Test -abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) { +abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected val pipApp = PipAppHelper(instrumentation) protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation) protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) @@ -78,16 +78,16 @@ abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) { /** Defines the default method of entering PiP */ protected open val defaultEnterPip: FlickerBuilder.() -> Unit = { setup { - pipApp.launchViaIntentAndWaitForPip(wmHelper, - stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")) + pipApp.launchViaIntentAndWaitForPip( + wmHelper, + stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true") + ) } } /** Defines the default teardown required to clean up after the test */ protected open val defaultTeardown: FlickerBuilder.() -> Unit = { - teardown { - pipApp.exit(wmHelper) - } + teardown { pipApp.exit(wmHelper) } } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt index c618e5a24fdf..c315e744bd55 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt @@ -21,10 +21,11 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.Rotation +import android.tools.common.flicker.assertions.FlickerTest import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.testapp.ActivityOptions @@ -46,7 +47,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransition(flicker) { +open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) @@ -69,20 +70,19 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit setup { // Launch the PiP activity fixed as landscape. pipApp.launchViaIntent( - wmHelper, - stringExtras = - mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) + wmHelper, + stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) ) // Enter PiP. broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP) // System bar may fade out during fixed rotation. wmHelper - .StateSyncBuilder() - .withPipShown() - .withRotation(Rotation.ROTATION_0) - .withNavOrTaskBarVisible() - .withStatusBarVisible() - .waitForAndVerify() + .StateSyncBuilder() + .withPipShown() + .withRotation(Rotation.ROTATION_0) + .withNavOrTaskBarVisible() + .withStatusBarVisible() + .waitForAndVerify() } } @@ -150,7 +150,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + return LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt index 43d6c8f26126..0ff9cfff873e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt @@ -17,10 +17,11 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit +import android.tools.common.flicker.assertions.FlickerTest import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.SimpleAppHelper @@ -58,7 +59,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowPipAndRotateDisplay(flicker: FlickerTest) : PipTransition(flicker) { +open class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val testApp = SimpleAppHelper(instrumentation) private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation) private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) @@ -154,13 +155,13 @@ open class ShowPipAndRotateDisplay(flicker: FlickerTest) : PipTransition(flicker /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() + return LegacyFlickerTestFactory.rotationTests() } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt index b7a2c47e3b32..25164711b2e5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt @@ -16,9 +16,10 @@ package com.android.wm.shell.flicker.pip +import android.tools.common.flicker.assertions.FlickerTest import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -27,18 +28,18 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowPipAndRotateDisplayCfArm(flicker: FlickerTest) : ShowPipAndRotateDisplay(flicker) { +class ShowPipAndRotateDisplayCfArm(flicker: LegacyFlickerTest) : ShowPipAndRotateDisplay(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation - * and navigation modes. + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen + * orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() + return LegacyFlickerTestFactory.rotationTests() } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt index 72f25f36c9d9..a43ad9b4dd39 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt @@ -17,15 +17,14 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.EdgeExtensionComponentMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -46,13 +45,13 @@ import org.junit.runners.Parameterized /** * Test copy content from the left to the right side of the split-screen. * - * To run this test: `atest WMShellFlickerTests:CopyContentInSplit` + * To run this test: `atest WMShellFlickerTestsSplitScreen:CopyContentInSplit` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CopyContentInSplit(override val flicker: FlickerTest) : +class CopyContentInSplit(override val flicker: LegacyFlickerTest) : CopyContentInSplitBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -62,7 +61,6 @@ class CopyContentInSplit(override val flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test override fun cujCompleted() { @@ -138,8 +136,6 @@ class CopyContentInSplit(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt index 4505b9978b76..0b8f109b0e42 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt @@ -21,7 +21,7 @@ import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions @@ -41,13 +41,13 @@ import org.junit.runners.Parameterized /** * Test dismiss split screen by dragging the divider bar. * - * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider` + * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByDivider` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class DismissSplitScreenByDivider(override val flicker: FlickerTest) : +class DismissSplitScreenByDivider(override val flicker: LegacyFlickerTest) : DismissSplitScreenByDividerBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index e05b22141e4d..38d4b4029c64 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesInvisible @@ -38,13 +38,13 @@ import org.junit.runners.Parameterized /** * Test dismiss split screen by go home. * - * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByGoHome` + * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByGoHome` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class DismissSplitScreenByGoHome(override val flicker: FlickerTest) : +class DismissSplitScreenByGoHome(override val flicker: LegacyFlickerTest) : DismissSplitScreenByGoHomeBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -154,8 +154,6 @@ class DismissSplitScreenByGoHome(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt index ed3df9ced3b0..a118c08b35e2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt @@ -17,13 +17,12 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -44,13 +43,13 @@ import org.junit.runners.Parameterized /** * Test resize split by dragging the divider bar. * - * To run this test: `atest WMShellFlickerTests:DragDividerToResize` + * To run this test: `atest WMShellFlickerTestsSplitScreen:DragDividerToResize` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class DragDividerToResize(override val flicker: FlickerTest) : +class DragDividerToResize(override val flicker: LegacyFlickerTest) : DragDividerToResizeBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -60,7 +59,6 @@ class DragDividerToResize(override val flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test override fun cujCompleted() { @@ -129,8 +127,6 @@ class DragDividerToResize(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt index e55868675da7..05c048050b3b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt @@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -45,13 +45,13 @@ import org.junit.runners.Parameterized * Test enter split screen by dragging app icon from all apps. This test is only for large screen * devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromAllApps` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromAllApps(override val flicker: FlickerTest) : +class EnterSplitScreenByDragFromAllApps(override val flicker: LegacyFlickerTest) : EnterSplitScreenByDragFromAllAppsBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -160,11 +160,10 @@ class EnterSplitScreenByDragFromAllApps(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt index ab8ecc54e71c..3a75fa60a865 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt @@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -44,13 +44,13 @@ import org.junit.runners.Parameterized * Test enter split screen by dragging app icon from notification. This test is only for large * screen devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromNotification` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromNotification(override val flicker: FlickerTest) : +class EnterSplitScreenByDragFromNotification(override val flicker: LegacyFlickerTest) : EnterSplitScreenByDragFromNotificationBenchmark(flicker), ICommonAssertions { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -162,11 +162,10 @@ class EnterSplitScreenByDragFromNotification(override val flicker: FlickerTest) companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt index 516ca97bc531..6d73f92637db 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd @@ -41,13 +41,13 @@ import org.junit.runners.Parameterized /** * Test enter split screen by dragging a shortcut. This test is only for large screen devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromShortcut` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromShortcut` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromShortcut(override val flicker: FlickerTest) : +class EnterSplitScreenByDragFromShortcut(override val flicker: LegacyFlickerTest) : EnterSplitScreenByDragFromShortcutBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit @@ -105,11 +105,10 @@ class EnterSplitScreenByDragFromShortcut(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt index 4af7e248b660..15cae6947f88 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt @@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -45,13 +45,13 @@ import org.junit.runners.Parameterized * Test enter split screen by dragging app icon from taskbar. This test is only for large screen * devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromTaskbar` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenByDragFromTaskbar(override val flicker: FlickerTest) : +class EnterSplitScreenByDragFromTaskbar(override val flicker: LegacyFlickerTest) : EnterSplitScreenByDragFromTaskbarBenchmark(flicker), ICommonAssertions { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -163,10 +163,9 @@ class EnterSplitScreenByDragFromTaskbar(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt index faad9e82ffef..90399fca2574 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible @@ -40,13 +40,13 @@ import org.junit.runners.Parameterized /** * Test enter split screen from Overview. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenFromOverview` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterSplitScreenFromOverview(override val flicker: FlickerTest) : +class EnterSplitScreenFromOverview(override val flicker: LegacyFlickerTest) : EnterSplitScreenFromOverviewBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -106,8 +106,6 @@ class EnterSplitScreenFromOverview(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt index 195b73a14a72..580b153421a4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt @@ -18,11 +18,11 @@ package com.android.wm.shell.flicker.splitscreen import android.content.Context import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.helpers.setRotation import com.android.wm.shell.flicker.BaseBenchmarkTest -abstract class SplitScreenBase(flicker: FlickerTest) : BaseBenchmarkTest(flicker) { +abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) { protected val context: Context = instrumentation.context protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation) protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt index 1063dfd8d737..27eaa40ee49b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt @@ -39,6 +39,7 @@ import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.NotificationAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME import org.junit.Assert.assertNotNull @@ -112,6 +113,17 @@ internal object SplitScreenUtils { waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } + fun enterSplitViaIntent( + wmHelper: WindowManagerStateHelper, + primaryApp: StandardAppHelper, + secondaryApp: StandardAppHelper + ) { + val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true") + primaryApp.launchViaIntent(wmHelper, null, null, + stringExtras) + waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) { // Note: The initial split position in landscape is different between tablet and phone. // In landscape, tablet will let the first app split to right side, and phone will diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt index b4c6afd14a90..e0a47b394ba1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt @@ -16,15 +16,14 @@ package com.android.wm.shell.flicker.splitscreen -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -45,13 +44,13 @@ import org.junit.runners.Parameterized /** * Test double tap the divider bar to switch the two apps. * - * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchAppByDoubleTapDivider` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) : +class SwitchAppByDoubleTapDivider(override val flicker: LegacyFlickerTest) : SwitchAppByDoubleTapDividerBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -61,7 +60,6 @@ class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test override fun cujCompleted() { @@ -122,11 +120,10 @@ class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt index 078d95de1dd0..a4060092b422 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible @@ -39,13 +39,13 @@ import org.junit.runners.Parameterized /** * Test quick switch to split pair from another app. * - * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromAnotherApp` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBackToSplitFromAnotherApp(override val flicker: FlickerTest) : +class SwitchBackToSplitFromAnotherApp(override val flicker: LegacyFlickerTest) : SwitchBackToSplitFromAnotherAppBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -149,11 +149,10 @@ class SwitchBackToSplitFromAnotherApp(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index 7c84243e00d7..251bd1030da3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible @@ -39,13 +39,13 @@ import org.junit.runners.Parameterized /** * Test quick switch to split pair from home. * - * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromHome` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBackToSplitFromHome(override val flicker: FlickerTest) : +class SwitchBackToSplitFromHome(override val flicker: LegacyFlickerTest) : SwitchBackToSplitFromHomeBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -149,11 +149,10 @@ class SwitchBackToSplitFromHome(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt index 7c46d3e099a2..1dd45fef30cc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.appWindowBecomesVisible @@ -39,13 +39,13 @@ import org.junit.runners.Parameterized /** * Test switch back to split pair from recent. * - * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromRecent` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBackToSplitFromRecent(override val flicker: FlickerTest) : +class SwitchBackToSplitFromRecent(override val flicker: LegacyFlickerTest) : SwitchBackToSplitFromRecentBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -149,11 +149,10 @@ class SwitchBackToSplitFromRecent(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt index 2cedc3570cfd..8f867df3fea1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt @@ -17,13 +17,12 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -48,13 +47,13 @@ import org.junit.runners.Parameterized /** * Test quick switch between two split pairs. * - * To run this test: `atest WMShellFlickerTests:SwitchBetweenSplitPairs` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBetweenSplitPairs` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchBetweenSplitPairs(override val flicker: FlickerTest) : +class SwitchBetweenSplitPairs(override val flicker: LegacyFlickerTest) : SwitchBetweenSplitPairsBenchmark(flicker), ICommonAssertions { override val transition: FlickerBuilder.() -> Unit get() = { @@ -64,7 +63,6 @@ class SwitchBetweenSplitPairs(override val flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test override fun cujCompleted() { @@ -225,8 +223,6 @@ class SwitchBetweenSplitPairs(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index 676c150815ad..994d6cbfaa1f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt @@ -21,8 +21,8 @@ import android.tools.common.NavBar import android.tools.common.flicker.subject.region.RegionSubject import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.ICommonAssertions import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT @@ -37,21 +37,21 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test unlocking insecure keyguard to back to split screen tasks and verify the transition behavior. + * Test unlocking insecure keyguard to back to split screen tasks and verify the transition + * behavior. * - * To run this test: `atest WMShellFlickerTests:UnlockKeyguardToSplitScreen` + * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen` */ @RequiresDevice @Postsubmit @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) : - UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions { +class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) : + UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { - defaultSetup(this) defaultTeardown(this) thisTransition(this) } @@ -65,33 +65,35 @@ class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) : @Test fun primaryAppBoundsIsVisibleAtEnd() = - flicker.splitAppLayerBoundsIsVisibleAtEnd( - primaryApp, - landscapePosLeft = false, - portraitPosTop = false - ) + flicker.splitAppLayerBoundsIsVisibleAtEnd( + primaryApp, + landscapePosLeft = false, + portraitPosTop = false + ) @Test fun secondaryAppBoundsIsVisibleAtEnd() = - flicker.splitAppLayerBoundsIsVisibleAtEnd( - secondaryApp, - landscapePosLeft = true, - portraitPosTop = true - ) + flicker.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, + landscapePosLeft = true, + portraitPosTop = true + ) - @Test - fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp) + @Test fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp) - @Test - fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp) + @Test fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp) @Test fun notOverlapsForPrimaryAndSecondaryAppLayers() { flicker.assertLayers { this.invoke("notOverlapsForPrimaryAndSecondaryLayers") { - val primaryAppRegions = it.subjects.filter { subject -> - subject.name.contains(primaryApp.toLayerName()) && subject.isVisible - }.mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }.toTypedArray() + val primaryAppRegions = + it.subjects + .filter { subject -> + subject.name.contains(primaryApp.toLayerName()) && subject.isVisible + } + .mapNotNull { primaryApp -> primaryApp.layer.visibleRegion } + .toTypedArray() val primaryAppRegionArea = RegionSubject(primaryAppRegions, it.timestamp) it.visibleRegion(secondaryApp).notOverlaps(primaryAppRegionArea.region) @@ -102,10 +104,9 @@ class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt index a5ad97d8ad65..d1ca9eac198d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils @@ -37,7 +36,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) : +open class CopyContentInSplitBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val textEditApp = SplitScreenUtils.getIme(instrumentation) protected val magnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#") @@ -64,7 +63,6 @@ open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() { @@ -74,8 +72,6 @@ open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt index fa6a4bfbcda6..73acb1f0cc47 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt @@ -16,13 +16,12 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenDismissed import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -37,7 +36,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScreenBase(flicker) { +open class DismissSplitScreenByDividerBenchmark(override val flicker: LegacyFlickerTest) : + SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } @@ -70,7 +70,6 @@ open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScr } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = false) @@ -78,8 +77,6 @@ open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScr companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt index d2beb678a4d5..86ffd2af6748 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt @@ -16,13 +16,12 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenDismissed import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -37,7 +36,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest) : +open class DismissSplitScreenByGoHomeBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -57,7 +56,6 @@ open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = true) @@ -65,8 +63,6 @@ open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt index e95fd947ebde..dfde3b669813 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt @@ -16,13 +16,12 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils @@ -38,7 +37,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) : +open class DragDividerToResizeBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -60,7 +59,6 @@ open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() { @@ -71,8 +69,6 @@ open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt index 63b74e29c39c..d13e4134a961 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -40,7 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: FlickerTest) : +open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit @@ -72,7 +71,6 @@ open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: Flic } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = @@ -86,11 +84,10 @@ open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: Flic companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt index e94da8713b43..1d4166922b13 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -40,8 +39,9 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker: FlickerTest) : - SplitScreenBase(flicker) { +open class EnterSplitScreenByDragFromNotificationBenchmark( + override val flicker: LegacyFlickerTest +) : SplitScreenBase(flicker) { protected val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation) protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -69,7 +69,6 @@ open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker: } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = @@ -83,11 +82,10 @@ open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker: companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt index f41117f0d54e..b4bafa79cd48 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -40,7 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) : +open class EnterSplitScreenByDragFromShortcutBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { @Before fun before() { @@ -72,7 +71,6 @@ open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = @@ -86,11 +84,10 @@ open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) : companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt index 12f610b73e13..da44ecdb9304 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -40,7 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: FlickerTest) : +open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -67,7 +66,6 @@ open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: Flic } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = @@ -86,10 +84,9 @@ open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: Flic companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt index 77818d380053..af06d6da2518 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt @@ -16,13 +16,12 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -37,7 +36,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTest) : +open class EnterSplitScreenFromOverviewBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -66,7 +65,6 @@ open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTe } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) @@ -74,8 +72,6 @@ open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTe companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt index 6ff22902667c..23156b5d1628 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt @@ -16,15 +16,14 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.filters.RequiresDevice @@ -40,7 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTest) : +open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -136,7 +135,6 @@ open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTes } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() { @@ -147,11 +145,10 @@ open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTes companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt index 400adea8880d..2d810d3e2631 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -38,7 +37,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: FlickerTest) : +open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation) @@ -65,7 +64,6 @@ open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: Flicke } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) @@ -73,11 +71,10 @@ open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: Flicke companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt index 1ec43405ee44..f6df1e42d1b7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -38,7 +37,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest) : +open class SwitchBackToSplitFromHomeBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -63,7 +62,6 @@ open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest) } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) @@ -71,11 +69,10 @@ open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest) companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt index 9757153929ca..ba46bdcdad21 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt @@ -16,14 +16,13 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitScreenEntered import com.android.wm.shell.flicker.splitscreen.SplitScreenBase @@ -38,7 +37,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTest) : +open class SwitchBackToSplitFromRecentBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { @@ -63,7 +62,6 @@ open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTes } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true) @@ -71,11 +69,10 @@ open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTes companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt index c19a38dc9daf..0d871e500688 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt @@ -16,13 +16,12 @@ package com.android.wm.shell.flicker.splitscreen.benchmark -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils @@ -36,7 +35,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) : +open class SwitchBetweenSplitPairsBenchmark(override val flicker: LegacyFlickerTest) : SplitScreenBase(flicker) { protected val thirdApp = SplitScreenUtils.getIme(instrumentation) protected val fourthApp = SplitScreenUtils.getSendNotification(instrumentation) @@ -66,13 +65,11 @@ open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) : } @PlatinumTest(focusArea = "sysui") - @IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {} + @Presubmit @Test open fun cujCompleted() {} companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt index 5f16e5b4d65e..7952b7125a34 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt @@ -19,8 +19,8 @@ package com.android.wm.shell.flicker.splitscreen.benchmark import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.splitscreen.SplitScreenBase import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils @@ -33,11 +33,11 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: FlickerTest) : - SplitScreenBase(flicker) { +open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: LegacyFlickerTest) : + SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) } + setup { SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp) } transitions { device.sleep() wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() @@ -58,10 +58,9 @@ open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: FlickerTes companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java index 8278c67a9b4f..0dc16f44340f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java @@ -64,18 +64,27 @@ public class BubbleOverflowTest extends ShellTestCase { } @Test - public void test_initialize() { + public void test_initialize_forStack() { assertThat(mOverflow.getExpandedView()).isNull(); - mOverflow.initialize(mBubbleController); + mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false); assertThat(mOverflow.getExpandedView()).isNotNull(); assertThat(mOverflow.getExpandedView().getBubbleKey()).isEqualTo(BubbleOverflow.KEY); + assertThat(mOverflow.getBubbleBarExpandedView()).isNull(); + } + + @Test + public void test_initialize_forBubbleBar() { + mOverflow.initialize(mBubbleController, /* forBubbleBar= */ true); + + assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull(); + assertThat(mOverflow.getExpandedView()).isNull(); } @Test public void test_cleanUpExpandedState() { - mOverflow.createExpandedView(); + mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false); assertThat(mOverflow.getExpandedView()).isNotNull(); mOverflow.cleanUpExpandedState(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java deleted file mode 100644 index 58e91cb50c7a..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java +++ /dev/null @@ -1,163 +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.wm.shell.kidsmode; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ParceledListSlice; -import android.content.res.Resources; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.window.ITaskOrganizerController; -import android.window.TaskAppearedInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.sysui.ShellCommandHandler; -import com.android.wm.shell.sysui.ShellInit; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Optional; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class KidsModeTaskOrganizerTest extends ShellTestCase { - @Mock private ITaskOrganizerController mTaskOrganizerController; - @Mock private Context mContext; - @Mock private Handler mHandler; - @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private ShellExecutor mTestExecutor; - @Mock private DisplayController mDisplayController; - @Mock private SurfaceControl mLeash; - @Mock private WindowContainerToken mToken; - @Mock private WindowContainerTransaction mTransaction; - @Mock private KidsModeSettingsObserver mObserver; - @Mock private ShellInit mShellInit; - @Mock private ShellCommandHandler mShellCommandHandler; - @Mock private DisplayInsetsController mDisplayInsetsController; - @Mock private Resources mResources; - - KidsModeTaskOrganizer mOrganizer; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - try { - doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList()) - .when(mTaskOrganizerController).registerTaskOrganizer(any()); - } catch (RemoteException e) { - } - // NOTE: KidsModeTaskOrganizer should have a null CompatUIController. - doReturn(mResources).when(mContext).getResources(); - final KidsModeTaskOrganizer kidsModeTaskOrganizer = new KidsModeTaskOrganizer(mContext, - mShellInit, mShellCommandHandler, mTaskOrganizerController, mSyncTransactionQueue, - mDisplayController, mDisplayInsetsController, Optional.empty(), Optional.empty(), - mObserver, mTestExecutor, mHandler); - mOrganizer = spy(kidsModeTaskOrganizer); - doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction(); - doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY); - } - - @Test - public void instantiateController_addInitCallback() { - verify(mShellInit, times(1)).addInitCallback(any(), any()); - } - - @Test - public void testKidsModeOn() { - doReturn(true).when(mObserver).isEnabled(); - - mOrganizer.updateKidsModeState(); - - verify(mOrganizer, times(1)).enable(); - verify(mOrganizer, times(1)).registerOrganizer(); - verify(mOrganizer, times(1)).createRootTask( - eq(DEFAULT_DISPLAY), eq(WINDOWING_MODE_FULLSCREEN), eq(mOrganizer.mCookie)); - verify(mOrganizer, times(1)) - .setOrientationRequestPolicy(eq(true), any(), any()); - - final ActivityManager.RunningTaskInfo rootTask = createTaskInfo(12, - WINDOWING_MODE_FULLSCREEN, mOrganizer.mCookie); - mOrganizer.onTaskAppeared(rootTask, mLeash); - - assertThat(mOrganizer.mLaunchRootLeash).isEqualTo(mLeash); - assertThat(mOrganizer.mLaunchRootTask).isEqualTo(rootTask); - } - - @Test - public void testKidsModeOff() { - doReturn(true).when(mObserver).isEnabled(); - mOrganizer.updateKidsModeState(); - final ActivityManager.RunningTaskInfo rootTask = createTaskInfo(12, - WINDOWING_MODE_FULLSCREEN, mOrganizer.mCookie); - mOrganizer.onTaskAppeared(rootTask, mLeash); - - doReturn(false).when(mObserver).isEnabled(); - mOrganizer.updateKidsModeState(); - - verify(mOrganizer, times(1)).disable(); - verify(mOrganizer, times(1)).unregisterOrganizer(); - verify(mOrganizer, times(1)).deleteRootTask(rootTask.token); - verify(mOrganizer, times(1)) - .setOrientationRequestPolicy(eq(false), any(), any()); - assertThat(mOrganizer.mLaunchRootLeash).isNull(); - assertThat(mOrganizer.mLaunchRootTask).isNull(); - } - - private ActivityManager.RunningTaskInfo createTaskInfo( - int taskId, int windowingMode, IBinder cookies) { - ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); - taskInfo.taskId = taskId; - taskInfo.token = mToken; - taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); - final ArrayList<IBinder> launchCookies = new ArrayList<>(); - if (cookies != null) { - launchCookies.add(cookies); - } - taskInfo.launchCookies = launchCookies; - return taskInfo; - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index b00a60c593e6..5efd9ad97a3e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -76,6 +76,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitDecorManager; import com.android.wm.shell.common.split.SplitLayout; +import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.WindowDecorViewModel; @@ -105,6 +106,7 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private WindowDecorViewModel mWindowDecorViewModel; @Mock private ShellExecutor mMainExecutor; @Mock private LaunchAdjacentController mLaunchAdjacentController; + @Mock private DefaultMixedHandler mMixedHandler; private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; @@ -136,6 +138,7 @@ public class SplitTransitionTests extends ShellTestCase { mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mMainExecutor, Optional.empty(), mLaunchAdjacentController, Optional.empty()); + mStageCoordinator.setMixedHandler(mMixedHandler); mSplitScreenTransitions = mStageCoordinator.getSplitTransitions(); doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class)) .when(mTransitions).startTransition(anyInt(), any(), any()); diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index aeaa17198be5..4c9a23d3fde0 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -96,17 +96,33 @@ public: sk_sp<SkColorSpace> decodeColorSpace = mGainmapBRD->computeOutputColorSpace(decodeColorType, nullptr); SkBitmap bm; - HeapAllocator heapAlloc; - if (!mGainmapBRD->decodeRegion(&bm, &heapAlloc, desiredSubset, sampleSize, decodeColorType, - requireUnpremul, decodeColorSpace)) { - ALOGE("Error decoding Gainmap region"); - return false; - } - sk_sp<Bitmap> nativeBitmap(heapAlloc.getStorageObjAndReset()); + // Because we must match the dimensions of the base bitmap, we always use a + // recycling allocator even though we are allocating a new bitmap. This is to ensure + // that if a recycled bitmap was used for the base image that we match the relative + // dimensions of that base image. The behavior of BRD here is: + // if inBitmap is specified -> output dimensions are always equal to the inBitmap's + // if no bitmap is reused -> output dimensions are the intersect of the desiredSubset & + // the image bounds + // The handling of the above conditionals are baked into the desiredSubset, so we + // simply need to ensure that the resulting bitmap is the exact same width/height as + // the specified desiredSubset regardless of the intersection to the image bounds. + // kPremul_SkAlphaType is used just as a placeholder as it doesn't change the underlying + // allocation type. RecyclingClippingPixelAllocator will populate this with the + // actual alpha type in either allocPixelRef() or copyIfNecessary() + sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap( + SkImageInfo::Make(desiredSubset.width(), desiredSubset.height(), decodeColorType, + kPremul_SkAlphaType, decodeColorSpace)); if (!nativeBitmap) { ALOGE("OOM allocating Bitmap for Gainmap"); return false; } + RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false); + if (!mGainmapBRD->decodeRegion(&bm, &allocator, desiredSubset, sampleSize, decodeColorType, + requireUnpremul, decodeColorSpace)) { + ALOGE("Error decoding Gainmap region"); + return false; + } + allocator.copyIfNecessary(); auto gainmap = sp<uirenderer::Gainmap>::make(); if (!gainmap) { ALOGE("OOM allocating Gainmap"); @@ -238,13 +254,11 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in // Recycle a bitmap if possible. android::Bitmap* recycledBitmap = nullptr; - size_t recycledBytes = 0; if (javaBitmap) { recycledBitmap = &bitmap::toBitmap(inBitmapHandle); if (recycledBitmap->isImmutable()) { ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); } - recycledBytes = recycledBitmap->getAllocationByteCount(); } auto* brd = reinterpret_cast<BitmapRegionDecoderWrapper*>(brdHandle); @@ -263,7 +277,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in // Set up the pixel allocator skia::BRDAllocator* allocator = nullptr; - RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); + RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap); HeapAllocator heapAlloc; if (javaBitmap) { allocator = &recycleAlloc; @@ -277,7 +291,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in decodeColorType, colorSpace); // Decode the region. - SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); + const SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); SkBitmap bitmap; if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, decodeColorType, requireUnpremul, decodeColorSpace)) { @@ -307,10 +321,27 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType)); } + if (javaBitmap) { + recycleAlloc.copyIfNecessary(); + } + sp<uirenderer::Gainmap> gainmap; bool hasGainmap = brd->hasGainmap(); if (hasGainmap) { - SkIRect gainmapSubset = brd->calculateGainmapRegion(subset); + SkIRect adjustedSubset{}; + if (javaBitmap) { + // Clamp to the width/height of the recycled bitmap in case the reused bitmap + // was too small for the specified rectangle, in which case we need to clip + adjustedSubset = SkIRect::MakeXYWH(inputX, inputY, + std::min(subset.width(), recycledBitmap->width()), + std::min(subset.height(), recycledBitmap->height())); + } else { + // We are not recycling, so use the decoded width/height for calculating the gainmap + // subset instead to ensure the gainmap region proportionally matches + adjustedSubset = SkIRect::MakeXYWH(std::max(0, inputX), std::max(0, inputY), + bitmap.width(), bitmap.height()); + } + SkIRect gainmapSubset = brd->calculateGainmapRegion(adjustedSubset); if (!brd->decodeGainmapRegion(&gainmap, gainmapSubset, sampleSize, requireUnpremul)) { // If there is an error decoding Gainmap - we don't fail. We just don't provide Gainmap hasGainmap = false; @@ -319,7 +350,6 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in // If we may have reused a bitmap, we need to indicate that the pixels have changed. if (javaBitmap) { - recycleAlloc.copyIfNecessary(); if (hasGainmap) { recycledBitmap->setGainmap(std::move(gainmap)); } @@ -331,6 +361,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in if (!requireUnpremul) { bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied; } + if (isHardware) { sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap); if (hasGainmap) { diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 914266de2753..78b4f7b7654d 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -620,13 +620,13 @@ bool HeapAllocator::allocPixelRef(SkBitmap* bitmap) { //////////////////////////////////////////////////////////////////////////////// -RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator( - android::Bitmap* recycledBitmap, size_t recycledBytes) - : mRecycledBitmap(recycledBitmap) - , mRecycledBytes(recycledBytes) - , mSkiaBitmap(nullptr) - , mNeedsCopy(false) -{} +RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, + bool mustMatchColorType) + : mRecycledBitmap(recycledBitmap) + , mRecycledBytes(recycledBitmap ? recycledBitmap->getAllocationByteCount() : 0) + , mSkiaBitmap(nullptr) + , mNeedsCopy(false) + , mMustMatchColorType(mustMatchColorType) {} RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {} @@ -637,10 +637,16 @@ bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) { LOG_ALWAYS_FATAL_IF(!bitmap); mSkiaBitmap = bitmap; - // This behaves differently than the RecyclingPixelAllocator. For backwards - // compatibility, the original color type of the recycled bitmap must be maintained. - if (mRecycledBitmap->info().colorType() != bitmap->colorType()) { - return false; + if (mMustMatchColorType) { + // This behaves differently than the RecyclingPixelAllocator. For backwards + // compatibility, the original color type of the recycled bitmap must be maintained. + if (mRecycledBitmap->info().colorType() != bitmap->colorType()) { + ALOGW("recycled color type %d != bitmap color type %d", + mRecycledBitmap->info().colorType(), bitmap->colorType()); + return false; + } + } else { + mRecycledBitmap->reconfigure(mRecycledBitmap->info().makeColorType(bitmap->colorType())); } // The Skia bitmap specifies the width and height needed by the decoder. @@ -695,7 +701,7 @@ bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) { void RecyclingClippingPixelAllocator::copyIfNecessary() { if (mNeedsCopy) { mRecycledBitmap->ref(); - SkPixelRef* recycledPixels = mRecycledBitmap; + android::Bitmap* recycledPixels = mRecycledBitmap; void* dst = recycledPixels->pixels(); const size_t dstRowBytes = mRecycledBitmap->rowBytes(); const size_t bytesToCopy = std::min(mRecycledBitmap->info().minRowBytes(), @@ -708,6 +714,8 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() { dst = reinterpret_cast<void*>( reinterpret_cast<uint8_t*>(dst) + dstRowBytes); } + recycledPixels->setAlphaType(mSkiaBitmap->alphaType()); + recycledPixels->setColorSpace(mSkiaBitmap->refColorSpace()); recycledPixels->notifyPixelsChanged(); recycledPixels->unref(); } diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index 24f9e82b5340..23ab5dd38b1a 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -222,9 +222,8 @@ private: */ class RecyclingClippingPixelAllocator : public android::skia::BRDAllocator { public: - RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, - size_t recycledBytes); + bool mustMatchColorType = true); ~RecyclingClippingPixelAllocator(); @@ -252,6 +251,7 @@ private: const size_t mRecycledBytes; SkBitmap* mSkiaBitmap; bool mNeedsCopy; + const bool mMustMatchColorType; }; class AshmemPixelAllocator : public SkBitmap::Allocator { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index d4e919fefbbd..31a92ac5ab23 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -657,7 +657,6 @@ void VulkanManager::destroySurface(VulkanSurface* surface) { if (VK_NULL_HANDLE != mGraphicsQueue) { mQueueWaitIdle(mGraphicsQueue); } - mDeviceWaitIdle(mDevice); delete surface; } diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index 9be77281eb11..0eb657aba033 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -709,7 +709,7 @@ public class Location implements Parcelable { /** * Returns the Mean Sea Level altitude of this location in meters. * - * <p>This is only valid if {@link #hasMslAltitude()} is true. + * @throws IllegalStateException if {@link #hasMslAltitude()} is false. */ public @FloatRange double getMslAltitudeMeters() { Preconditions.checkState(hasMslAltitude(), @@ -744,7 +744,7 @@ public class Location implements Parcelable { * percentile confidence level. This means that there is 68% chance that the true Mean Sea Level * altitude of this location falls within {@link #getMslAltitudeMeters()} +/- this uncertainty. * - * <p>This is only valid if {@link #hasMslAltitudeAccuracy()} is true. + * @throws IllegalStateException if {@link #hasMslAltitudeAccuracy()} is false. */ public @FloatRange(from = 0.0) float getMslAltitudeAccuracyMeters() { Preconditions.checkState(hasMslAltitudeAccuracy(), diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index f86b9af25933..23f87abaffed 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -1566,7 +1566,7 @@ public class ExifInterface { FileInputStream in = null; try { in = new FileInputStream(fileDescriptor); - loadAttributes(in, fileDescriptor); + loadAttributes(in); } finally { closeQuietly(in); if (isFdDuped) { @@ -1637,7 +1637,7 @@ public class ExifInterface { mSeekableFileDescriptor = null; } } - loadAttributes(inputStream, null); + loadAttributes(inputStream); } /** @@ -1963,7 +1963,7 @@ public class ExifInterface { * This function decides which parser to read the image data according to the given input stream * type and the content of the input stream. */ - private void loadAttributes(@NonNull InputStream in, @Nullable FileDescriptor fd) { + private void loadAttributes(@NonNull InputStream in) { if (in == null) { throw new NullPointerException("inputstream shouldn't be null"); } @@ -1993,7 +1993,7 @@ public class ExifInterface { break; } case IMAGE_TYPE_HEIF: { - getHeifAttributes(inputStream, fd); + getHeifAttributes(inputStream); break; } case IMAGE_TYPE_ORF: { @@ -2580,7 +2580,7 @@ public class ExifInterface { } else if (isSeekableFD(in.getFD())) { mSeekableFileDescriptor = in.getFD(); } - loadAttributes(in, null); + loadAttributes(in); } finally { closeQuietly(in); if (modernFd != null) { @@ -3068,66 +3068,59 @@ public class ExifInterface { } } - private void getHeifAttributes(ByteOrderedDataInputStream in, @Nullable FileDescriptor fd) - throws IOException { + private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { - if (fd != null) { - retriever.setDataSource(fd); - } else { - retriever.setDataSource(new MediaDataSource() { - long mPosition; + retriever.setDataSource(new MediaDataSource() { + long mPosition; - @Override - public void close() throws IOException {} + @Override + public void close() throws IOException {} - @Override - public int readAt(long position, byte[] buffer, int offset, int size) - throws IOException { - if (size == 0) { - return 0; - } - if (position < 0) { - return -1; - } - try { - if (mPosition != position) { - // We don't allow seek to positions after the available bytes, - // the input stream won't be able to seek back then. - // However, if we hit an exception before (mPosition set to -1), - // let it try the seek in hope it might recover. - if (mPosition >= 0 && position >= mPosition + in.available()) { - return -1; - } - in.seek(position); - mPosition = position; + @Override + public int readAt(long position, byte[] buffer, int offset, int size) + throws IOException { + if (size == 0) { + return 0; + } + if (position < 0) { + return -1; + } + try { + if (mPosition != position) { + // We don't allow seek to positions after the available bytes, + // the input stream won't be able to seek back then. + // However, if we hit an exception before (mPosition set to -1), + // let it try the seek in hope it might recover. + if (mPosition >= 0 && position >= mPosition + in.available()) { + return -1; } + in.seek(position); + mPosition = position; + } - // If the read will cause us to go over the available bytes, - // reduce the size so that we stay in the available range. - // Otherwise the input stream may not be able to seek back. - if (size > in.available()) { - size = in.available(); - } + // If the read will cause us to go over the available bytes, + // reduce the size so that we stay in the available range. + // Otherwise the input stream may not be able to seek back. + if (size > in.available()) { + size = in.available(); + } - int bytesRead = in.read(buffer, offset, size); - if (bytesRead >= 0) { - mPosition += bytesRead; - return bytesRead; - } - } catch (IOException e) { - // absorb the exception and fall through to the 'failed read' path below + int bytesRead = in.read(buffer, offset, size); + if (bytesRead >= 0) { + mPosition += bytesRead; + return bytesRead; } - mPosition = -1; // need to seek on next read - return -1; - } + } catch (IOException e) {} + mPosition = -1; // need to seek on next read + return -1; + } - @Override - public long getSize() throws IOException { - return -1; - } - }); - } + @Override + public long getSize() throws IOException { + return -1; + } + }); String exifOffsetStr = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET); diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java index 6744359d12d7..9b238e172247 100644 --- a/media/java/android/media/ThumbnailUtils.java +++ b/media/java/android/media/ThumbnailUtils.java @@ -49,7 +49,6 @@ import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; @@ -256,19 +255,17 @@ public class ThumbnailUtils { // get orientation if (MediaFile.isExifMimeType(mimeType)) { - try (FileInputStream is = new FileInputStream(file)) { - exif = new ExifInterface(is.getFD()); - switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) { - case ExifInterface.ORIENTATION_ROTATE_90: - orientation = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - orientation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - orientation = 270; - break; - } + exif = new ExifInterface(file); + switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) { + case ExifInterface.ORIENTATION_ROTATE_90: + orientation = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + orientation = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + orientation = 270; + break; } } diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 094a33f3f2ba..ed68d1eb5724 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -455,6 +455,11 @@ public class AudioMix { if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) { throw new IllegalArgumentException("Unsupported device on non-playback mix"); } + } else if (mDeviceSystemType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX) { + if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) { + throw new IllegalArgumentException( + "DEVICE_OUT_REMOTE_SUBMIX device is not supported on non-playback mix"); + } } else { if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_RENDER) { throw new IllegalArgumentException( diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index f1cffb63af07..fb72c7ba9db4 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -159,30 +159,42 @@ public final class MediaProjection { * @param surface The surface to which the content of the virtual display should be rendered, * or null if there is none initially. * @param flags A combination of virtual display flags. See {@link DisplayManager} for the - * full list of flags. + * full list of flags. Note that + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION} + * is always enabled. The following flags may be overridden, depending on how + * the component with {android.Manifest.permission.MANAGE_MEDIA_PROJECTION} + * handles the user's consent: + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}. * @param callback Callback invoked when the virtual display's state changes, or null. * @param handler The {@link android.os.Handler} on which the callback should be invoked, or * null if the callback should be invoked on the calling thread's main * {@link android.os.Looper}. - * @throws IllegalStateException In the following scenarios, if the target SDK is {@link - * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up: + * @throws IllegalStateException If the target SDK is {@link + * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up, and + * if no {@link Callback} is registered. + * @throws SecurityException In any of the following scenarios: * <ol> - * <li>If no {@link Callback} is registered.</li> - * <li>If {@link MediaProjectionManager#getMediaProjection} + * <li>If attempting to create a new virtual display + * associated with this MediaProjection instance after it has + * been stopped by invoking {@link #stop()}. + * <li>If the target SDK is {@link + * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up, + * and if this instance has already taken a recording through + * {@code #createVirtualDisplay}, but {@link #stop()} wasn't + * invoked to end the recording. + * <li>If the target SDK is {@link + * android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up, + * and if {@link MediaProjectionManager#getMediaProjection} * was invoked more than once to get this * {@code MediaProjection} instance. - * <li>If this instance has already taken a recording through - * {@code #createVirtualDisplay}. * </ol> - * However, if the target SDK is less than - * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U}, no - * exception is thrown. In case 1, recording begins even without - * the callback. In case 2 & 3, recording doesn't begin - * until the user re-grants consent in the dialog. - * @throws SecurityException If attempting to create a new virtual display associated with this - * MediaProjection instance after it has been stopped by invoking - * {@link #stop()}. - * + * In cases 2 & 3, no exception is thrown if the target SDK is + * less than + * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U}. + * Instead, recording doesn't begin until the user re-grants + * consent in the dialog. * @see VirtualDisplay * @see VirtualDisplay.Callback */ diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java index bbca8823dde4..ac8a7f37c527 100644 --- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java @@ -24,7 +24,11 @@ import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_PLAYERS; import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_AUDIO_SESSION_ID; import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_UID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + import android.media.AudioFormat; +import android.media.AudioSystem; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMixingRule; import android.media.audiopolicy.AudioPolicyConfig; @@ -145,6 +149,84 @@ public class AudioMixUnitTests { equalsTester.testEquals(); } + @Test + public void buildRenderToRemoteSubmix_success() { + final String deviceAddress = "address"; + final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() + .setTargetMixRole(MIX_ROLE_PLAYERS) + .addMixRule(RULE_MATCH_UID, 42).build()) + .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) + .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER) + .setDevice(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, /*address=*/deviceAddress).build(); + + assertEquals(deviceAddress, audioMix.getRegistration()); + assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); + assertEquals(AudioMix.ROUTE_FLAG_RENDER, audioMix.getRouteFlags()); + } + + @Test + public void buildLoopbackAndRenderToRemoteSubmix_success() { + final String deviceAddress = "address"; + final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() + .setTargetMixRole(MIX_ROLE_PLAYERS) + .addMixRule(RULE_MATCH_UID, 42).build()) + .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) + .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK_RENDER) + .setDevice(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, /*address=*/deviceAddress).build(); + + assertEquals(deviceAddress, audioMix.getRegistration()); + assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); + assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK_RENDER, audioMix.getRouteFlags()); + } + + @Test + public void buildRenderToSpeaker_success() { + final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() + .setTargetMixRole(MIX_ROLE_PLAYERS) + .addMixRule(RULE_MATCH_UID, 42).build()) + .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) + .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER) + .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build(); + + assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); + assertEquals(AudioMix.ROUTE_FLAG_RENDER, audioMix.getRouteFlags()); + } + + @Test + public void buildLoopbackForPlayerMix_success() { + final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder() + .setTargetMixRole(MIX_ROLE_PLAYERS) + .addMixRule(RULE_MATCH_UID, 42).build()) + .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) + .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build(); + + assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat()); + assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK, audioMix.getRouteFlags()); + } + + @Test + public void buildLoopbackWithDevice_throws() { + assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder( + new AudioMixingRule.Builder() + .setTargetMixRole(MIX_ROLE_PLAYERS) + .addMixRule(RULE_MATCH_UID, 42).build()) + .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) + .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK) + .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build()); + } + + @Test + public void buildRenderWithoutDevice_throws() { + assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder( + new AudioMixingRule.Builder() + .setTargetMixRole(MIX_ROLE_PLAYERS) + .addMixRule(RULE_MATCH_UID, 42).build()) + .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM) + .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER).build()); + } + + + private static AudioMix writeToAndFromParcel(AudioMix audioMix) { AudioPolicyConfig apc = new AudioPolicyConfig(new ArrayList<>(List.of(audioMix))); Parcel parcel = Parcel.obtain(); diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp index e88410c97415..8699f59e4fba 100644 --- a/packages/EasterEgg/Android.bp +++ b/packages/EasterEgg/Android.bp @@ -26,7 +26,10 @@ package { android_app { // the build system in pi-dev can't quite handle R.java in kt // so we will have a mix of java and kotlin files - srcs: ["src/**/*.java", "src/**/*.kt"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], resource_dirs: ["res"], @@ -36,17 +39,34 @@ android_app { certificate: "platform", optimize: { + enabled: true, + optimize: true, + shrink: true, + shrink_resources: true, + proguard_compatibility: false, proguard_flags_files: ["proguard.flags"], }, - static_libs: [ - "androidx.core_core", - "androidx.recyclerview_recyclerview", + static_libs: [ + "androidx.core_core", "androidx.annotation_annotation", - "kotlinx-coroutines-android", - "kotlinx-coroutines-core", - //"kotlinx-coroutines-reactive", - ], + "androidx.recyclerview_recyclerview", + "kotlinx-coroutines-android", + "kotlinx-coroutines-core", + + "androidx.core_core-ktx", + "androidx.lifecycle_lifecycle-runtime-ktx", + "androidx.activity_activity-compose", + "androidx.compose.ui_ui", + "androidx.compose.ui_ui-util", + "androidx.compose.ui_ui-tooling-preview", + "androidx.compose.material_material", + "androidx.window_window", + + "androidx.compose.runtime_runtime", + "androidx.activity_activity-compose", + "androidx.compose.ui_ui", + ], manifest: "AndroidManifest.xml", diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml index cc7bb4a3ff81..d1db237966d5 100644 --- a/packages/EasterEgg/AndroidManifest.xml +++ b/packages/EasterEgg/AndroidManifest.xml @@ -1,4 +1,19 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?><!-- + 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. +--> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.egg" android:versionCode="12" @@ -18,8 +33,27 @@ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application - android:icon="@drawable/icon" + android:icon="@drawable/android14_patch_adaptive" android:label="@string/app_name"> + + <!-- Android U easter egg --> + + <activity + android:name=".landroid.MainActivity" + android:exported="true" + android:label="@string/u_egg_name" + android:icon="@drawable/android14_patch_adaptive" + android:configChanges="orientation|screenLayout|screenSize|density" + android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="com.android.internal.category.PLATLOGO" /> + </intent-filter> + </activity> + + + <!-- Android Q easter egg --> <activity android:name=".quares.QuaresActivity" android:exported="true" @@ -69,7 +103,7 @@ android:exported="true" android:showOnLockScreen="true" android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar" /> - <!-- Used to enable easter egg --> + <!-- Used to enable easter egg components for earlier easter eggs. --> <activity android:name=".ComponentActivationActivity" android:excludeFromRecents="true" @@ -79,7 +113,6 @@ <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> - <category android:name="com.android.internal.category.PLATLOGO" /> </intent-filter> </activity> diff --git a/packages/EasterEgg/res/drawable/android14_patch_adaptive.xml b/packages/EasterEgg/res/drawable/android14_patch_adaptive.xml new file mode 100644 index 000000000000..423e35146c24 --- /dev/null +++ b/packages/EasterEgg/res/drawable/android14_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android14_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android14_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android14_patch_monochrome"/> +</adaptive-icon>
\ No newline at end of file diff --git a/packages/EasterEgg/res/drawable/android14_patch_adaptive_background.xml b/packages/EasterEgg/res/drawable/android14_patch_adaptive_background.xml new file mode 100644 index 000000000000..c31aa7bcfac1 --- /dev/null +++ b/packages/EasterEgg/res/drawable/android14_patch_adaptive_background.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:pathData="M0,0 L108,0 L108,108 L0,108 z" + android:fillColor="#FF073042"/> + <path + android:pathData="M44.51,43.32L44.86,42.27C47.04,54.48 52.81,86.71 52.81,50.14C52.81,49.99 52.92,49.86 53.06,49.86H55.04C55.18,49.86 55.3,49.98 55.3,50.14C55.27,114.18 44.51,43.32 44.51,43.32Z" + android:fillColor="#3DDC84"/> + <path + android:name="planetary head" + android:pathData="M38.81,42.23L33.63,51.21C33.33,51.72 33.51,52.38 34.02,52.68C34.54,52.98 35.2,52.8 35.49,52.28L40.74,43.2C49.22,47 58.92,47 67.4,43.2L72.65,52.28C72.96,52.79 73.62,52.96 74.13,52.65C74.62,52.35 74.79,51.71 74.51,51.21L69.33,42.23C78.23,37.39 84.32,28.38 85.21,17.74H22.93C23.82,28.38 29.91,37.39 38.81,42.23Z" + android:fillColor="#ffffff"/> + <!-- yes it's an easter egg in a vector drawable --> + <path + android:name="planetary body" + android:pathData="M22.9,0 L85.1,0 L85.1,15.5 L22.9,15.5 z" + android:fillColor="#ffffff" /> + <path + android:pathData="M54.96,43.32H53.1C52.92,43.32 52.77,43.47 52.77,43.65V48.04C52.77,48.22 52.92,48.37 53.1,48.37H54.96C55.15,48.37 55.3,48.22 55.3,48.04V43.65C55.3,43.47 55.15,43.32 54.96,43.32Z" + android:fillColor="#3DDC84"/> + <path + android:pathData="M54.99,40.61H53.08C52.91,40.61 52.77,40.75 52.77,40.92V41.56C52.77,41.73 52.91,41.87 53.08,41.87H54.99C55.16,41.87 55.3,41.73 55.3,41.56V40.92C55.3,40.75 55.16,40.61 54.99,40.61Z" + android:fillColor="#3DDC84"/> + <path + android:pathData="M41.49,47.88H40.86V48.51H41.49V47.88Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M44.13,57.08H43.5V57.71H44.13V57.08Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72.29,66.76H71.66V67.39H72.29V66.76Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M59.31,53.41H58.68V54.04H59.31V53.41Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M64.47,48.19H63.84V48.83H64.47V48.19Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M60.58,59.09H59.95V59.72H60.58V59.09Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M66.95,56.7H65.69V57.97H66.95V56.7Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M44.13,60.71H43.5V61.34H44.13V60.71Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M49.66,51.33H48.4V52.6H49.66V51.33Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M57.78,63.83H56.52V65.09H57.78V63.83Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M61.1,68.57H59.83V69.83H61.1V68.57Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M40.43,53.73H39.16V54.99H40.43V53.73Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M74.47,44H73.21V45.26H74.47V44Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M36.8,64.58H35.54V65.84H36.8V64.58Z" + android:fillColor="#ffffff"/> +</vector> diff --git a/packages/EasterEgg/res/drawable/android14_patch_adaptive_foreground.xml b/packages/EasterEgg/res/drawable/android14_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..391d5158e522 --- /dev/null +++ b/packages/EasterEgg/res/drawable/android14_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:pathData="M54.03,33.03C52.99,33.03 52.14,33.86 52.14,34.87V37.14C52.14,37.34 52.3,37.5 52.5,37.5C52.69,37.5 52.85,37.34 52.85,37.14V36.53C52.85,36.14 53.17,35.82 53.56,35.82H54.51C54.9,35.82 55.22,36.14 55.22,36.53V37.14C55.22,37.34 55.38,37.5 55.57,37.5C55.77,37.5 55.93,37.34 55.93,37.14V34.87C55.93,33.86 55.08,33.03 54.03,33.03H54.03Z" + android:fillColor="#3DDC84"/> + <path + android:pathData="M108,0H0V108H108V0ZM54,80.67C68.73,80.67 80.67,68.73 80.67,54C80.67,39.27 68.73,27.33 54,27.33C39.27,27.33 27.33,39.27 27.33,54C27.33,68.73 39.27,80.67 54,80.67Z" + android:fillColor="#F86734" + android:fillType="evenOdd"/> + <group> + <!-- the text doesn't look great everywhere but you can remove the clip to try it out. --> + <clip-path /> + <path + android:pathData="M28.58,32.18L29.18,31.5L33.82,33.02L33.12,33.81L32.15,33.48L30.92,34.87L31.37,35.8L30.68,36.58L28.58,32.18L28.58,32.18ZM31.25,33.18L29.87,32.71L30.51,34.02L31.25,33.18V33.18Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M38,29.76L34.61,28.79L36.23,31.04L35.42,31.62L32.8,27.99L33.5,27.48L36.88,28.45L35.26,26.21L36.08,25.62L38.7,29.25L38,29.76Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39.23,23.87L40.63,23.27C41.79,22.77 43.13,23.28 43.62,24.43C44.11,25.57 43.56,26.89 42.4,27.39L40.99,27.99L39.23,23.87ZM42.03,26.54C42.73,26.24 42.96,25.49 42.68,24.83C42.4,24.17 41.69,23.82 41,24.11L40.51,24.32L41.55,26.75L42.03,26.54Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M45.91,21.43L47.64,21.09C48.47,20.93 49.12,21.41 49.27,22.15C49.38,22.72 49.15,23.14 48.63,23.45L50.57,25.08L49.39,25.31L47.57,23.79L47.41,23.82L47.76,25.63L46.78,25.83L45.91,21.43H45.91ZM47.87,22.86C48.16,22.8 48.34,22.59 48.29,22.34C48.24,22.07 48,21.96 47.71,22.02L47.07,22.14L47.24,22.98L47.87,22.86Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M52.17,22.69C52.19,21.41 53.24,20.39 54.52,20.41C55.8,20.43 56.82,21.49 56.8,22.76C56.78,24.04 55.72,25.06 54.45,25.04C53.17,25.02 52.15,23.96 52.17,22.69ZM55.79,22.75C55.8,22.02 55.23,21.39 54.51,21.38C53.78,21.37 53.19,21.98 53.18,22.7C53.17,23.43 53.73,24.06 54.47,24.07C55.19,24.08 55.78,23.47 55.79,22.75H55.79Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M60,21.01L60.98,21.2L60.12,25.6L59.14,25.41L60,21.01Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M64.3,22.03L65.73,22.58C66.91,23.03 67.51,24.32 67.07,25.49C66.62,26.65 65.31,27.22 64.13,26.77L62.71,26.22L64.3,22.03L64.3,22.03ZM64.46,25.9C65.17,26.17 65.86,25.8 66.12,25.12C66.37,24.45 66.11,23.71 65.4,23.44L64.91,23.25L63.97,25.72L64.46,25.9Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M73.59,27.94L72.94,27.44L73.51,26.69L74.92,27.77L72.2,31.34L71.45,30.76L73.59,27.94Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76.18,33.75L74.69,32.14L75.25,31.62L78.81,31.42L79.4,32.05L77.47,33.85L77.86,34.27L77.22,34.86L76.83,34.44L76.12,35.11L75.47,34.41L76.18,33.75ZM77.72,32.31L76.12,32.4L76.82,33.15L77.72,32.31Z" + android:fillColor="#ffffff"/> + </group> +</vector> diff --git a/packages/EasterEgg/res/drawable/android14_patch_monochrome.xml b/packages/EasterEgg/res/drawable/android14_patch_monochrome.xml new file mode 100644 index 000000000000..beef85ce3b3f --- /dev/null +++ b/packages/EasterEgg/res/drawable/android14_patch_monochrome.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <clip-path + android:pathData="M0,0h108v108h-108z"/> + <group> + <clip-path + android:pathData="M22,22h64v64h-64z"/> + <path + android:pathData="M54,78C67.25,78 78,67.25 78,54C78,40.75 67.25,30 54,30C40.75,30 30,40.75 30,54C30,67.25 40.75,78 54,78Z" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000"/> + <group> + <clip-path + android:pathData="M77.5,54C77.5,66.98 66.98,77.5 54,77.5C41.02,77.5 30.5,66.98 30.5,54C30.5,41.02 41.02,30.5 54,30.5C66.98,30.5 77.5,41.02 77.5,54Z"/> + <path + android:pathData="M61.5,46.06C56.7,47.89 51.4,47.89 46.61,46.06L44.04,50.51C43.49,51.46 42.28,51.79 41.33,51.24C40.39,50.69 40.06,49.48 40.61,48.53L43.06,44.28C37.97,41.03 34.54,35.56 34,29.19L33.88,27.74H74.22L74.1,29.19C73.57,35.56 70.14,41.03 65.04,44.28L67.51,48.56C68.03,49.49 67.71,50.66 66.8,51.21C65.87,51.77 64.65,51.47 64.08,50.54L64.07,50.51L61.5,46.06Z" + android:fillColor="#000000"/> + </group> + <path + android:pathData="M51.33,67.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M48.67,62h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M56.67,70h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M56.67,62h2.67v2.67h-2.67z" + android:fillColor="#000000"/> + <path + android:pathData="M67.33,62h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M59.33,51.33h2.67v2.67h-2.67z" + android:fillColor="#000000"/> + <path + android:pathData="M62,59.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M70,54h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M35.33,56.67h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M35.33,48.67h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M40.67,59.33h2.67v2.67h-2.67z" + android:fillColor="#000000"/> + <path + android:pathData="M46,51.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M43.33,67.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M54,54h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + </group> + </group> +</vector> diff --git a/packages/EasterEgg/res/values/landroid_strings.xml b/packages/EasterEgg/res/values/landroid_strings.xml new file mode 100644 index 000000000000..1394f2f55868 --- /dev/null +++ b/packages/EasterEgg/res/values/landroid_strings.xml @@ -0,0 +1,371 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + 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. +--> + +<resources> + <string name="u_egg_name" translatable="false">Android 14 Easter Egg</string> + + <string-array name="planet_descriptors" translatable="false"> + <item>earthy</item> + <item>swamp</item> + <item>frozen</item> + <item>grassy</item> + <item>arid</item> + <item>crowded</item> + <item>ancient</item> + <item>lively</item> + <item>homey</item> + <item>modern</item> + <item>boring</item> + <item>compact</item> + <item>expensive</item> + <item>polluted</item> + <item>rusty</item> + <item>sandy</item> + <item>undulating</item> + <item>verdant</item> + <item>tessellated</item> + <item>hollow</item> + <item>scalding</item> + <item>hemispherical</item> + <item>oblong</item> + <item>oblate</item> + <item>vacuum</item> + <item>high-pressure</item> + <item>low-pressure</item> + <item>plastic</item> + <item>metallic</item> + <item>burned-out</item> + <item>bucolic</item> + </string-array> + + <string-array name="life_descriptors" translatable="false"> + <item>aggressive</item> + <item>passive-aggressive</item> + <item>shy</item> + <item>timid</item> + <item>nasty</item> + <item>brutish</item> + <item>short</item> + <item>absent</item> + <item>teen-aged</item> + <item>confused</item> + <item>transparent</item> + <item>cubic</item> + <item>quadratic</item> + <item>higher-order</item> + <item>huge</item> + <item>tall</item> + <item>wary</item> + <item>loud</item> + <item>yodeling</item> + <item>purring</item> + <item>slender</item> + <item>cats</item> + <item>adorable</item> + <item>eclectic</item> + <item>electric</item> + <item>microscopic</item> + <item>trunkless</item> + <item>myriad</item> + <item>cantankerous</item> + <item>gargantuan</item> + <item>contagious</item> + <item>fungal</item> + <item>cattywampus</item> + <item>spatchcocked</item> + <item>rotisserie</item> + <item>farm-to-table</item> + <item>organic</item> + <item>synthetic</item> + <item>unfocused</item> + <item>focused</item> + <item>capitalist</item> + <item>communal</item> + <item>bossy</item> + <item>malicious</item> + <item>compliant</item> + <item>psychic</item> + <item>oblivious</item> + <item>passive</item> + <item>bonsai</item> + </string-array> + + <string-array name="any_descriptors" translatable="false"> + <item>silly</item> + <item>dangerous</item> + <item>vast</item> + <item>invisible</item> + <item>superfluous</item> + <item>superconducting</item> + <item>superior</item> + <item>alien</item> + <item>phantom</item> + <item>friendly</item> + <item>peaceful</item> + <item>lonely</item> + <item>uncomfortable</item> + <item>charming</item> + <item>fractal</item> + <item>imaginary</item> + <item>forgotten</item> + <item>tardy</item> + <item>gassy</item> + <item>fungible</item> + <item>bespoke</item> + <item>artisanal</item> + <item>exceptional</item> + <item>puffy</item> + <item>rusty</item> + <item>fresh</item> + <item>crusty</item> + <item>glossy</item> + <item>lovely</item> + <item>processed</item> + <item>macabre</item> + <item>reticulated</item> + <item>shocking</item> + <item>void</item> + <item>undefined</item> + <item>gothic</item> + <item>beige</item> + <item>mid</item> + <item>milquetoast</item> + <item>melancholy</item> + <item>unnerving</item> + <item>cheery</item> + <item>vibrant</item> + <item>heliotrope</item> + <item>psychedelic</item> + <item>nondescript</item> + <item>indescribable</item> + <item>tubular</item> + <item>toroidal</item> + <item>voxellated</item> + <item>low-poly</item> + <item>low-carb</item> + <item>100% cotton</item> + <item>synthetic</item> + <item>boot-cut</item> + <item>bell-bottom</item> + <item>bumpy</item> + <item>fluffy</item> + <item>sous-vide</item> + <item>tepid</item> + <item>upcycled</item> + <item>sous-vide</item> + <item>bedazzled</item> + <item>ancient</item> + <item>inexplicable</item> + <item>sparkling</item> + <item>still</item> + <item>lemon-scented</item> + <item>eccentric</item> + <item>tilted</item> + <item>pungent</item> + <item>pine-scented</item> + <item>corduroy</item> + <item>overengineered</item> + <item>bioengineered</item> + <item>impossible</item> + </string-array> + + <string-array name="constellations" translatable="false"> + <item>Aries</item> + <item>Taurus</item> + <item>Gemini</item> + <item>Cancer</item> + <item>Leo</item> + <item>Virgo</item> + <item>Libra</item> + <item>Scorpio</item> + <item>Sagittarius</item> + <item>Capricorn</item> + <item>Aquarius</item> + <item>Pisces</item> + <item>Andromeda</item> + <item>Cygnus</item> + <item>Draco</item> + <item>Alcor</item> + <item>Calamari</item> + <item>Cuckoo</item> + <item>Neko</item> + <item>Monoceros</item> + <item>Norma</item> + <item>Abnorma</item> + <item>Morel</item> + <item>Redlands</item> + <item>Cupcake</item> + <item>Donut</item> + <item>Eclair</item> + <item>Froyo</item> + <item>Gingerbread</item> + <item>Honeycomb</item> + <item>Icecreamsandwich</item> + <item>Jellybean</item> + <item>Kitkat</item> + <item>Lollipop</item> + <item>Marshmallow</item> + <item>Nougat</item> + <item>Oreo</item> + <item>Pie</item> + <item>Quincetart</item> + <item>Redvelvetcake</item> + <item>Snowcone</item> + <item>Tiramisu</item> + <item>Upsidedowncake</item> + <item>Vanillaicecream</item> + <item>Android</item> + <item>Binder</item> + <item>Campanile</item> + <item>Dread</item> + </string-array> + + <!-- prob: 5% --> + <string-array name="constellations_rare" translatable="false"> + <item>Jandycane</item> + <item>Zombiegingerbread</item> + <item>Astro</item> + <item>Bender</item> + <item>Flan</item> + <item>Untitled-1</item> + <item>Expedit</item> + <item>Petit Four</item> + <item>Worcester</item> + <item>Xylophone</item> + <item>Yellowpeep</item> + <item>Zebraball</item> + <item>Hutton</item> + <item>Klang</item> + <item>Frogblast</item> + <item>Exo</item> + <item>Keylimepie</item> + <item>Nat</item> + <item>Nrp</item> + </string-array> + + <!-- prob: 75% --> + <string-array name="star_suffixes" translatable="false"> + <item>Alpha</item> + <item>Beta</item> + <item>Gamma</item> + <item>Delta</item> + <item>Epsilon</item> + <item>Zeta</item> + <item>Eta</item> + <item>Theta</item> + <item>Iota</item> + <item>Kappa</item> + <item>Lambda</item> + <item>Mu</item> + <item>Nu</item> + <item>Xi</item> + <item>Omicron</item> + <item>Pi</item> + <item>Rho</item> + <item>Sigma</item> + <item>Tau</item> + <item>Upsilon</item> + <item>Phi</item> + <item>Chi</item> + <item>Psi</item> + <item>Omega</item> + + <item>Prime</item> + <item>Secundo</item> + <item>Major</item> + <item>Minor</item> + <item>Diminished</item> + <item>Augmented</item> + <item>Ultima</item> + <item>Penultima</item> + <item>Mid</item> + + <item>Proxima</item> + <item>Novis</item> + + <item>Plus</item> + </string-array> + + <!-- prob: 5% --> + <!-- more than one can be appended, with very low prob --> + <string-array name="star_suffixes_rare" translatable="false"> + <item>Serif</item> + <item>Sans</item> + <item>Oblique</item> + <item>Grotesque</item> + <item>Handtooled</item> + <item>III “Trey”</item> + <item>Alfredo</item> + <item>2.0</item> + <item>(Final)</item> + <item>(Final (Final))</item> + <item>(Draft)</item> + <item>Con Carne</item> + </string-array> + + <string-array name="planet_types" translatable="false"> + <item>planet</item> + <item>planetoid</item> + <item>moon</item> + <item>moonlet</item> + <item>centaur</item> + <item>asteroid</item> + <item>space garbage</item> + <item>detritus</item> + <item>satellite</item> + <item>core</item> + <item>giant</item> + <item>body</item> + <item>slab</item> + <item>rock</item> + <item>husk</item> + <item>planemo</item> + <item>object</item> + <item>planetesimal</item> + <item>exoplanet</item> + <item>ploonet</item> + </string-array> + + <string-array name="atmo_descriptors" translatable="false"> + <item>toxic</item> + <item>breathable</item> + <item>radioactive</item> + <item>clear</item> + <item>calm</item> + <item>peaceful</item> + <item>vacuum</item> + <item>stormy</item> + <item>freezing</item> + <item>burning</item> + <item>humid</item> + <item>tropical</item> + <item>cloudy</item> + <item>obscured</item> + <item>damp</item> + <item>dank</item> + <item>clammy</item> + <item>frozen</item> + <item>contaminated</item> + <item>temperate</item> + <item>moist</item> + <item>minty</item> + <item>relaxed</item> + <item>skunky</item> + <item>breezy</item> + <item>soup </item> + </string-array> + +</resources> diff --git a/packages/EasterEgg/res/values/q_puzzles.xml b/packages/EasterEgg/res/values/q_puzzles.xml index 7c2eff152ffe..2a477f5ee18f 100644 --- a/packages/EasterEgg/res/values/q_puzzles.xml +++ b/packages/EasterEgg/res/values/q_puzzles.xml @@ -44,7 +44,6 @@ <item>android:drawable/ic_audio_alarm</item> <item>android:drawable/ic_audio_alarm_mute</item> - <item>android:drawable/ic_bluetooth_share_icon</item> <item>android:drawable/ic_bt_headphones_a2dp</item> <item>android:drawable/ic_bt_headset_hfp</item> <item>android:drawable/ic_bt_hearing_aid</item> diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml index 743947ad281e..79957df04720 100644 --- a/packages/EasterEgg/res/values/strings.xml +++ b/packages/EasterEgg/res/values/strings.xml @@ -14,7 +14,7 @@ Copyright (C) 2018 The Android Open Source Project limitations under the License. --> <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <string name="app_name" translatable="false">Android S Easter Egg</string> + <string name="app_name" translatable="false">Android Easter Egg</string> <!-- name of the Q easter egg, a nonogram-style icon puzzle --> <string name="q_egg_name" translatable="false">Icon Quiz</string> diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Colors.kt b/packages/EasterEgg/src/com/android/egg/landroid/Colors.kt new file mode 100644 index 000000000000..f5657ae6c0c3 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Colors.kt @@ -0,0 +1,29 @@ +/* + * 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.egg.landroid + +import androidx.compose.ui.graphics.Color + +/** Various UI colors. */ +object Colors { + val Eigengrau = Color(0xFF16161D) + val Eigengrau2 = Color(0xFF292936) + val Eigengrau3 = Color(0xFF3C3C4F) + val Eigengrau4 = Color(0xFFA7A7CA) + + val Console = Color(0xFFB7B7FF) +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/ComposeTools.kt b/packages/EasterEgg/src/com/android/egg/landroid/ComposeTools.kt new file mode 100644 index 000000000000..d040fba49fdf --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/ComposeTools.kt @@ -0,0 +1,41 @@ +/* + * 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.egg.landroid + +import androidx.compose.animation.core.CubicBezierEasing +import androidx.compose.animation.core.Easing +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import kotlin.random.Random + +@Composable fun Dp.toLocalPx() = with(LocalDensity.current) { this@toLocalPx.toPx() } + +operator fun Easing.times(next: Easing) = { x: Float -> next.transform(transform(x)) } + +fun flickerFadeEasing(rng: Random) = Easing { frac -> if (rng.nextFloat() < frac) 1f else 0f } + +val flickerFadeIn = + fadeIn( + animationSpec = + tween( + durationMillis = 1000, + easing = CubicBezierEasing(0f, 1f, 1f, 0f) * flickerFadeEasing(Random) + ) + ) diff --git a/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt new file mode 100644 index 000000000000..5a9b8141bb40 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt @@ -0,0 +1,543 @@ +/* + * 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.egg.landroid + +import android.content.res.Resources +import android.os.Bundle +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.CubicBezierEasing +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.withInfiniteAnimationFrameNanos +import androidx.compose.animation.fadeIn +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.border +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.forEachGesture +import androidx.compose.foundation.gestures.rememberTransformableState +import androidx.compose.foundation.gestures.transformable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.AbsoluteAlignment.Left +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.input.pointer.PointerEvent +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.math.MathUtils.clamp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.window.layout.FoldingFeature +import androidx.window.layout.WindowInfoTracker +import java.lang.Float.max +import java.lang.Float.min +import java.util.Calendar +import java.util.GregorianCalendar +import kotlin.math.absoluteValue +import kotlin.math.floor +import kotlin.math.sqrt +import kotlin.random.Random +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +enum class RandomSeedType { + Fixed, + Daily, + Evergreen +} + +const val TEST_UNIVERSE = false + +val RANDOM_SEED_TYPE = RandomSeedType.Daily + +const val FIXED_RANDOM_SEED = 5038L +const val DEFAULT_CAMERA_ZOOM = 0.25f +const val MIN_CAMERA_ZOOM = 250f / UNIVERSE_RANGE // 0.0025f +const val MAX_CAMERA_ZOOM = 5f +const val TOUCH_CAMERA_PAN = false +const val TOUCH_CAMERA_ZOOM = true +const val DYNAMIC_ZOOM = false // @@@ FIXME + +fun dailySeed(): Long { + val today = GregorianCalendar() + return today.get(Calendar.YEAR) * 10_000L + + today.get(Calendar.MONTH) * 100L + + today.get(Calendar.DAY_OF_MONTH) +} + +fun randomSeed(): Long { + return when (RANDOM_SEED_TYPE) { + RandomSeedType.Fixed -> FIXED_RANDOM_SEED + RandomSeedType.Daily -> dailySeed() + else -> Random.Default.nextLong().mod(10_000_000).toLong() + }.absoluteValue +} + +val DEBUG_TEXT = mutableStateOf("Hello Universe") +const val SHOW_DEBUG_TEXT = false + +@Composable +fun DebugText(text: MutableState<String>) { + if (SHOW_DEBUG_TEXT) { + Text( + modifier = Modifier.fillMaxWidth().border(0.5.dp, color = Color.Yellow).padding(2.dp), + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + fontSize = 9.sp, + color = Color.Yellow, + text = text.value + ) + } +} + +@Composable +fun ColumnScope.ConsoleText( + modifier: Modifier = Modifier, + visible: Boolean = true, + random: Random = Random.Default, + text: String +) { + AnimatedVisibility( + modifier = modifier, + visible = visible, + enter = + fadeIn( + animationSpec = + tween( + durationMillis = 1000, + easing = flickerFadeEasing(random) * CubicBezierEasing(0f, 1f, 1f, 0f) + ) + ) + ) { + Text( + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + color = Color(0xFFFF8000), + text = text + ) + } +} + +@Composable +fun Telemetry(universe: VisibleUniverse) { + var topVisible by remember { mutableStateOf(false) } + var bottomVisible by remember { mutableStateOf(false) } + + LaunchedEffect("blah") { + delay(1000) + bottomVisible = true + delay(1000) + topVisible = true + } + + Column(modifier = Modifier.fillMaxSize().padding(6.dp)) { + universe.triggerDraw.value // recompose on every frame + val explored = universe.planets.filter { it.explored } + + AnimatedVisibility(modifier = Modifier, visible = topVisible, enter = flickerFadeIn) { + Text( + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + color = Colors.Console, + modifier = Modifier.align(Left), + text = + with(universe.star) { + " STAR: $name (UDC-${universe.randomSeed % 100_000})\n" + + " CLASS: ${cls.name}\n" + + "RADIUS: ${radius.toInt()}\n" + + " MASS: %.3g\n".format(mass) + + "BODIES: ${explored.size} / ${universe.planets.size}\n" + + "\n" + } + + explored + .map { + " BODY: ${it.name}\n" + + " TYPE: ${it.description.capitalize()}\n" + + " ATMO: ${it.atmosphere.capitalize()}\n" + + " FAUNA: ${it.fauna.capitalize()}\n" + + " FLORA: ${it.flora.capitalize()}\n" + } + .joinToString("\n") + + // TODO: different colors, highlight latest discovery + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + AnimatedVisibility(modifier = Modifier, visible = bottomVisible, enter = flickerFadeIn) { + Text( + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + color = Colors.Console, + modifier = Modifier.align(Left), + text = + with(universe.ship) { + val closest = universe.closestPlanet() + val distToClosest = (closest.pos - pos).mag().toInt() + listOfNotNull( + landing?.let { "LND: ${it.planet.name}" } + ?: if (distToClosest < 10_000) { + "ALT: $distToClosest" + } else null, + if (thrust != Vec2.Zero) "THR: %.0f%%".format(thrust.mag() * 100f) + else null, + "POS: %s".format(pos.str("%+7.0f")), + "VEL: %.0f".format(velocity.mag()) + ) + .joinToString("\n") + } + ) + } + } +} + +class MainActivity : ComponentActivity() { + private var foldState = mutableStateOf<FoldingFeature?>(null) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + onWindowLayoutInfoChange() + + val universe = VisibleUniverse(namer = Namer(resources), randomSeed = randomSeed()) + + if (TEST_UNIVERSE) { + universe.initTest() + } else { + universe.initRandom() + } + + setContent { + Spaaaace(modifier = Modifier.fillMaxSize(), u = universe, foldState = foldState) + DebugText(DEBUG_TEXT) + + val minRadius = 50.dp.toLocalPx() + val maxRadius = 100.dp.toLocalPx() + FlightStick( + modifier = Modifier.fillMaxSize(), + minRadius = minRadius, + maxRadius = maxRadius, + color = Color.Green + ) { vec -> + (universe.follow as? Spacecraft)?.let { ship -> + if (vec == Vec2.Zero) { + ship.thrust = Vec2.Zero + } else { + val a = vec.angle() + ship.angle = a + + val m = vec.mag() + if (m < minRadius) { + // within this radius, just reorient + ship.thrust = Vec2.Zero + } else { + ship.thrust = + Vec2.makeWithAngleMag( + a, + lexp(minRadius, maxRadius, m).coerceIn(0f, 1f) + ) + } + } + } + } + Telemetry(universe) + } + } + + private fun onWindowLayoutInfoChange() { + val windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity) + + lifecycleScope.launch(Dispatchers.Main) { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + windowInfoTracker.windowLayoutInfo(this@MainActivity).collect { layoutInfo -> + foldState.value = + layoutInfo.displayFeatures.filterIsInstance<FoldingFeature>().firstOrNull() + Log.v("Landroid", "fold updated: $foldState") + } + } + } + } +} + +@Preview(name = "phone", device = Devices.PHONE) +@Preview(name = "fold", device = Devices.FOLDABLE) +@Preview(name = "tablet", device = Devices.TABLET) +@Composable +fun MainActivityPreview() { + val universe = VisibleUniverse(namer = Namer(Resources.getSystem()), randomSeed = randomSeed()) + + universe.initTest() + + Spaaaace(modifier = Modifier.fillMaxSize(), universe) + DebugText(DEBUG_TEXT) + Telemetry(universe) +} + +@Composable +fun FlightStick( + modifier: Modifier, + minRadius: Float = 0f, + maxRadius: Float = 1000f, + color: Color = Color.Green, + onStickChanged: (vector: Vec2) -> Unit +) { + val origin = remember { mutableStateOf(Vec2.Zero) } + val target = remember { mutableStateOf(Vec2.Zero) } + + Box( + modifier = + modifier + .pointerInput(Unit) { + forEachGesture { + awaitPointerEventScope { + // ACTION_DOWN + val down = awaitFirstDown(requireUnconsumed = false) + origin.value = down.position + target.value = down.position + + do { + // ACTION_MOVE + val event: PointerEvent = awaitPointerEvent() + target.value = event.changes[0].position + + onStickChanged(target.value - origin.value) + } while ( + !event.changes.any { it.isConsumed } && + event.changes.count { it.pressed } == 1 + ) + + // ACTION_UP / CANCEL + target.value = Vec2.Zero + origin.value = Vec2.Zero + + onStickChanged(Vec2.Zero) + } + } + } + .drawBehind { + if (origin.value != Vec2.Zero) { + val delta = target.value - origin.value + val mag = min(maxRadius, delta.mag()) + val r = max(minRadius, mag) + val a = delta.angle() + drawCircle( + color = color, + center = origin.value, + radius = r, + style = + Stroke( + width = 2f, + pathEffect = + if (mag < minRadius) + PathEffect.dashPathEffect( + floatArrayOf(this.density * 1f, this.density * 2f) + ) + else null + ) + ) + drawLine( + color = color, + start = origin.value, + end = origin.value + Vec2.makeWithAngleMag(a, mag), + strokeWidth = 2f + ) + } + } + ) +} + +@Composable +fun Spaaaace( + modifier: Modifier, + u: VisibleUniverse, + foldState: MutableState<FoldingFeature?> = mutableStateOf(null) +) { + LaunchedEffect(u) { + while (true) withInfiniteAnimationFrameNanos { frameTimeNanos -> + u.simulateAndDrawFrame(frameTimeNanos) + } + } + + var cameraZoom by remember { mutableStateOf(1f) } + var cameraOffset by remember { mutableStateOf(Offset.Zero) } + + val transformableState = + rememberTransformableState { zoomChange, offsetChange, rotationChange -> + if (TOUCH_CAMERA_PAN) cameraOffset += offsetChange / cameraZoom + if (TOUCH_CAMERA_ZOOM) + cameraZoom = clamp(cameraZoom * zoomChange, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM) + } + + var canvasModifier = modifier + + if (TOUCH_CAMERA_PAN || TOUCH_CAMERA_ZOOM) { + canvasModifier = canvasModifier.transformable(transformableState) + } + + val halfFolded = foldState.value?.let { it.state == FoldingFeature.State.HALF_OPENED } ?: false + val horizontalFold = + foldState.value?.let { it.orientation == FoldingFeature.Orientation.HORIZONTAL } ?: false + + val centerFracX: Float by + animateFloatAsState(if (halfFolded && !horizontalFold) 0.25f else 0.5f, label = "centerX") + val centerFracY: Float by + animateFloatAsState(if (halfFolded && horizontalFold) 0.25f else 0.5f, label = "centerY") + + Canvas(modifier = canvasModifier) { + drawRect(Colors.Eigengrau, Offset.Zero, size) + + val closest = u.closestPlanet() + val distToNearestSurf = max(0f, (u.ship.pos - closest.pos).mag() - closest.radius * 1.2f) + // val normalizedDist = clamp(distToNearestSurf, 50f, 50_000f) / 50_000f + if (DYNAMIC_ZOOM) { + // cameraZoom = lerp(0.1f, 5f, smooth(1f-normalizedDist)) + cameraZoom = clamp(500f / distToNearestSurf, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM) + } else if (!TOUCH_CAMERA_ZOOM) cameraZoom = DEFAULT_CAMERA_ZOOM + if (!TOUCH_CAMERA_PAN) cameraOffset = (u.follow?.pos ?: Vec2.Zero) * -1f + + // cameraZoom: metersToPixels + // visibleSpaceSizeMeters: meters + // cameraOffset: meters ≈ vector pointing from ship to (0,0) (e.g. -pos) + val visibleSpaceSizeMeters = size / cameraZoom // meters x meters + val visibleSpaceRectMeters = + Rect( + -cameraOffset - + Offset( + visibleSpaceSizeMeters.width * centerFracX, + visibleSpaceSizeMeters.height * centerFracY + ), + visibleSpaceSizeMeters + ) + + var gridStep = 1000f + while (gridStep * cameraZoom < 32.dp.toPx()) gridStep *= 10 + + DEBUG_TEXT.value = + ("SIMULATION //\n" + + // "normalizedDist=${normalizedDist} \n" + + "entities: ${u.entities.size} // " + + "zoom: ${"%.4f".format(cameraZoom)}x // " + + "fps: ${"%3.0f".format(1f / u.dt)} " + + "dt: ${u.dt}\n" + + ((u.follow as? Spacecraft)?.let { + "ship: p=%s v=%7.2f a=%6.3f t=%s\n".format( + it.pos.str("%+7.1f"), + it.velocity.mag(), + it.angle, + it.thrust.str("%+5.2f") + ) + } + ?: "") + + "star: '${u.star.name}' designation=UDC-${u.randomSeed % 100_000} " + + "class=${u.star.cls.name} r=${u.star.radius.toInt()} m=${u.star.mass}\n" + + "planets: ${u.planets.size}\n" + + u.planets.joinToString("\n") { + val range = (u.ship.pos - it.pos).mag() + val vorbit = sqrt(GRAVITATION * it.mass / range) + val vescape = sqrt(2 * GRAVITATION * it.mass / it.radius) + " * ${it.name}:\n" + + if (it.explored) { + " TYPE: ${it.description.capitalize()}\n" + + " ATMO: ${it.atmosphere.capitalize()}\n" + + " FAUNA: ${it.fauna.capitalize()}\n" + + " FLORA: ${it.flora.capitalize()}\n" + } else { + " (Unexplored)\n" + } + + " orbit=${(it.pos - it.orbitCenter).mag().toInt()}" + + " radius=${it.radius.toInt()}" + + " mass=${"%g".format(it.mass)}" + + " vel=${(it.speed).toInt()}" + + " // range=${"%.0f".format(range)}" + + " vorbit=${vorbit.toInt()} vescape=${vescape.toInt()}" + }) + + zoom(cameraZoom) { + // All coordinates are space coordinates now. + + translate( + -visibleSpaceRectMeters.center.x + size.width * 0.5f, + -visibleSpaceRectMeters.center.y + size.height * 0.5f + ) { + // debug outer frame + // drawRect( + // Colors.Eigengrau2, + // visibleSpaceRectMeters.topLeft, + // visibleSpaceRectMeters.size, + // style = Stroke(width = 10f / cameraZoom) + // ) + + var x = floor(visibleSpaceRectMeters.left / gridStep) * gridStep + while (x < visibleSpaceRectMeters.right) { + drawLine( + color = Colors.Eigengrau2, + start = Offset(x, visibleSpaceRectMeters.top), + end = Offset(x, visibleSpaceRectMeters.bottom), + strokeWidth = (if ((x % (gridStep * 10) == 0f)) 3f else 1.5f) / cameraZoom + ) + x += gridStep + } + + var y = floor(visibleSpaceRectMeters.top / gridStep) * gridStep + while (y < visibleSpaceRectMeters.bottom) { + drawLine( + color = Colors.Eigengrau2, + start = Offset(visibleSpaceRectMeters.left, y), + end = Offset(visibleSpaceRectMeters.right, y), + strokeWidth = (if ((y % (gridStep * 10) == 0f)) 3f else 1.5f) / cameraZoom + ) + y += gridStep + } + + this@zoom.drawUniverse(u) + } + } + } +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Maths.kt b/packages/EasterEgg/src/com/android/egg/landroid/Maths.kt new file mode 100644 index 000000000000..fdf29f7aa948 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Maths.kt @@ -0,0 +1,34 @@ +/* + * 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.egg.landroid + +import kotlin.math.pow + +/** smoothstep. Ken Perlin's version */ +fun smooth(x: Float): Float { + return x * x * x * (x * (x * 6 - 15) + 10) +} + +/** Kind of like an inverted smoothstep, but */ +fun invsmoothish(x: Float): Float { + return 0.25f * ((2f * x - 1f).pow(5f) + 1f) + 0.5f * x +} + +/** Compute the fraction that progress represents between start and end (inverse of lerp). */ +fun lexp(start: Float, end: Float, progress: Float): Float { + return (progress - start) / (end - start) +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Namer.kt b/packages/EasterEgg/src/com/android/egg/landroid/Namer.kt new file mode 100644 index 000000000000..67d536e0aea1 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Namer.kt @@ -0,0 +1,96 @@ +/* + * 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.egg.landroid + +import android.content.res.Resources +import kotlin.random.Random + +import com.android.egg.R + +const val SUFFIX_PROB = 0.75f +const val LETTER_PROB = 0.3f +const val NUMBER_PROB = 0.3f +const val RARE_PROB = 0.05f + +class Namer(resources: Resources) { + private val planetDescriptors = Bag(resources.getStringArray(R.array.planet_descriptors)) + private val lifeDescriptors = Bag(resources.getStringArray(R.array.life_descriptors)) + private val anyDescriptors = Bag(resources.getStringArray(R.array.any_descriptors)) + private val atmoDescriptors = Bag(resources.getStringArray(R.array.atmo_descriptors)) + + private val planetTypes = Bag(resources.getStringArray(R.array.planet_types)) + private val constellations = Bag(resources.getStringArray(R.array.constellations)) + private val constellationsRare = Bag(resources.getStringArray(R.array.constellations_rare)) + private val suffixes = Bag(resources.getStringArray(R.array.star_suffixes)) + private val suffixesRare = Bag(resources.getStringArray(R.array.star_suffixes_rare)) + + private val planetTable = RandomTable(0.75f to planetDescriptors, 0.25f to anyDescriptors) + + private var lifeTable = RandomTable(0.75f to lifeDescriptors, 0.25f to anyDescriptors) + + private var constellationsTable = + RandomTable(RARE_PROB to constellationsRare, 1f - RARE_PROB to constellations) + + private var suffixesTable = RandomTable(RARE_PROB to suffixesRare, 1f - RARE_PROB to suffixes) + + private var atmoTable = RandomTable(0.75f to atmoDescriptors, 0.25f to anyDescriptors) + + private var delimiterTable = + RandomTable( + 15f to " ", + 3f to "-", + 1f to "_", + 1f to "/", + 1f to ".", + 1f to "*", + 1f to "^", + 1f to "#", + 0.1f to "(^*!%@##!!" + ) + + fun describePlanet(rng: Random): String { + return planetTable.roll(rng).pull(rng) + " " + planetTypes.pull(rng) + } + + fun describeLife(rng: Random): String { + return lifeTable.roll(rng).pull(rng) + } + + fun nameSystem(rng: Random): String { + val parts = StringBuilder() + parts.append(constellationsTable.roll(rng).pull(rng)) + if (rng.nextFloat() <= SUFFIX_PROB) { + parts.append(delimiterTable.roll(rng)) + parts.append(suffixesTable.roll(rng).pull(rng)) + if (rng.nextFloat() <= RARE_PROB) parts.append(' ').append(suffixesRare.pull(rng)) + } + if (rng.nextFloat() <= LETTER_PROB) { + parts.append(delimiterTable.roll(rng)) + parts.append('A' + rng.nextInt(0, 26)) + if (rng.nextFloat() <= RARE_PROB) parts.append(delimiterTable.roll(rng)) + } + if (rng.nextFloat() <= NUMBER_PROB) { + parts.append(delimiterTable.roll(rng)) + parts.append(rng.nextInt(2, 5039)) + } + return parts.toString() + } + + fun describeAtmo(rng: Random): String { + return atmoTable.roll(rng).pull(rng) + } +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/PathTools.kt b/packages/EasterEgg/src/com/android/egg/landroid/PathTools.kt new file mode 100644 index 000000000000..851064063d19 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/PathTools.kt @@ -0,0 +1,62 @@ +/* + * 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.egg.landroid + +import android.util.Log +import androidx.compose.ui.graphics.Path +import kotlin.math.cos +import kotlin.math.sin + +fun createPolygon(radius: Float, sides: Int): Path { + return Path().apply { + moveTo(radius, 0f) + val angleStep = PI2f / sides + for (i in 1 until sides) { + lineTo(radius * cos(angleStep * i), radius * sin(angleStep * i)) + } + close() + } +} + +fun createStar(radius1: Float, radius2: Float, points: Int): Path { + return Path().apply { + val angleStep = PI2f / points + moveTo(radius1, 0f) + lineTo(radius2 * cos(angleStep * (0.5f)), radius2 * sin(angleStep * (0.5f))) + for (i in 1 until points) { + lineTo(radius1 * cos(angleStep * i), radius1 * sin(angleStep * i)) + lineTo(radius2 * cos(angleStep * (i + 0.5f)), radius2 * sin(angleStep * (i + 0.5f))) + } + close() + } +} + +fun Path.parseSvgPathData(d: String) { + Regex("([A-Z])([-.,0-9e ]+)").findAll(d.trim()).forEach { + val cmd = it.groups[1]!!.value + val args = + it.groups[2]?.value?.split(Regex("\\s+"))?.map { v -> v.toFloat() } ?: emptyList() + Log.d("Landroid", "cmd = $cmd, args = " + args.joinToString(",")) + when (cmd) { + "M" -> moveTo(args[0], args[1]) + "C" -> cubicTo(args[0], args[1], args[2], args[3], args[4], args[5]) + "L" -> lineTo(args[0], args[1]) + "Z" -> close() + else -> Log.v("Landroid", "unsupported SVG command: $cmd") + } + } +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt new file mode 100644 index 000000000000..fc66ad6bc2ae --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt @@ -0,0 +1,160 @@ +/* + * 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.egg.landroid + +import android.util.ArraySet +import kotlin.random.Random + +// artificially speed up or slow down the simulation +const val TIME_SCALE = 1f + +// if it's been over 1 real second since our last timestep, don't simulate that elapsed time. +// this allows the simulation to "pause" when, for example, the activity pauses +const val MAX_VALID_DT = 1f + +interface Entity { + // Integrate. + // Compute accelerations from forces, add accelerations to velocity, save old position, + // add velocity to position. + fun update(sim: Simulator, dt: Float) + + // Post-integration step, after constraints are satisfied. + fun postUpdate(sim: Simulator, dt: Float) +} + +open class Body(var name: String = "Unknown") : Entity { + var pos = Vec2.Zero + var opos = Vec2.Zero + var velocity = Vec2.Zero + + var mass = 0f + var angle = 0f + var radius = 0f + + var collides = true + + var omega: Float + get() = angle - oangle + set(value) { + oangle = angle - value + } + + var oangle = 0f + + override fun update(sim: Simulator, dt: Float) { + if (dt <= 0) return + + // integrate velocity + val vscaled = velocity * dt + opos = pos + pos += vscaled + + // integrate angular velocity + // val wscaled = omega * timescale + // oangle = angle + // angle = (angle + wscaled) % PI2f + } + + override fun postUpdate(sim: Simulator, dt: Float) { + if (dt <= 0) return + velocity = (pos - opos) / dt + } +} + +interface Constraint { + // Solve constraints. Pick up objects and put them where they are "supposed" to be. + fun solve(sim: Simulator, dt: Float) +} + +open class Container(val radius: Float) : Constraint { + private val list = ArraySet<Body>() + private val softness = 0.0f + + override fun toString(): String { + return "Container($radius)" + } + + fun add(p: Body) { + list.add(p) + } + + fun remove(p: Body) { + list.remove(p) + } + + override fun solve(sim: Simulator, dt: Float) { + for (p in list) { + if ((p.pos.mag() + p.radius) > radius) { + p.pos = + p.pos * (softness) + + Vec2.makeWithAngleMag(p.pos.angle(), radius - p.radius) * (1f - softness) + } + } + } +} + +open class Simulator(val randomSeed: Long) { + private var wallClockNanos: Long = 0L + var now: Float = 0f + var dt: Float = 0f + val rng = Random(randomSeed) + val entities = ArraySet<Entity>(1000) + val constraints = ArraySet<Constraint>(100) + + fun add(e: Entity) = entities.add(e) + fun remove(e: Entity) = entities.remove(e) + fun add(c: Constraint) = constraints.add(c) + fun remove(c: Constraint) = constraints.remove(c) + + open fun updateAll(dt: Float, entities: ArraySet<Entity>) { + entities.forEach { it.update(this, dt) } + } + + open fun solveAll(dt: Float, constraints: ArraySet<Constraint>) { + constraints.forEach { it.solve(this, dt) } + } + + open fun postUpdateAll(dt: Float, entities: ArraySet<Entity>) { + entities.forEach { it.postUpdate(this, dt) } + } + + fun step(nanos: Long) { + val firstFrame = (wallClockNanos == 0L) + + dt = (nanos - wallClockNanos) / 1_000_000_000f * TIME_SCALE + this.wallClockNanos = nanos + + // we start the simulation on the next frame + if (firstFrame || dt > MAX_VALID_DT) return + + // simulation is running; we start accumulating simulation time + this.now += dt + + val localEntities = ArraySet(entities) + val localConstraints = ArraySet(constraints) + + // position-based dynamics approach: + // 1. apply acceleration to velocity, save positions, apply velocity to position + updateAll(dt, localEntities) + + // 2. solve all constraints + solveAll(dt, localConstraints) + + // 3. compute new velocities from updated positions and saved positions + postUpdateAll(dt, localEntities) + } +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Randomness.kt b/packages/EasterEgg/src/com/android/egg/landroid/Randomness.kt new file mode 100644 index 000000000000..ebbb2bd1270c --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Randomness.kt @@ -0,0 +1,69 @@ +/* + * 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.egg.landroid + +import kotlin.random.Random + +/** + * A bag of stones. Each time you pull one out it is not replaced, preventing duplicates. When the + * bag is exhausted, all the stones are replaced and reshuffled. + */ +class Bag<T>(items: Array<T>) { + private val remaining = items.copyOf() + private var next = remaining.size // will cause a shuffle on first pull() + + /** Return the next random item from the bag, without replacing it. */ + fun pull(rng: Random): T { + if (next >= remaining.size) { + remaining.shuffle(rng) + next = 0 + } + return remaining[next++] + } +} + +/** + * A loot table. The weight of each possibility is in the first of the pair; the value to be + * returned in the second. They need not add up to 1f (we will do that for you, free of charge). + */ +class RandomTable<T>(private vararg val pairs: Pair<Float, T>) { + private val total = pairs.map { it.first }.sum() + + /** Select a random value from the weighted table. */ + fun roll(rng: Random): T { + var x = rng.nextFloatInRange(0f, total) + for ((weight, result) in pairs) { + x -= weight + if (x < 0f) return result + } + return pairs.last().second + } +} + +/** Return a random float in the range [from, until). */ +fun Random.nextFloatInRange(from: Float, until: Float): Float = + from + ((until - from) * nextFloat()) + +/** Return a random float in the range [start, end). */ +fun Random.nextFloatInRange(fromUntil: ClosedFloatingPointRange<Float>): Float = + nextFloatInRange(fromUntil.start, fromUntil.endInclusive) +/** Return a random float in the range [first, second). */ +fun Random.nextFloatInRange(fromUntil: Pair<Float, Float>): Float = + nextFloatInRange(fromUntil.first, fromUntil.second) + +/** Choose a random element from an array. */ +fun <T> Random.choose(array: Array<T>) = array[nextInt(array.size)] diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt new file mode 100644 index 000000000000..fec3ab3877ea --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt @@ -0,0 +1,513 @@ +/* + * 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.egg.landroid + +import android.util.ArraySet +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.util.lerp +import kotlin.math.absoluteValue +import kotlin.math.pow +import kotlin.math.sqrt + +const val UNIVERSE_RANGE = 200_000f + +val NUM_PLANETS_RANGE = 1..10 +val STAR_RADIUS_RANGE = (1_000f..8_000f) +val PLANET_RADIUS_RANGE = (50f..2_000f) +val PLANET_ORBIT_RANGE = (STAR_RADIUS_RANGE.endInclusive * 2f)..(UNIVERSE_RANGE * 0.75f) + +const val GRAVITATION = 1e-2f +const val KEPLER_CONSTANT = 50f // * 4f * PIf * PIf / GRAVITATION + +// m = d * r +const val PLANETARY_DENSITY = 2.5f +const val STELLAR_DENSITY = 0.5f + +const val SPACECRAFT_MASS = 10f + +const val CRAFT_SPEED_LIMIT = 5_000f +const val MAIN_ENGINE_ACCEL = 1000f // thrust effect, pixels per second squared +const val LAUNCH_MECO = 2f // how long to suspend gravity when launching + +const val SCALED_THRUST = true + +interface Removable { + fun canBeRemoved(): Boolean +} + +open class Planet( + val orbitCenter: Vec2, + radius: Float, + pos: Vec2, + var speed: Float, + var color: Color = Color.White +) : Body() { + var atmosphere = "" + var description = "" + var flora = "" + var fauna = "" + var explored = false + private val orbitRadius: Float + init { + this.radius = radius + this.pos = pos + orbitRadius = pos.distance(orbitCenter) + mass = 4 / 3 * PIf * radius.pow(3) * PLANETARY_DENSITY + } + + override fun update(sim: Simulator, dt: Float) { + val orbitAngle = (pos - orbitCenter).angle() + // constant linear velocity + velocity = Vec2.makeWithAngleMag(orbitAngle + PIf / 2f, speed) + + super.update(sim, dt) + } + + override fun postUpdate(sim: Simulator, dt: Float) { + // This is kind of like a constraint, but whatever. + val orbitAngle = (pos - orbitCenter).angle() + pos = orbitCenter + Vec2.makeWithAngleMag(orbitAngle, orbitRadius) + super.postUpdate(sim, dt) + } +} + +enum class StarClass { + O, + B, + A, + F, + G, + K, + M +} + +fun starColor(cls: StarClass) = + when (cls) { + StarClass.O -> Color(0xFF6666FF) + StarClass.B -> Color(0xFFCCCCFF) + StarClass.A -> Color(0xFFEEEEFF) + StarClass.F -> Color(0xFFFFFFFF) + StarClass.G -> Color(0xFFFFFF66) + StarClass.K -> Color(0xFFFFCC33) + StarClass.M -> Color(0xFFFF8800) + } + +class Star(val cls: StarClass, radius: Float) : + Planet(orbitCenter = Vec2.Zero, radius = radius, pos = Vec2.Zero, speed = 0f) { + init { + pos = Vec2.Zero + mass = 4 / 3 * PIf * radius.pow(3) * STELLAR_DENSITY + color = starColor(cls) + collides = false + } + var anim = 0f + override fun update(sim: Simulator, dt: Float) { + anim += dt + } +} + +open class Universe(val namer: Namer, randomSeed: Long) : Simulator(randomSeed) { + var latestDiscovery: Planet? = null + lateinit var star: Star + lateinit var ship: Spacecraft + val planets: MutableList<Planet> = mutableListOf() + var follow: Body? = null + val ringfence = Container(UNIVERSE_RANGE) + + fun initTest() { + val systemName = "TEST SYSTEM" + star = + Star( + cls = StarClass.A, + radius = STAR_RADIUS_RANGE.endInclusive, + ) + .apply { name = "TEST SYSTEM" } + + repeat(NUM_PLANETS_RANGE.last) { + val thisPlanetFrac = it.toFloat() / (NUM_PLANETS_RANGE.last - 1) + val radius = + lerp(PLANET_RADIUS_RANGE.start, PLANET_RADIUS_RANGE.endInclusive, thisPlanetFrac) + val orbitRadius = + lerp(PLANET_ORBIT_RANGE.start, PLANET_ORBIT_RANGE.endInclusive, thisPlanetFrac) + + val period = sqrt(orbitRadius.pow(3f) / star.mass) * KEPLER_CONSTANT + val speed = 2f * PIf * orbitRadius / period + + val p = + Planet( + orbitCenter = star.pos, + radius = radius, + pos = star.pos + Vec2.makeWithAngleMag(thisPlanetFrac * PI2f, orbitRadius), + speed = speed, + color = Colors.Eigengrau4 + ) + android.util.Log.v( + "Landroid", + "created planet $p with period $period and vel $speed" + ) + val num = it + 1 + p.description = "TEST PLANET #$num" + p.atmosphere = "radius=$radius" + p.flora = "mass=${p.mass}" + p.fauna = "speed=$speed" + planets.add(p) + add(p) + } + + planets.sortBy { it.pos.distance(star.pos) } + planets.forEachIndexed { idx, planet -> planet.name = "$systemName ${idx + 1}" } + add(star) + + ship = Spacecraft() + + ship.pos = star.pos + Vec2.makeWithAngleMag(PIf / 4, PLANET_ORBIT_RANGE.start) + ship.angle = 0f + add(ship) + + ringfence.add(ship) + add(ringfence) + + follow = ship + } + + fun initRandom() { + val systemName = namer.nameSystem(rng) + star = + Star( + cls = rng.choose(StarClass.values()), + radius = rng.nextFloatInRange(STAR_RADIUS_RANGE) + ) + star.name = systemName + repeat(rng.nextInt(NUM_PLANETS_RANGE.first, NUM_PLANETS_RANGE.last + 1)) { + val radius = rng.nextFloatInRange(PLANET_RADIUS_RANGE) + val orbitRadius = + lerp( + PLANET_ORBIT_RANGE.start, + PLANET_ORBIT_RANGE.endInclusive, + rng.nextFloat().pow(1f) + ) + + // Kepler's third law + val period = sqrt(orbitRadius.pow(3f) / star.mass) * KEPLER_CONSTANT + val speed = 2f * PIf * orbitRadius / period + + val p = + Planet( + orbitCenter = star.pos, + radius = radius, + pos = star.pos + Vec2.makeWithAngleMag(rng.nextFloat() * PI2f, orbitRadius), + speed = speed, + color = Colors.Eigengrau4 + ) + android.util.Log.v( + "Landroid", + "created planet $p with period $period and vel $speed" + ) + p.description = namer.describePlanet(rng) + p.atmosphere = namer.describeAtmo(rng) + p.flora = namer.describeLife(rng) + p.fauna = namer.describeLife(rng) + planets.add(p) + add(p) + } + planets.sortBy { it.pos.distance(star.pos) } + planets.forEachIndexed { idx, planet -> planet.name = "$systemName ${idx + 1}" } + add(star) + + ship = Spacecraft() + + ship.pos = + star.pos + + Vec2.makeWithAngleMag( + rng.nextFloat() * PI2f, + rng.nextFloatInRange(PLANET_ORBIT_RANGE.start, PLANET_ORBIT_RANGE.endInclusive) + ) + ship.angle = rng.nextFloat() * PI2f + add(ship) + + ringfence.add(ship) + add(ringfence) + + follow = ship + } + + override fun updateAll(dt: Float, entities: ArraySet<Entity>) { + // check for passing in front of the sun + ship.transit = false + + (planets + star).forEach { planet -> + val vector = planet.pos - ship.pos + val d = vector.mag() + if (d < planet.radius) { + if (planet is Star) ship.transit = true + } else if ( + now > ship.launchClock + LAUNCH_MECO + ) { // within MECO sec of launch, no gravity at all + // simulate gravity: $ f_g = G * m1 * m2 * 1/d^2 $ + ship.velocity = + ship.velocity + + Vec2.makeWithAngleMag( + vector.angle(), + GRAVITATION * (ship.mass * planet.mass) / d.pow(2) + ) * dt + } + } + + super.updateAll(dt, entities) + } + + fun closestPlanet(): Planet { + val bodiesByDist = + (planets + star) + .map { planet -> (planet.pos - ship.pos) to planet } + .sortedBy { it.first.mag() } + + return bodiesByDist[0].second + } + + override fun solveAll(dt: Float, constraints: ArraySet<Constraint>) { + if (ship.landing == null) { + val planet = closestPlanet() + + if (planet.collides) { + val d = (ship.pos - planet.pos).mag() - ship.radius - planet.radius + val a = (ship.pos - planet.pos).angle() + + if (d < 0) { + // landing, or impact? + + // 1. relative speed + val vDiff = (ship.velocity - planet.velocity).mag() + // 2. landing angle + val aDiff = (ship.angle - a).absoluteValue + + // landing criteria + if (aDiff < PIf / 4 + // && + // vDiff < 100f + ) { + val landing = Landing(ship, planet, a) + ship.landing = landing + ship.velocity = planet.velocity + add(landing) + + planet.explored = true + latestDiscovery = planet + } else { + val impact = planet.pos + Vec2.makeWithAngleMag(a, planet.radius) + ship.pos = + planet.pos + Vec2.makeWithAngleMag(a, planet.radius + ship.radius - d) + + // add(Spark( + // lifetime = 1f, + // style = Spark.Style.DOT, + // color = Color.Yellow, + // size = 10f + // ).apply { + // pos = impact + // opos = impact + // velocity = Vec2.Zero + // }) + // + (1..10).forEach { + Spark( + lifetime = rng.nextFloatInRange(0.5f, 2f), + style = Spark.Style.DOT, + color = Color.White, + size = 1f + ) + .apply { + pos = + impact + + Vec2.makeWithAngleMag( + rng.nextFloatInRange(0f, 2 * PIf), + rng.nextFloatInRange(0.1f, 0.5f) + ) + opos = pos + velocity = + ship.velocity * 0.8f + + Vec2.makeWithAngleMag( + // a + + // rng.nextFloatInRange(-PIf, PIf), + rng.nextFloatInRange(0f, 2 * PIf), + rng.nextFloatInRange(0.1f, 0.5f) + ) + add(this) + } + } + } + } + } + } + + super.solveAll(dt, constraints) + } + + override fun postUpdateAll(dt: Float, entities: ArraySet<Entity>) { + super.postUpdateAll(dt, entities) + + entities + .filterIsInstance<Removable>() + .filter(predicate = Removable::canBeRemoved) + .filterIsInstance<Entity>() + .forEach { remove(it) } + } +} + +class Landing(val ship: Spacecraft, val planet: Planet, val angle: Float) : Constraint { + private val landingVector = Vec2.makeWithAngleMag(angle, ship.radius + planet.radius) + override fun solve(sim: Simulator, dt: Float) { + val desiredPos = planet.pos + landingVector + ship.pos = (ship.pos * 0.5f) + (desiredPos * 0.5f) // @@@ FIXME + ship.angle = angle + } +} + +class Spark( + var lifetime: Float, + collides: Boolean = false, + mass: Float = 0f, + val style: Style = Style.LINE, + val color: Color = Color.Gray, + val size: Float = 2f +) : Removable, Body() { + enum class Style { + LINE, + LINE_ABSOLUTE, + DOT, + DOT_ABSOLUTE, + RING + } + + init { + this.collides = collides + this.mass = mass + } + override fun update(sim: Simulator, dt: Float) { + super.update(sim, dt) + lifetime -= dt + } + override fun canBeRemoved(): Boolean { + return lifetime < 0 + } +} + +const val TRACK_LENGTH = 10_000 +const val SIMPLE_TRACK_DRAWING = true + +class Track { + val positions = ArrayDeque<Vec2>(TRACK_LENGTH) + private val angles = ArrayDeque<Float>(TRACK_LENGTH) + fun add(x: Float, y: Float, a: Float) { + if (positions.size >= (TRACK_LENGTH - 1)) { + positions.removeFirst() + angles.removeFirst() + positions.removeFirst() + angles.removeFirst() + } + positions.addLast(Vec2(x, y)) + angles.addLast(a) + } +} + +class Spacecraft : Body() { + var thrust = Vec2.Zero + var launchClock = 0f + + var transit = false + + val track = Track() + + var landing: Landing? = null + + init { + mass = SPACECRAFT_MASS + radius = 12f + } + + override fun update(sim: Simulator, dt: Float) { + // check for thrusters + val thrustMag = thrust.mag() + if (thrustMag > 0) { + var deltaV = MAIN_ENGINE_ACCEL * dt + if (SCALED_THRUST) deltaV *= thrustMag.coerceIn(0f, 1f) + + if (landing == null) { + // we are free in space, so we attempt to pivot toward the desired direction + // NOTE: no longer required thanks to FlightStick + // angle = thrust.angle() + } else + landing?.let { landing -> + if (launchClock == 0f) launchClock = sim.now + 1f /* @@@ TODO extract */ + + if (sim.now > launchClock) { + // first-stage to orbit has 1000x power + // deltaV *= 1000f + sim.remove(landing) + this.landing = null + } else { + deltaV = 0f + } + } + + // this is it. impart thrust to the ship. + // note that we always thrust in the forward direction + velocity += Vec2.makeWithAngleMag(angle, deltaV) + } else { + if (launchClock != 0f) launchClock = 0f + } + + // apply global speed limit + if (velocity.mag() > CRAFT_SPEED_LIMIT) + velocity = Vec2.makeWithAngleMag(velocity.angle(), CRAFT_SPEED_LIMIT) + + super.update(sim, dt) + } + + override fun postUpdate(sim: Simulator, dt: Float) { + super.postUpdate(sim, dt) + + // special effects all need to be added after the simulation step so they have + // the correct position of the ship. + track.add(pos.x, pos.y, angle) + + val mag = thrust.mag() + if (sim.rng.nextFloat() < mag) { + // exhaust + sim.add( + Spark( + lifetime = sim.rng.nextFloatInRange(0.5f, 1f), + collides = true, + mass = 1f, + style = Spark.Style.RING, + size = 3f, + color = Color(0x40FFFFFF) + ) + .also { spark -> + spark.pos = pos + spark.opos = pos + spark.velocity = + velocity + + Vec2.makeWithAngleMag( + angle + sim.rng.nextFloatInRange(-0.2f, 0.2f), + -MAIN_ENGINE_ACCEL * mag * 10f * dt + ) + } + ) + } + } +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Vec2.kt b/packages/EasterEgg/src/com/android/egg/landroid/Vec2.kt new file mode 100644 index 000000000000..82bae759e84e --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/Vec2.kt @@ -0,0 +1,65 @@ +/* + * 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.egg.landroid + +import androidx.compose.ui.geometry.Offset +import kotlin.math.PI +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.sin + +const val PIf = PI.toFloat() +const val PI2f = (2 * PI).toFloat() + +typealias Vec2 = Offset + +fun Vec2.str(fmt: String = "%+.2f"): String = "<$fmt,$fmt>".format(x, y) + +fun Vec2(x: Float, y: Float): Vec2 = Offset(x, y) + +fun Vec2.mag(): Float { + return getDistance() +} + +fun Vec2.distance(other: Vec2): Float { + return (this - other).mag() +} + +fun Vec2.angle(): Float { + return atan2(y, x) +} + +fun Vec2.dot(o: Vec2): Float { + return x * o.x + y * o.y +} + +fun Vec2.product(f: Float): Vec2 { + return Vec2(x * f, y * f) +} + +fun Offset.Companion.makeWithAngleMag(a: Float, m: Float): Vec2 { + return Vec2(m * cos(a), m * sin(a)) +} + +fun Vec2.rotate(angle: Float, origin: Vec2 = Vec2.Zero): Offset { + val translated = this - origin + return origin + + Offset( + (translated.x * cos(angle) - translated.y * sin(angle)), + (translated.x * sin(angle) + translated.y * cos(angle)) + ) +} diff --git a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt new file mode 100644 index 000000000000..24b9c6a283c2 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt @@ -0,0 +1,334 @@ +/* + * 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.egg.landroid + +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.PointMode +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.drawscope.rotateRad +import androidx.compose.ui.graphics.drawscope.scale +import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.util.lerp +import androidx.core.math.MathUtils.clamp +import java.lang.Float.max +import kotlin.math.sqrt + +const val DRAW_ORBITS = true +const val DRAW_GRAVITATIONAL_FIELDS = true +const val DRAW_STAR_GRAVITATIONAL_FIELDS = true + +val STAR_POINTS = android.os.Build.VERSION.SDK_INT.takeIf { it in 1..99 } ?: 31 + +/** + * A zoomedDrawScope is one that is scaled, but remembers its zoom level, so you can correct for it + * if you want to draw single-pixel lines. Which we do. + */ +interface ZoomedDrawScope : DrawScope { + val zoom: Float +} + +fun DrawScope.zoom(zoom: Float, block: ZoomedDrawScope.() -> Unit) { + val ds = + object : ZoomedDrawScope, DrawScope by this { + override var zoom = zoom + } + ds.scale(zoom) { block(ds) } +} + +class VisibleUniverse(namer: Namer, randomSeed: Long) : Universe(namer, randomSeed) { + // Magic variable. Every time we update it, Compose will notice and redraw the universe. + val triggerDraw = mutableStateOf(0L) + + fun simulateAndDrawFrame(nanos: Long) { + // By writing this value, Compose will look for functions that read it (like drawZoomed). + triggerDraw.value = nanos + + step(nanos) + } +} + +fun ZoomedDrawScope.drawUniverse(universe: VisibleUniverse) { + with(universe) { + triggerDraw.value // Please recompose when this value changes. + + // star.drawZoomed(ds, zoom) + // planets.forEach { p -> + // p.drawZoomed(ds, zoom) + // if (p == follow) { + // drawCircle(Color.Red, 20f / zoom, p.pos) + // } + // } + // + // ship.drawZoomed(ds, zoom) + + constraints.forEach { + when (it) { + is Landing -> drawLanding(it) + is Container -> drawContainer(it) + } + } + drawStar(star) + entities.forEach { + if (it === ship || it === star) return@forEach // draw the ship last + when (it) { + is Spacecraft -> drawSpacecraft(it) + is Spark -> drawSpark(it) + is Planet -> drawPlanet(it) + } + } + drawSpacecraft(ship) + } +} + +fun ZoomedDrawScope.drawContainer(container: Container) { + drawCircle( + color = Color(0xFF800000), + radius = container.radius, + center = Vec2.Zero, + style = + Stroke( + width = 1f / zoom, + pathEffect = PathEffect.dashPathEffect(floatArrayOf(8f / zoom, 8f / zoom), 0f) + ) + ) + // val path = Path().apply { + // fillType = PathFillType.EvenOdd + // addOval(Rect(center = Vec2.Zero, radius = container.radius)) + // addOval(Rect(center = Vec2.Zero, radius = container.radius + 10_000)) + // } + // drawPath( + // path = path, + // + // ) +} + +fun ZoomedDrawScope.drawGravitationalField(planet: Planet) { + val rings = 8 + for (i in 0 until rings) { + val force = + lerp( + 200f, + 0.01f, + i.toFloat() / rings + ) // first rings at force = 1N, dropping off after that + val r = sqrt(GRAVITATION * planet.mass * SPACECRAFT_MASS / force) + drawCircle( + color = Color(1f, 0f, 0f, lerp(0.5f, 0.1f, i.toFloat() / rings)), + center = planet.pos, + style = Stroke(2f / zoom), + radius = r + ) + } +} + +fun ZoomedDrawScope.drawPlanet(planet: Planet) { + with(planet) { + if (DRAW_ORBITS) + drawCircle( + color = Color(0x8000FFFF), + radius = pos.distance(orbitCenter), + center = orbitCenter, + style = + Stroke( + width = 1f / zoom, + ) + ) + + if (DRAW_GRAVITATIONAL_FIELDS) { + drawGravitationalField(this) + } + + drawCircle(color = Colors.Eigengrau, radius = radius, center = pos) + drawCircle(color = color, radius = radius, center = pos, style = Stroke(2f / zoom)) + } +} + +fun ZoomedDrawScope.drawStar(star: Star) { + translate(star.pos.x, star.pos.y) { + drawCircle(color = star.color, radius = star.radius, center = Vec2.Zero) + + if (DRAW_STAR_GRAVITATIONAL_FIELDS) this@drawStar.drawGravitationalField(star) + + rotateRad(radians = star.anim / 23f * PI2f, pivot = Vec2.Zero) { + drawPath( + path = + createStar( + radius1 = star.radius + 80, + radius2 = star.radius + 250, + points = STAR_POINTS + ), + color = star.color, + style = + Stroke( + width = 3f / this@drawStar.zoom, + pathEffect = PathEffect.cornerPathEffect(radius = 200f) + ) + ) + } + rotateRad(radians = star.anim / -19f * PI2f, pivot = Vec2.Zero) { + drawPath( + path = + createStar( + radius1 = star.radius + 20, + radius2 = star.radius + 200, + points = STAR_POINTS + 1 + ), + color = star.color, + style = + Stroke( + width = 3f / this@drawStar.zoom, + pathEffect = PathEffect.cornerPathEffect(radius = 200f) + ) + ) + } + } +} + +val spaceshipPath = + Path().apply { + parseSvgPathData( + """ +M11.853 0 +C11.853 -4.418 8.374 -8 4.083 -8 +L-5.5 -8 +C-6.328 -8 -7 -7.328 -7 -6.5 +C-7 -5.672 -6.328 -5 -5.5 -5 +L-2.917 -5 +C-1.26 -5 0.083 -3.657 0.083 -2 +L0.083 2 +C0.083 3.657 -1.26 5 -2.917 5 +L-5.5 5 +C-6.328 5 -7 5.672 -7 6.5 +C-7 7.328 -6.328 8 -5.5 8 +L4.083 8 +C8.374 8 11.853 4.418 11.853 0 +Z +""" + ) + } +val thrustPath = createPolygon(-3f, 3).also { it.translate(Vec2(-4f, 0f)) } + +fun ZoomedDrawScope.drawSpacecraft(ship: Spacecraft) { + with(ship) { + rotateRad(angle, pivot = pos) { + translate(pos.x, pos.y) { + // drawPath( + // path = createStar(200f, 100f, 3), + // color = Color.White, + // style = Stroke(width = 2f / zoom) + // ) + drawPath(path = spaceshipPath, color = Colors.Eigengrau) // fauxpaque + drawPath( + path = spaceshipPath, + color = if (transit) Color.Black else Color.White, + style = Stroke(width = 2f / this@drawSpacecraft.zoom) + ) + if (thrust != Vec2.Zero) { + drawPath( + path = thrustPath, + color = Color(0xFFFF8800), + style = + Stroke( + width = 2f / this@drawSpacecraft.zoom, + pathEffect = PathEffect.cornerPathEffect(radius = 1f) + ) + ) + } + // drawRect( + // topLeft = Offset(-1f, -1f), + // size = Size(2f, 2f), + // color = Color.Cyan, + // style = Stroke(width = 2f / zoom) + // ) + // drawLine( + // start = Vec2.Zero, + // end = Vec2(20f, 0f), + // color = Color.Cyan, + // strokeWidth = 2f / zoom + // ) + } + } + // // DEBUG: draw velocity vector + // drawLine( + // start = pos, + // end = pos + velocity, + // color = Color.Red, + // strokeWidth = 3f / zoom + // ) + drawTrack(track) + } +} + +fun ZoomedDrawScope.drawLanding(landing: Landing) { + val v = landing.planet.pos + Vec2.makeWithAngleMag(landing.angle, landing.planet.radius) + drawLine(Color.Red, v + Vec2(-5f, -5f), v + Vec2(5f, 5f), strokeWidth = 1f / zoom) + drawLine(Color.Red, v + Vec2(5f, -5f), v + Vec2(-5f, 5f), strokeWidth = 1f / zoom) +} + +fun ZoomedDrawScope.drawSpark(spark: Spark) { + with(spark) { + if (lifetime < 0) return + when (style) { + Spark.Style.LINE -> + if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size) + Spark.Style.LINE_ABSOLUTE -> + if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size / zoom) + Spark.Style.DOT -> drawCircle(color, size, pos) + Spark.Style.DOT_ABSOLUTE -> drawCircle(color, size, pos / zoom) + Spark.Style.RING -> drawCircle(color, size, pos, style = Stroke(width = 1f / zoom)) + // drawPoints(listOf(pos), PointMode.Points, color, strokeWidth = 2f/zoom) + // drawCircle(color, 2f/zoom, pos) + } + // drawCircle(Color.Gray, center = pos, radius = 1.5f / zoom) + } +} + +fun ZoomedDrawScope.drawTrack(track: Track) { + with(track) { + if (SIMPLE_TRACK_DRAWING) { + drawPoints( + positions, + pointMode = PointMode.Lines, + color = Color.Green, + strokeWidth = 1f / zoom + ) + // if (positions.size < 2) return + // drawPath(Path() + // .apply { + // val p = positions[positions.size - 1] + // moveTo(p.x, p.y) + // positions.reversed().subList(1, positions.size).forEach { p -> + // lineTo(p.x, p.y) + // } + // }, + // color = Color.Green, style = Stroke(1f/zoom)) + } else { + if (positions.size < 2) return + var prev: Vec2 = positions[positions.size - 1] + var a = 0.5f + positions.reversed().subList(1, positions.size).forEach { pos -> + drawLine(Color(0f, 1f, 0f, a), prev, pos, strokeWidth = max(1f, 1f / zoom)) + prev = pos + a = clamp((a - 1f / TRACK_LENGTH), 0f, 1f) + } + } + } +} diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index 52896351d92f..329d80e05434 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -69,7 +69,6 @@ dependencies { implementation("com.airbnb.android:lottie-compose:5.2.0") androidTestImplementation(project(":testutils")) - androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing") androidTestImplementation(libs.dexmaker.mockito) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt index 0f5862a9829d..afce16c530c7 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt @@ -24,7 +24,7 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.annotation.VisibleForTesting -import androidx.compose.animation.AnimatedContentScope +import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn @@ -146,22 +146,26 @@ private fun NavControllerWrapperImpl.NavContent( arguments = spp.parameter, enterTransition = { slideIntoContainer( - AnimatedContentScope.SlideDirection.Start, animationSpec = slideEffect + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = slideEffect ) + fadeIn(animationSpec = fadeEffect) }, exitTransition = { slideOutOfContainer( - AnimatedContentScope.SlideDirection.Start, animationSpec = slideEffect + AnimatedContentTransitionScope.SlideDirection.Start, + animationSpec = slideEffect ) + fadeOut(animationSpec = fadeEffect) }, popEnterTransition = { slideIntoContainer( - AnimatedContentScope.SlideDirection.End, animationSpec = slideEffect + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = slideEffect ) + fadeIn(animationSpec = fadeEffect) }, popExitTransition = { slideOutOfContainer( - AnimatedContentScope.SlideDirection.End, animationSpec = slideEffect + AnimatedContentTransitionScope.SlideDirection.End, + animationSpec = slideEffect ) + fadeOut(animationSpec = fadeEffect) }, ) { navBackStackEntry -> diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt index 57bb838d55ea..81bbc2456577 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt @@ -18,7 +18,7 @@ package com.android.settingslib.spa.framework.compose import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.AnimatedContentScope +import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.ContentTransform import androidx.compose.animation.EnterTransition import androidx.compose.animation.ExitTransition @@ -78,14 +78,10 @@ public fun AnimatedNavHost( modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.Center, route: String? = null, - enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) = - { fadeIn(animationSpec = tween(700)) }, - exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) = - { fadeOut(animationSpec = tween(700)) }, - popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) = - enterTransition, - popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) = - exitTransition, + enterTransition: (AnimatedScope.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) }, + exitTransition: (AnimatedScope.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) }, + popEnterTransition: (AnimatedScope.() -> EnterTransition) = enterTransition, + popExitTransition: (AnimatedScope.() -> ExitTransition) = exitTransition, builder: NavGraphBuilder.() -> Unit ) { AnimatedNavHost( @@ -123,14 +119,10 @@ public fun AnimatedNavHost( graph: NavGraph, modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.Center, - enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) = - { fadeIn(animationSpec = tween(700)) }, - exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) = - { fadeOut(animationSpec = tween(700)) }, - popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) = - enterTransition, - popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) = - exitTransition, + enterTransition: (AnimatedScope.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) }, + exitTransition: (AnimatedScope.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) }, + popEnterTransition: (AnimatedScope.() -> EnterTransition) = enterTransition, + popExitTransition: (AnimatedScope.() -> ExitTransition) = exitTransition, ) { val lifecycleOwner = LocalLifecycleOwner.current @@ -168,7 +160,7 @@ public fun AnimatedNavHost( val backStackEntry = visibleEntries.lastOrNull() if (backStackEntry != null) { - val finalEnter: AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition = { + val finalEnter: AnimatedScope.() -> EnterTransition = { val targetDestination = targetState.destination as AnimatedComposeNavigator.Destination if (composeNavigator.isPop.value) { @@ -182,7 +174,7 @@ public fun AnimatedNavHost( } } - val finalExit: AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition = { + val finalExit: AnimatedScope.() -> ExitTransition = { val initialDestination = initialState.destination as AnimatedComposeNavigator.Destination @@ -245,19 +237,16 @@ public fun AnimatedNavHost( DialogHost(dialogNavigator) } -@ExperimentalAnimationApi -internal val enterTransitions = - mutableMapOf<String?, - (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)?>() +@OptIn(ExperimentalAnimationApi::class) +internal typealias AnimatedScope = AnimatedContentTransitionScope<NavBackStackEntry> @ExperimentalAnimationApi -internal val exitTransitions = - mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)?>() +internal val enterTransitions = mutableMapOf<String?, (AnimatedScope.() -> EnterTransition?)?>() @ExperimentalAnimationApi -internal val popEnterTransitions = - mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)?>() +internal val exitTransitions = mutableMapOf<String?, (AnimatedScope.() -> ExitTransition?)?>() +@ExperimentalAnimationApi +internal val popEnterTransitions = mutableMapOf<String?, (AnimatedScope.() -> EnterTransition?)?>() @ExperimentalAnimationApi -internal val popExitTransitions = - mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)?>() +internal val popExitTransitions = mutableMapOf<String?, (AnimatedScope.() -> ExitTransition?)?>() diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt index 9e58603bbaff..bf92f5dda1a9 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt @@ -16,7 +16,6 @@ package com.android.settingslib.spa.framework.compose -import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.EnterTransition import androidx.compose.animation.ExitTransition @@ -25,9 +24,7 @@ import androidx.compose.runtime.Composable import androidx.navigation.NamedNavArgument import androidx.navigation.NavBackStackEntry import androidx.navigation.NavDeepLink -import androidx.navigation.NavGraph import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.navigation import androidx.navigation.get /** @@ -47,14 +44,10 @@ public fun NavGraphBuilder.composable( route: String, arguments: List<NamedNavArgument> = emptyList(), deepLinks: List<NavDeepLink> = emptyList(), - enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = null, - exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = null, - popEnterTransition: ( - AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition? - )? = enterTransition, - popExitTransition: ( - AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition? - )? = exitTransition, + enterTransition: (AnimatedScope.() -> EnterTransition?)? = null, + exitTransition: (AnimatedScope.() -> ExitTransition?)? = null, + popEnterTransition: (AnimatedScope.() -> EnterTransition?)? = enterTransition, + popExitTransition: (AnimatedScope.() -> ExitTransition?)? = exitTransition, content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit ) { addDestination( @@ -76,43 +69,3 @@ public fun NavGraphBuilder.composable( } ) } - -/** - * Construct a nested [NavGraph] - * - * @param startDestination the starting destination's route for this NavGraph - * @param route the destination's unique route - * @param arguments list of arguments to associate with destination - * @param deepLinks list of deep links to associate with the destinations - * @param enterTransition callback to define enter transitions for destination in this NavGraph - * @param exitTransition callback to define exit transitions for destination in this NavGraph - * @param popEnterTransition callback to define pop enter transitions for destination in this - * NavGraph - * @param popExitTransition callback to define pop exit transitions for destination in this NavGraph - * @param builder the builder used to construct the graph - * - * @return the newly constructed nested NavGraph - */ -@ExperimentalAnimationApi -public fun NavGraphBuilder.navigation( - startDestination: String, - route: String, - arguments: List<NamedNavArgument> = emptyList(), - deepLinks: List<NavDeepLink> = emptyList(), - enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = null, - exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = null, - popEnterTransition: ( - AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition? - )? = enterTransition, - popExitTransition: ( - AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition? - )? = exitTransition, - builder: NavGraphBuilder.() -> Unit -) { - navigation(startDestination, route, arguments, deepLinks, builder).apply { - enterTransition?.let { enterTransitions[route] = enterTransition } - exitTransition?.let { exitTransitions[route] = exitTransition } - popEnterTransition?.let { popEnterTransitions[route] = popEnterTransition } - popExitTransition?.let { popExitTransitions[route] = popExitTransition } - } -} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt index 79635a0c8280..aa148b022b92 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt @@ -20,20 +20,12 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager -import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.TabRow import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalConfiguration import com.android.settingslib.spa.framework.theme.SettingsDimension import kotlin.math.absoluteValue import kotlinx.coroutines.launch @@ -49,7 +41,7 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit Column { val coroutineScope = rememberCoroutineScope() - val pagerState = rememberPageStateFixed() + val pagerState = rememberPagerState { titles.size } TabRow( selectedTabIndex = pagerState.currentPage, @@ -72,46 +64,8 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit } } - HorizontalPager(pageCount = titles.size, state = pagerState) { page -> + HorizontalPager(state = pagerState) { page -> content(page) } } } - -/** - * Gets the state of [PagerState]. - * - * This is a work around. - * - * TODO: Remove this and replace with rememberPageState() after the Compose Foundation 1.5.0-alpha04 - * updated in the platform. - */ -@Composable -@OptIn(ExperimentalFoundationApi::class) -private fun rememberPageStateFixed(): PagerState { - var currentPage by rememberSaveable { mutableStateOf(0) } - var targetPage by rememberSaveable { mutableStateOf(-1) } - val pagerState = rememberPagerState() - val configuration = LocalConfiguration.current - var lastScreenWidthDp by rememberSaveable { mutableStateOf(-1) } - val screenWidthDp = configuration.screenWidthDp - LaunchedEffect(screenWidthDp) { - // Reset pager state to fix an issue after configuration change. - // When we declare android:configChanges in the manifest, the pager state is in a weird - // state after configuration change. - targetPage = currentPage - lastScreenWidthDp = screenWidthDp - } - LaunchedEffect(targetPage) { - if (targetPage != -1) { - pagerState.scrollToPage(targetPage) - targetPage = -1 - } - } - SideEffect { - if (lastScreenWidthDp == screenWidthDp) { - currentPage = pagerState.currentPage - } - } - return pagerState -} diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp index b4c67ccda6f2..f9e64aee1513 100644 --- a/packages/SettingsLib/Spa/tests/Android.bp +++ b/packages/SettingsLib/Spa/tests/Android.bp @@ -31,7 +31,6 @@ android_test { "SpaLib", "SpaLibTestUtils", "androidx.compose.runtime_runtime", - "androidx.lifecycle_lifecycle-runtime-testing", "androidx.test.ext.junit", "androidx.test.runner", "mockito-target-minus-junit4", diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp index 2c1e1c2abc2c..e4d56cc4f2a0 100644 --- a/packages/SettingsLib/Spa/testutils/Android.bp +++ b/packages/SettingsLib/Spa/testutils/Android.bp @@ -29,6 +29,7 @@ android_library { "androidx.compose.runtime_runtime", "androidx.compose.ui_ui-test-junit4", "androidx.compose.ui_ui-test-manifest", + "androidx.lifecycle_lifecycle-runtime-testing", "mockito", "truth-prebuilt", ], diff --git a/packages/SettingsLib/Spa/testutils/build.gradle.kts b/packages/SettingsLib/Spa/testutils/build.gradle.kts index 6df0226a94f9..f5a22c9fbb5d 100644 --- a/packages/SettingsLib/Spa/testutils/build.gradle.kts +++ b/packages/SettingsLib/Spa/testutils/build.gradle.kts @@ -26,7 +26,7 @@ android { sourceSets { sourceSets.getByName("main") { - java.setSrcDirs(listOf("src")) + kotlin.setSrcDirs(listOf("src")) manifest.srcFile("AndroidManifest.xml") } } @@ -40,6 +40,7 @@ dependencies { api("androidx.arch.core:core-testing:2.2.0-alpha01") api("androidx.compose.ui:ui-test-junit4:$jetpackComposeVersion") + api("androidx.lifecycle:lifecycle-runtime-testing") api(libs.truth) api("org.mockito:mockito-core:2.21.0") debugApi("androidx.compose.ui:ui-test-manifest:$jetpackComposeVersion") diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 3e2b800d5a2c..3adb882bc1b1 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -643,8 +643,10 @@ array must also be populated with a content description for each image. --> <array name="avatar_images"/> - <!-- Content descriptions for each of the images in the avatar_images array. --> - <string-array name="avatar_image_descriptions"/> + <!-- Content descriptions for each of the images in the avatar_images array. When overlaid + these values should be translated, but this empty array must not be translated or it may + replace the real descriptions with an empty array. --> + <string-array name="avatar_image_descriptions" translatable="false"/> <!-- NOTE: if you change this, you must also add the corresponding scale key and lookup table to frameworks/base/core/java/android/content/res/FontScaleConverterFactory.java --> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 79fb56602328..a05a6e9781da 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -237,7 +237,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { "startBroadcast: language = " + language + " ,programInfo = " + programInfo); } buildContentMetadata(language, programInfo); - mService.startBroadcast(mBluetoothLeAudioContentMetadata, mBroadcastCode); + mService.startBroadcast(mBluetoothLeAudioContentMetadata, + (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null); } public String getProgramInfo() { diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java index bcbd1caa45d3..848b177c821f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java @@ -243,7 +243,7 @@ public class AvatarPickerActivity extends Activity { int index = indexFromPosition(position); viewHolder.setSelected(position == mSelectedPosition); viewHolder.setDrawable(mImageDrawables.get(index)); - if (mImageDescriptions != null) { + if (mImageDescriptions != null && index < mImageDescriptions.size()) { viewHolder.setContentDescription(mImageDescriptions.get(index)); } else { viewHolder.setContentDescription(getString( diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2c0d73a3b682..2913c169a0be 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -363,7 +363,7 @@ android:killAfterRestore="false" android:hardwareAccelerated="true" android:label="@string/app_label" - android:icon="@drawable/icon" + android:icon="@drawable/android14_patch_adaptive" android:process="com.android.systemui" android:supportsRtl="true" android:theme="@style/Theme.SystemUI" @@ -1068,15 +1068,6 @@ android:exported="true"> </provider> - <!-- Provides list and realistic previews of clock faces for the picker app. --> - <provider - android:name="com.android.keyguard.clock.ClockOptionsProvider" - android:authorities="com.android.keyguard.clock" - android:enabled="false" - android:exported="false" - android:grantUriPermissions="true"> - </provider> - <receiver android:name=".statusbar.KeyboardShortcutsReceiver" android:exported="true"> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt index 23e3a01c2686..1a03ede98d12 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -34,7 +34,6 @@ import android.view.ViewGroup import android.view.ViewGroupOverlay import android.widget.FrameLayout import com.android.internal.jank.InteractionJankMonitor -import java.lang.IllegalArgumentException import java.util.LinkedList import kotlin.math.min import kotlin.math.roundToInt @@ -240,7 +239,7 @@ constructor( val ghostView = this.ghostView ?: return val backgroundView = this.backgroundView!! - if (!state.visible) { + if (!state.visible || !ghostedView.isAttachedToWindow) { if (ghostView.visibility == View.VISIBLE) { // Making the ghost view invisible will make the ghosted view visible, so order is // important here. diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt index cc337459a83c..82fe3f265384 100644 --- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -24,6 +24,9 @@ import androidx.lifecycle.LifecycleOwner import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.util.time.SystemClock /** The Compose facade, when Compose is *not* available. */ @@ -58,6 +61,14 @@ object ComposeFacade : BaseComposeFacade { throwComposeUnavailableError() } + override fun createSceneContainerView( + context: Context, + viewModel: SceneContainerViewModel, + sceneByKey: Map<SceneKey, Scene>, + ): View { + throwComposeUnavailableError() + } + private fun throwComposeUnavailableError(): Nothing { error( "Compose is not available. Make sure to check isComposeAvailable() before calling any" + diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt index 0e79b18b1c24..7926f9224347 100644 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -29,6 +29,11 @@ import com.android.systemui.people.ui.compose.PeopleScreen import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.compose.FooterActions import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.composable.ComposableScene +import com.android.systemui.scene.ui.composable.SceneContainer +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.util.time.SystemClock /** The Compose facade, when Compose is available. */ @@ -71,4 +76,22 @@ object ComposeFacade : BaseComposeFacade { } } } + + override fun createSceneContainerView( + context: Context, + viewModel: SceneContainerViewModel, + sceneByKey: Map<SceneKey, Scene>, + ): View { + return ComposeView(context).apply { + setContent { + PlatformTheme { + SceneContainer( + viewModel = viewModel, + sceneByKey = + sceneByKey.mapValues { (_, scene) -> scene as ComposableScene }, + ) + } + } + } + } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt index f80143499928..323fed0c11f3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt @@ -21,11 +21,14 @@ package com.android.systemui.bouncer.ui.composable import android.view.HapticFeedbackConstants import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.Transition import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.keyframes import androidx.compose.animation.core.snap import androidx.compose.animation.core.tween @@ -58,6 +61,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.Layout import androidx.compose.ui.platform.LocalView @@ -67,6 +71,7 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.Easings import com.android.compose.grid.VerticalGrid import com.android.systemui.R +import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance import com.android.systemui.bouncer.ui.viewmodel.EnteredKey import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.common.shared.model.ContentDescription @@ -76,6 +81,7 @@ import com.android.systemui.compose.modifiers.thenIf import kotlin.time.Duration.Companion.milliseconds import kotlin.time.DurationUnit import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -87,78 +93,13 @@ internal fun PinBouncer( // Report that the UI is shown to let the view-model run some logic. LaunchedEffect(Unit) { viewModel.onShown() } - val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState() - val animateFailure: Boolean by viewModel.animateFailure.collectAsState() - - // Show the failure animation if the user entered the wrong input. - LaunchedEffect(animateFailure) { - if (animateFailure) { - showFailureAnimation() - viewModel.onFailureAnimationShown() - } - } - Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier, ) { PinInputDisplay(viewModel) - Spacer(Modifier.height(100.dp)) - - VerticalGrid( - columns = 3, - verticalSpacing = 12.dp, - horizontalSpacing = 20.dp, - ) { - repeat(9) { index -> - val digit = index + 1 - PinButton( - onClicked = { viewModel.onPinButtonClicked(digit) }, - isEnabled = isInputEnabled, - ) { contentColor -> - PinDigit(digit, contentColor) - } - } - - PinButton( - onClicked = { viewModel.onBackspaceButtonClicked() }, - onLongPressed = { viewModel.onBackspaceButtonLongPressed() }, - isEnabled = isInputEnabled, - isIconButton = true, - ) { contentColor -> - PinIcon( - Icon.Resource( - res = R.drawable.ic_backspace_24dp, - contentDescription = - ContentDescription.Resource(R.string.keyboardview_keycode_delete), - ), - contentColor, - ) - } - - PinButton( - onClicked = { viewModel.onPinButtonClicked(0) }, - isEnabled = isInputEnabled, - ) { contentColor -> - PinDigit(0, contentColor) - } - - PinButton( - onClicked = { viewModel.onAuthenticateButtonClicked() }, - isEnabled = isInputEnabled, - isIconButton = true, - ) { contentColor -> - PinIcon( - Icon.Resource( - res = R.drawable.ic_keyboard_tab_36dp, - contentDescription = - ContentDescription.Resource(R.string.keyboardview_keycode_enter), - ), - contentColor, - ) - } - } + PinPad(viewModel) } } @@ -305,38 +246,153 @@ private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) { } @Composable -private fun PinDigit( +private fun PinPad(viewModel: PinBouncerViewModel) { + val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState() + val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState() + val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState() + val animateFailure: Boolean by viewModel.animateFailure.collectAsState() + + val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } } + LaunchedEffect(animateFailure) { + // Show the failure animation if the user entered the wrong input. + if (animateFailure) { + showFailureAnimation(buttonScaleAnimatables) + viewModel.onFailureAnimationShown() + } + } + + VerticalGrid( + columns = 3, + verticalSpacing = 12.dp, + horizontalSpacing = 20.dp, + ) { + repeat(9) { index -> + DigitButton( + index + 1, + isInputEnabled, + viewModel::onPinButtonClicked, + buttonScaleAnimatables[index]::value, + ) + } + + ActionButton( + icon = + Icon.Resource( + res = R.drawable.ic_backspace_24dp, + contentDescription = + ContentDescription.Resource(R.string.keyboardview_keycode_delete), + ), + isInputEnabled = isInputEnabled, + onClicked = viewModel::onBackspaceButtonClicked, + onLongPressed = viewModel::onBackspaceButtonLongPressed, + appearance = backspaceButtonAppearance, + scaling = buttonScaleAnimatables[9]::value, + ) + + DigitButton( + 0, + isInputEnabled, + viewModel::onPinButtonClicked, + buttonScaleAnimatables[10]::value, + ) + + ActionButton( + icon = + Icon.Resource( + res = R.drawable.ic_keyboard_tab_36dp, + contentDescription = + ContentDescription.Resource(R.string.keyboardview_keycode_enter), + ), + isInputEnabled = isInputEnabled, + onClicked = viewModel::onAuthenticateButtonClicked, + appearance = confirmButtonAppearance, + scaling = buttonScaleAnimatables[11]::value, + ) + } +} + +@Composable +private fun DigitButton( digit: Int, - contentColor: Color, + isInputEnabled: Boolean, + onClicked: (Int) -> Unit, + scaling: () -> Float, ) { - // TODO(b/281878426): once "color: () -> Color" (added to BasicText in aosp/2568972) makes it - // into Text, use that here, to animate more efficiently. - Text( - text = digit.toString(), - style = MaterialTheme.typography.headlineLarge, - color = contentColor, - ) + PinPadButton( + onClicked = { onClicked(digit) }, + isEnabled = isInputEnabled, + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + foregroundColor = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = + Modifier.graphicsLayer { + val scale = scaling() + scaleX = scale + scaleY = scale + } + ) { contentColor -> + // TODO(b/281878426): once "color: () -> Color" (added to BasicText in aosp/2568972) makes + // it into Text, use that here, to animate more efficiently. + Text( + text = digit.toString(), + style = MaterialTheme.typography.headlineLarge, + color = contentColor(), + ) + } } @Composable -private fun PinIcon( +private fun ActionButton( icon: Icon, - contentColor: Color, + isInputEnabled: Boolean, + onClicked: () -> Unit, + onLongPressed: (() -> Unit)? = null, + appearance: ActionButtonAppearance, + scaling: () -> Float, ) { - Icon( - icon = icon, - tint = contentColor, - ) + val isHidden = appearance == ActionButtonAppearance.Hidden + val hiddenAlpha by animateFloatAsState(if (isHidden) 0f else 1f, label = "Action button alpha") + + val foregroundColor = + when (appearance) { + ActionButtonAppearance.Shown -> MaterialTheme.colorScheme.onSecondaryContainer + else -> MaterialTheme.colorScheme.onSurface + } + val backgroundColor = + when (appearance) { + ActionButtonAppearance.Shown -> MaterialTheme.colorScheme.secondaryContainer + else -> MaterialTheme.colorScheme.surface + } + + PinPadButton( + onClicked = onClicked, + onLongPressed = onLongPressed, + isEnabled = isInputEnabled && !isHidden, + backgroundColor = backgroundColor, + foregroundColor = foregroundColor, + modifier = + Modifier.graphicsLayer { + alpha = hiddenAlpha + val scale = scaling() + scaleX = scale + scaleY = scale + } + ) { contentColor -> + Icon( + icon = icon, + tint = contentColor(), + ) + } } @Composable -private fun PinButton( +private fun PinPadButton( onClicked: () -> Unit, isEnabled: Boolean, + backgroundColor: Color, + foregroundColor: Color, modifier: Modifier = Modifier, onLongPressed: (() -> Unit)? = null, - isIconButton: Boolean = false, - content: @Composable (contentColor: Color) -> Unit, + content: @Composable (contentColor: () -> Color) -> Unit, ) { var isPressed: Boolean by remember { mutableStateOf(false) } @@ -370,18 +426,16 @@ private fun PinButton( animateColorAsState( when { isPressed -> MaterialTheme.colorScheme.primary - isIconButton -> MaterialTheme.colorScheme.secondaryContainer - else -> MaterialTheme.colorScheme.surfaceVariant + else -> backgroundColor }, label = "Pin button container color", animationSpec = colorAnimationSpec ) - val contentColor: Color by + val contentColor = animateColorAsState( when { isPressed -> MaterialTheme.colorScheme.onPrimary - isIconButton -> MaterialTheme.colorScheme.onSecondaryContainer - else -> MaterialTheme.colorScheme.onSurfaceVariant + else -> foregroundColor }, label = "Pin button container color", animationSpec = colorAnimationSpec @@ -420,17 +474,46 @@ private fun PinButton( } }, ) { - content(contentColor) + content(contentColor::value) } } -private fun showFailureAnimation() { - // TODO(b/282730134): implement. +private suspend fun showFailureAnimation( + buttonScaleAnimatables: List<Animatable<Float, AnimationVector1D>> +) { + coroutineScope { + buttonScaleAnimatables.forEachIndexed { index, animatable -> + launch { + animatable.animateTo( + targetValue = pinButtonErrorShrinkFactor, + animationSpec = + tween( + durationMillis = pinButtonErrorShrinkMs, + delayMillis = index * pinButtonErrorStaggerDelayMs, + easing = Easings.Linear, + ), + ) + + animatable.animateTo( + targetValue = 1f, + animationSpec = + tween( + durationMillis = pinButtonErrorRevertMs, + easing = Easings.Legacy, + ), + ) + } + } + } } private val entryShapeSize = 16.dp private val pinButtonSize = 84.dp +private val pinButtonErrorShrinkFactor = 67.dp / pinButtonSize +private const val pinButtonErrorShrinkMs = 50 +private const val pinButtonErrorStaggerDelayMs = 33 +private const val pinButtonErrorRevertMs = 617 // Pin button motion spec: http://shortn/_9TTIG6SoEa private val pinButtonPressedDuration = 100.milliseconds diff --git a/packages/SystemUI/compose/features/tests/AndroidManifest.xml b/packages/SystemUI/compose/features/tests/AndroidManifest.xml index 2f41ea92b30f..8fe9656c1879 100644 --- a/packages/SystemUI/compose/features/tests/AndroidManifest.xml +++ b/packages/SystemUI/compose/features/tests/AndroidManifest.xml @@ -40,11 +40,6 @@ android:enabled="false" tools:replace="android:authorities" tools:node="remove" /> - <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" - android:authorities="com.android.systemui.test.keyguard.clock.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> <provider android:name="com.android.systemui.people.PeopleProvider" android:authorities="com.android.systemui.test.people.disabled" android:enabled="false" diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 8cba2ab0b70b..14434655fc92 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -113,6 +113,7 @@ open class ClockRegistry( val logBuffer: LogBuffer? = null, val keepAllLoaded: Boolean, subTag: String, + var isTransitClockEnabled: Boolean = false, ) { private val TAG = "${ClockRegistry::class.simpleName} ($subTag)" interface ClockChangeListener { @@ -204,6 +205,10 @@ open class ClockRegistry( var isClockListChanged = false for (clock in plugin.getClocks()) { val id = clock.clockId + if (!isTransitClockEnabled && id == "DIGITAL_CLOCK_METRO") { + continue + } + val info = availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) { isClockListChanged = true diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt index b6d5ef3720f5..95a9ce960dcd 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt @@ -184,6 +184,9 @@ object CustomizationProviderContract { /** Flag denoting AI Wallpapers are enabled in wallpaper picker. */ const val FLAG_NAME_WALLPAPER_PICKER_UI_FOR_AIWP = "wallpaper_picker_ui_for_aiwp" + /** Flag denoting transit clock are enabled in wallpaper picker. */ + const val FLAG_NAME_TRANSIT_CLOCK = "lockscreen_custom_transit_clock" + object Columns { /** String. Unique ID for the flag. */ const val NAME = "name" diff --git a/packages/SystemUI/docs/clock-plugins.md b/packages/SystemUI/docs/clock-plugins.md index 2226d7956ded..9cb115a696c9 100644 --- a/packages/SystemUI/docs/clock-plugins.md +++ b/packages/SystemUI/docs/clock-plugins.md @@ -1,7 +1,7 @@ # Clock Plugins -The clock appearing on the lock screen and always on display (AOD) can be -customized via the ClockProviderPlugin plugin interface. +The clock appearing on the lock screen and always on display (AOD) can be customized via the +ClockProviderPlugin plugin interface. The ClockPlugin interface has been removed. ## Lock screen integration The lockscreen code has two main components, a [clock customization library](../customization), and diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java deleted file mode 100644 index bef61b867f7d..000000000000 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2018 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.plugins; - -import android.graphics.Bitmap; -import android.graphics.Paint.Style; -import android.view.View; - -import com.android.systemui.plugins.annotations.ProvidesInterface; - -import java.util.TimeZone; - -/** - * Plugin used to replace main clock in keyguard. - * @deprecated Migrating to ClockProviderPlugin - */ -@Deprecated -@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION) -public interface ClockPlugin extends Plugin { - - String ACTION = "com.android.systemui.action.PLUGIN_CLOCK"; - int VERSION = 5; - - /** - * Get the name of the clock face. - * - * This name should not be translated. - */ - String getName(); - - /** - * Get the title of the clock face to be shown in the picker app. - */ - String getTitle(); - - /** - * Get thumbnail of clock face to be shown in the picker app. - */ - Bitmap getThumbnail(); - - /** - * Get preview images of clock face to be shown in the picker app. - * - * Preview image should be realistic and show what the clock face will look like on AOD and lock - * screen. - * - * @param width width of the preview image, should be the same as device width in pixels. - * @param height height of the preview image, should be the same as device height in pixels. - */ - Bitmap getPreview(int width, int height); - - /** - * Get clock view. - * @return clock view from plugin. - */ - View getView(); - - /** - * Get clock view for a large clock that appears behind NSSL. - */ - default View getBigClockView() { - return null; - } - - /** - * Returns the preferred Y position of the clock. - * - * @param totalHeight Height of the parent container. - * @return preferred Y position. - */ - int getPreferredY(int totalHeight); - - /** - * Allows the plugin to clean up resources when no longer needed. - * - * Called when the view previously created by {@link ClockPlugin#getView()} has been detached - * from the view hierarchy. - */ - void onDestroyView(); - - /** - * Set clock paint style. - * @param style The new style to set in the paint. - */ - void setStyle(Style style); - - /** - * Set clock text color. - * @param color A color value. - */ - void setTextColor(int color); - - /** - * Sets the color palette for the clock face. - * @param supportsDarkText Whether dark text can be displayed. - * @param colors Colors that should be used on the clock face, ordered from darker to lighter. - */ - default void setColorPalette(boolean supportsDarkText, int[] colors) {} - - /** - * Set the amount (ratio) that the device has transitioned to doze. - * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake. - */ - default void setDarkAmount(float darkAmount) {} - - /** - * Notifies that time tick alarm from doze service fired. - * - * Implement this method instead of registering a broadcast listener for TIME_TICK. - */ - default void onTimeTick() {} - - /** - * Notifies that the time zone has changed. - * - * Implement this method instead of registering a broadcast listener for TIME_ZONE_CHANGED. - */ - default void onTimeZoneChanged(TimeZone timeZone) {} - - /** - * Notifies that the time format has changed. - * - * @param timeFormat "12" for 12-hour format, "24" for 24-hour format - */ - default void onTimeFormatChanged(String timeFormat) {} - - /** - * Indicates whether the keyguard status area (date) should be shown below - * the clock. - */ - default boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt index a4b1ceeebfa3..f83fa33caa04 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt @@ -2,14 +2,18 @@ package com.android.systemui.plugins import android.os.Bundle import android.util.Log +import android.view.View import androidx.annotation.VisibleForTesting +typealias WeatherTouchAction = (View) -> Unit + class WeatherData constructor( val description: String, val state: WeatherStateIcon, val useCelsius: Boolean, val temperature: Int, + val touchAction: WeatherTouchAction? = null, ) { companion object { const val DEBUG = true @@ -20,7 +24,7 @@ constructor( @VisibleForTesting const val TEMPERATURE_KEY = "temperature" private const val INVALID_WEATHER_ICON_STATE = -1 - fun fromBundle(extras: Bundle): WeatherData? { + fun fromBundle(extras: Bundle, touchAction: WeatherTouchAction? = null): WeatherData? { val description = extras.getString(DESCRIPTION_KEY) val state = WeatherStateIcon.fromInt(extras.getInt(STATE_KEY, INVALID_WEATHER_ICON_STATE)) @@ -41,7 +45,8 @@ constructor( description = description, state = state, useCelsius = extras.getBoolean(USE_CELSIUS_KEY), - temperature = temperature + temperature = temperature, + touchAction = touchAction ) if (DEBUG) { Log.i(TAG, "Weather data parsed $result from $extras") diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 28f5f3d9edd7..badad584824b 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -358,12 +358,12 @@ <!-- Message shown to prepare for an unattended update (OTA). Also known as an over-the-air (OTA) update. [CHAR LIMIT=70] --> <string name="kg_prompt_unattended_update_pattern">Draw pattern to install update later</string> - <!-- Message shown after an unattended update (OTA) asking the user to enter their PIN. Also known as an over-the-air (OTA) update. [CHAR LIMIT=70] --> + <!-- Message shown after an unattended mainline (major) update asking the user to enter their PIN. [CHAR LIMIT=70] --> <string name="kg_prompt_after_update_pin">Device updated. Enter PIN to continue.</string> - <!-- Message shown after an unattended update (OTA) asking the user to enter their password. Also known as an over-the-air (OTA) update. [CHAR LIMIT=70] --> + <!-- Message shown after an unattended mainline (major) update asking the user to enter their password. [CHAR LIMIT=70] --> <string name="kg_prompt_after_update_password">Device updated. Enter password to continue.</string> - <!-- Message shown after an unattended update (OTA) asking the user to enter their pattern. Also known as an over-the-air (OTA) update. [CHAR LIMIT=70] --> + <!-- Message shown after an unattended mainline (major) update asking the user to enter their pattern. [CHAR LIMIT=70] --> <string name="kg_prompt_after_update_pattern">Device updated. Draw pattern to continue.</string> </resources> diff --git a/packages/SystemUI/res/drawable/android14_patch_adaptive.xml b/packages/SystemUI/res/drawable/android14_patch_adaptive.xml new file mode 100644 index 000000000000..423e35146c24 --- /dev/null +++ b/packages/SystemUI/res/drawable/android14_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android14_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android14_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android14_patch_monochrome"/> +</adaptive-icon>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/android14_patch_adaptive_background.xml b/packages/SystemUI/res/drawable/android14_patch_adaptive_background.xml new file mode 100644 index 000000000000..afeae4bd868c --- /dev/null +++ b/packages/SystemUI/res/drawable/android14_patch_adaptive_background.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:pathData="M0,0 L108,0 L108,108 L0,108 z" + android:fillColor="@android:color/system_neutral1_800"/> + <path + android:pathData="M44.51,43.32L44.86,42.27C47.04,54.48 52.81,86.71 52.81,50.14C52.81,49.99 52.92,49.86 53.06,49.86H55.04C55.18,49.86 55.3,49.98 55.3,50.14C55.27,114.18 44.51,43.32 44.51,43.32Z" + android:fillColor="@android:color/system_accent3_400"/> + <path + android:name="planetary head" + android:pathData="M38.81,42.23L33.63,51.21C33.33,51.72 33.51,52.38 34.02,52.68C34.54,52.98 35.2,52.8 35.49,52.28L40.74,43.2C49.22,47 58.92,47 67.4,43.2L72.65,52.28C72.96,52.79 73.62,52.96 74.13,52.65C74.62,52.35 74.79,51.71 74.51,51.21L69.33,42.23C78.23,37.39 84.32,28.38 85.21,17.74H22.93C23.82,28.38 29.91,37.39 38.81,42.23Z" + android:fillColor="#ffffff"/> + <!-- yes it's an easter egg in a vector drawable --> + <path + android:name="planetary body" + android:pathData="M22.9,0 L85.1,0 L85.1,15.5 L22.9,15.5 z" + android:fillColor="#ffffff" /> + <path + android:pathData="M54.96,43.32H53.1C52.92,43.32 52.77,43.47 52.77,43.65V48.04C52.77,48.22 52.92,48.37 53.1,48.37H54.96C55.15,48.37 55.3,48.22 55.3,48.04V43.65C55.3,43.47 55.15,43.32 54.96,43.32Z" + android:fillColor="@android:color/system_accent3_400"/> + <path + android:pathData="M54.99,40.61H53.08C52.91,40.61 52.77,40.75 52.77,40.92V41.56C52.77,41.73 52.91,41.87 53.08,41.87H54.99C55.16,41.87 55.3,41.73 55.3,41.56V40.92C55.3,40.75 55.16,40.61 54.99,40.61Z" + android:fillColor="@android:color/system_accent3_400"/> + <path + android:pathData="M41.49,47.88H40.86V48.51H41.49V47.88Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M44.13,57.08H43.5V57.71H44.13V57.08Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72.29,66.76H71.66V67.39H72.29V66.76Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M59.31,53.41H58.68V54.04H59.31V53.41Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M64.47,48.19H63.84V48.83H64.47V48.19Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M60.58,59.09H59.95V59.72H60.58V59.09Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M66.95,56.7H65.69V57.97H66.95V56.7Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M44.13,60.71H43.5V61.34H44.13V60.71Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M49.66,51.33H48.4V52.6H49.66V51.33Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M57.78,63.83H56.52V65.09H57.78V63.83Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M61.1,68.57H59.83V69.83H61.1V68.57Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M40.43,53.73H39.16V54.99H40.43V53.73Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M74.47,44H73.21V45.26H74.47V44Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M36.8,64.58H35.54V65.84H36.8V64.58Z" + android:fillColor="#ffffff"/> +</vector> diff --git a/packages/SystemUI/res/drawable/android14_patch_adaptive_foreground.xml b/packages/SystemUI/res/drawable/android14_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..ced43056d08f --- /dev/null +++ b/packages/SystemUI/res/drawable/android14_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <path + android:pathData="M54.03,33.03C52.99,33.03 52.14,33.86 52.14,34.87V37.14C52.14,37.34 52.3,37.5 52.5,37.5C52.69,37.5 52.85,37.34 52.85,37.14V36.53C52.85,36.14 53.17,35.82 53.56,35.82H54.51C54.9,35.82 55.22,36.14 55.22,36.53V37.14C55.22,37.34 55.38,37.5 55.57,37.5C55.77,37.5 55.93,37.34 55.93,37.14V34.87C55.93,33.86 55.08,33.03 54.03,33.03H54.03Z" + android:fillColor="@android:color/system_accent3_400"/> + <path + android:pathData="M108,0H0V108H108V0ZM54,80.67C68.73,80.67 80.67,68.73 80.67,54C80.67,39.27 68.73,27.33 54,27.33C39.27,27.33 27.33,39.27 27.33,54C27.33,68.73 39.27,80.67 54,80.67Z" + android:fillColor="@android:color/system_accent1_400" + android:fillType="evenOdd"/> + <group> + <!-- the text doesn't look great everywhere but you can remove the clip to try it out. --> + <clip-path /> + <path + android:pathData="M28.58,32.18L29.18,31.5L33.82,33.02L33.12,33.81L32.15,33.48L30.92,34.87L31.37,35.8L30.68,36.58L28.58,32.18L28.58,32.18ZM31.25,33.18L29.87,32.71L30.51,34.02L31.25,33.18V33.18Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M38,29.76L34.61,28.79L36.23,31.04L35.42,31.62L32.8,27.99L33.5,27.48L36.88,28.45L35.26,26.21L36.08,25.62L38.7,29.25L38,29.76Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39.23,23.87L40.63,23.27C41.79,22.77 43.13,23.28 43.62,24.43C44.11,25.57 43.56,26.89 42.4,27.39L40.99,27.99L39.23,23.87ZM42.03,26.54C42.73,26.24 42.96,25.49 42.68,24.83C42.4,24.17 41.69,23.82 41,24.11L40.51,24.32L41.55,26.75L42.03,26.54Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M45.91,21.43L47.64,21.09C48.47,20.93 49.12,21.41 49.27,22.15C49.38,22.72 49.15,23.14 48.63,23.45L50.57,25.08L49.39,25.31L47.57,23.79L47.41,23.82L47.76,25.63L46.78,25.83L45.91,21.43H45.91ZM47.87,22.86C48.16,22.8 48.34,22.59 48.29,22.34C48.24,22.07 48,21.96 47.71,22.02L47.07,22.14L47.24,22.98L47.87,22.86Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M52.17,22.69C52.19,21.41 53.24,20.39 54.52,20.41C55.8,20.43 56.82,21.49 56.8,22.76C56.78,24.04 55.72,25.06 54.45,25.04C53.17,25.02 52.15,23.96 52.17,22.69ZM55.79,22.75C55.8,22.02 55.23,21.39 54.51,21.38C53.78,21.37 53.19,21.98 53.18,22.7C53.17,23.43 53.73,24.06 54.47,24.07C55.19,24.08 55.78,23.47 55.79,22.75H55.79Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M60,21.01L60.98,21.2L60.12,25.6L59.14,25.41L60,21.01Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M64.3,22.03L65.73,22.58C66.91,23.03 67.51,24.32 67.07,25.49C66.62,26.65 65.31,27.22 64.13,26.77L62.71,26.22L64.3,22.03L64.3,22.03ZM64.46,25.9C65.17,26.17 65.86,25.8 66.12,25.12C66.37,24.45 66.11,23.71 65.4,23.44L64.91,23.25L63.97,25.72L64.46,25.9Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M73.59,27.94L72.94,27.44L73.51,26.69L74.92,27.77L72.2,31.34L71.45,30.76L73.59,27.94Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76.18,33.75L74.69,32.14L75.25,31.62L78.81,31.42L79.4,32.05L77.47,33.85L77.86,34.27L77.22,34.86L76.83,34.44L76.12,35.11L75.47,34.41L76.18,33.75ZM77.72,32.31L76.12,32.4L76.82,33.15L77.72,32.31Z" + android:fillColor="#ffffff"/> + </group> +</vector> diff --git a/packages/SystemUI/res/drawable/android14_patch_monochrome.xml b/packages/SystemUI/res/drawable/android14_patch_monochrome.xml new file mode 100644 index 000000000000..beef85ce3b3f --- /dev/null +++ b/packages/SystemUI/res/drawable/android14_patch_monochrome.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <clip-path + android:pathData="M0,0h108v108h-108z"/> + <group> + <clip-path + android:pathData="M22,22h64v64h-64z"/> + <path + android:pathData="M54,78C67.25,78 78,67.25 78,54C78,40.75 67.25,30 54,30C40.75,30 30,40.75 30,54C30,67.25 40.75,78 54,78Z" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000"/> + <group> + <clip-path + android:pathData="M77.5,54C77.5,66.98 66.98,77.5 54,77.5C41.02,77.5 30.5,66.98 30.5,54C30.5,41.02 41.02,30.5 54,30.5C66.98,30.5 77.5,41.02 77.5,54Z"/> + <path + android:pathData="M61.5,46.06C56.7,47.89 51.4,47.89 46.61,46.06L44.04,50.51C43.49,51.46 42.28,51.79 41.33,51.24C40.39,50.69 40.06,49.48 40.61,48.53L43.06,44.28C37.97,41.03 34.54,35.56 34,29.19L33.88,27.74H74.22L74.1,29.19C73.57,35.56 70.14,41.03 65.04,44.28L67.51,48.56C68.03,49.49 67.71,50.66 66.8,51.21C65.87,51.77 64.65,51.47 64.08,50.54L64.07,50.51L61.5,46.06Z" + android:fillColor="#000000"/> + </group> + <path + android:pathData="M51.33,67.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M48.67,62h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M56.67,70h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M56.67,62h2.67v2.67h-2.67z" + android:fillColor="#000000"/> + <path + android:pathData="M67.33,62h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M59.33,51.33h2.67v2.67h-2.67z" + android:fillColor="#000000"/> + <path + android:pathData="M62,59.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M70,54h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M35.33,56.67h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M35.33,48.67h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M40.67,59.33h2.67v2.67h-2.67z" + android:fillColor="#000000"/> + <path + android:pathData="M46,51.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M43.33,67.33h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + <path + android:pathData="M54,54h1.33v1.33h-1.33z" + android:fillColor="#000000"/> + </group> + </group> +</vector> diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 386c9d66a0c1..665c6127e06d 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -74,7 +74,7 @@ frame when animating QS <-> QQS transition android:layout_height="wrap_content" android:id="@+id/barrier" app:barrierDirection="start" - app:constraint_referenced_ids="statusIcons,privacy_container" /> + app:constraint_referenced_ids="shade_header_system_icons,privacy_container" /> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" @@ -108,46 +108,39 @@ frame when animating QS <-> QQS transition <include android:id="@+id/carrier_group" layout="@layout/shade_carrier_group" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - android:minHeight="@dimen/large_screen_shade_header_min_height" - app:layout_constraintWidth_min="48dp" android:layout_width="0dp" android:layout_height="0dp" - app:layout_constrainedWidth="true" android:layout_gravity="end|center_vertical" android:layout_marginStart="8dp" - app:layout_constraintStart_toEndOf="@id/date" - app:layout_constraintEnd_toStartOf="@id/statusIcons" - app:layout_constraintTop_toTopOf="@id/clock" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/shade_header_system_icons" app:layout_constraintHorizontal_bias="1" - /> + app:layout_constraintStart_toEndOf="@id/date" + app:layout_constraintTop_toTopOf="@id/clock" /> - <com.android.systemui.statusbar.phone.StatusIconContainer - android:id="@+id/statusIcons" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - android:paddingEnd="@dimen/signal_cluster_battery_padding" + <LinearLayout + android:id="@+id/shade_header_system_icons" android:layout_width="wrap_content" + app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" android:layout_height="@dimen/large_screen_shade_header_min_height" - app:layout_constraintStart_toEndOf="@id/carrier_group" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" - app:layout_constraintTop_toTopOf="@id/clock" + android:clickable="true" + android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintHorizontal_bias="1" - /> + app:layout_constraintEnd_toEndOf="@id/privacy_container" + app:layout_constraintTop_toTopOf="@id/clock"> - <com.android.systemui.battery.BatteryMeterView - android:id="@+id/batteryRemainingIcon" - android:layout_width="wrap_content" - android:layout_height="@dimen/large_screen_shade_header_min_height" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - app:layout_constrainedWidth="true" - app:textAppearance="@style/TextAppearance.QS.Status" - app:layout_constraintStart_toEndOf="@id/statusIcons" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/clock" - app:layout_constraintBottom_toBottomOf="parent" - /> + <com.android.systemui.statusbar.phone.StatusIconContainer + android:id="@+id/statusIcons" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingEnd="@dimen/signal_cluster_battery_padding" /> + + <com.android.systemui.battery.BatteryMeterView + android:id="@+id/batteryRemainingIcon" + android:layout_width="wrap_content" + android:layout_height="match_parent" + app:textAppearance="@style/TextAppearance.QS.Status" /> + </LinearLayout> <FrameLayout android:id="@+id/privacy_container" diff --git a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml index a2250b1975a3..0fd3c5b16435 100644 --- a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml +++ b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml @@ -17,7 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_height="@dimen/qs_tile_height" + android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_marginTop="@dimen/qs_tile_margin_top_bottom" android:layout_marginBottom="@dimen/qs_tile_margin_top_bottom" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index d7106762d17b..e21466671363 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -132,21 +132,6 @@ android:id="@+id/lock_icon_view" android:layout_width="wrap_content" android:layout_height="wrap_content"> - <!-- Background protection --> - <ImageView - android:id="@+id/lock_icon_bg" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:src="@drawable/fingerprint_bg" - android:visibility="invisible"/> - - <ImageView - android:id="@+id/lock_icon" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:scaleType="centerCrop"/> - </com.android.keyguard.LockIconView> <include diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 6601e63fa034..2fde9479d42a 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -65,17 +65,17 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - <include layout="@layout/status_bar_expanded" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="invisible" /> - <!-- 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" /> + <include layout="@layout/status_bar_expanded" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="invisible" /> + <include layout="@layout/brightness_mirror_container" /> <com.android.systemui.scrim.ScrimView diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index f277e8a6f02f..da4547b733df 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -76,4 +76,10 @@ <!-- Bouncer user switcher margins --> <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">0dp</dimen> <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">0dp</dimen> + + <!-- Power Menu Lite --> + <!-- These values are for small screen landscape. For larger landscape screens, they are + overlaid --> + <dimen name="global_actions_button_size">72dp</dimen> + <dimen name="global_actions_button_padding">26dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw410dp-land/dimens.xml b/packages/SystemUI/res/values-sw410dp-land/dimens.xml index c4d9b9b92f57..60456062aea1 100644 --- a/packages/SystemUI/res/values-sw410dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw410dp-land/dimens.xml @@ -18,4 +18,8 @@ <!-- Lock pattern view size, align sysui biometric_auth_pattern_view_size --> <dimen name="biometric_auth_pattern_view_size">248dp</dimen> <dimen name="biometric_auth_pattern_view_max_size">348dp</dimen> + + <!-- Power Menu Lite --> + <dimen name="global_actions_button_size">96dp</dimen> + <dimen name="global_actions_button_padding">38dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index d651a2159721..134a7a96bbc3 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -211,4 +211,6 @@ <item type="id" name="keyguard_indication_area" /> <item type="id" name="keyguard_indication_text" /> <item type="id" name="keyguard_indication_text_bottom" /> + <item type="id" name="lock_icon" /> + <item type="id" name="lock_icon_bg" /> </resources> diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml index 38c164023315..c16725682a82 100644 --- a/packages/SystemUI/res/xml/combined_qs_header_scene.xml +++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml @@ -16,12 +16,14 @@ --> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition android:id="@+id/header_transition" app:constraintSetEnd="@id/qs_header_constraint" - app:constraintSetStart="@id/qqs_header_constraint"> + app:constraintSetStart="@id/qqs_header_constraint" + motion:layoutDuringTransition="honorRequest"> <KeyFrameSet> <!-- These positions are to prevent visual movement of @id/date --> <KeyPosition @@ -51,56 +53,28 @@ android:alpha="0" /> <KeyPosition + app:motionTarget="@id/shade_header_system_icons" app:keyPositionType="deltaRelative" app:percentX="0" app:percentY="@dimen/percent_displacement_at_fade_out" app:framePosition="@integer/fade_out_complete_frame" app:sizePercent="0" - app:curveFit="linear" - app:motionTarget="@id/statusIcons" /> + app:curveFit="linear" /> <KeyPosition + app:motionTarget="@id/shade_header_system_icons" app:keyPositionType="deltaRelative" app:percentX="1" app:percentY="0.5" app:framePosition="50" app:sizePercent="1" - app:curveFit="linear" - app:motionTarget="@id/statusIcons" /> - <KeyAttribute - app:motionTarget="@id/statusIcons" - app:framePosition="@integer/fade_out_complete_frame" - android:alpha="0" - /> - <KeyAttribute - app:motionTarget="@id/statusIcons" - app:framePosition="@integer/fade_in_start_frame" - android:alpha="0" - /> - <KeyPosition - app:keyPositionType="deltaRelative" - app:percentX="0" - app:percentY="@dimen/percent_displacement_at_fade_out" - app:framePosition="@integer/fade_out_complete_frame" - app:percentWidth="1" - app:percentHeight="1" - app:curveFit="linear" - app:motionTarget="@id/batteryRemainingIcon" /> - <KeyPosition - app:keyPositionType="deltaRelative" - app:percentX="1" - app:percentY="0.5" - app:framePosition="50" - app:percentWidth="1" - app:percentHeight="1" - app:curveFit="linear" - app:motionTarget="@id/batteryRemainingIcon" /> + app:curveFit="linear" /> <KeyAttribute - app:motionTarget="@id/batteryRemainingIcon" + app:motionTarget="@id/shade_header_system_icons" app:framePosition="@integer/fade_out_complete_frame" android:alpha="0" /> <KeyAttribute - app:motionTarget="@id/batteryRemainingIcon" + app:motionTarget="@id/shade_header_system_icons" app:framePosition="@integer/fade_in_start_frame" android:alpha="0" /> diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml index bf576dc5790b..39f4c81b6dbe 100644 --- a/packages/SystemUI/res/xml/large_screen_shade_header.xml +++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml @@ -45,7 +45,7 @@ android:layout_height="0dp" android:layout_gravity="end|center_vertical" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/statusIcons" + app:layout_constraintEnd_toStartOf="@id/shade_header_system_icons" app:layout_constraintStart_toEndOf="@id/date" app:layout_constraintTop_toTopOf="parent" app:layout_constraintWidth_default="wrap" @@ -53,28 +53,17 @@ <PropertySet android:alpha="1" /> </Constraint> - <Constraint android:id="@+id/statusIcons"> + <Constraint android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="wrap_content" android:layout_height="@dimen/large_screen_shade_header_min_height" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" + app:layout_constraintEnd_toStartOf="@id/privacy_container" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="@id/carrier_group"/> <PropertySet android:alpha="1" /> </Constraint> - <Constraint android:id="@+id/batteryRemainingIcon"> - <Layout - android:layout_width="wrap_content" - android:layout_height="0dp" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/privacy_container" - app:layout_constraintTop_toTopOf="parent" /> - <PropertySet android:alpha="1" /> - </Constraint> - <Constraint android:id="@+id/privacy_container"> <Layout android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml index 1950965fc298..50a388d0fa64 100644 --- a/packages/SystemUI/res/xml/qqs_header.xml +++ b/packages/SystemUI/res/xml/qqs_header.xml @@ -54,27 +54,12 @@ </Constraint> <Constraint - android:id="@+id/statusIcons"> + android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="wrap_content" android:layout_height="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintStart_toEndOf="@id/date" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" - app:layout_constraintHorizontal_bias="1" - app:layout_constraintHorizontal_chainStyle="packed" - /> - </Constraint> - - <Constraint - android:id="@+id/batteryRemainingIcon"> - <Layout - android:layout_width="wrap_content" - android:layout_height="@dimen/new_qs_header_non_clickable_element_height" - app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height" - app:layout_constraintStart_toEndOf="@id/statusIcons" app:layout_constraintEnd_toEndOf="@id/end_guide" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml index 8039c68485ca..7b4282f049b8 100644 --- a/packages/SystemUI/res/xml/qs_header.xml +++ b/packages/SystemUI/res/xml/qs_header.xml @@ -59,7 +59,7 @@ /> </Constraint> - <!-- LargeScreenShadeHeaderController helps with managing clock width to layout this view --> + <!-- ShadeHeaderController helps with managing clock width to layout this view --> <Constraint android:id="@+id/carrier_group"> <Layout @@ -78,25 +78,11 @@ </Constraint> <Constraint - android:id="@+id/statusIcons"> + android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="0dp" android:layout_height="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintWidth_default="wrap" - app:layout_constraintStart_toEndOf="@id/date" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" - app:layout_constraintTop_toTopOf="@id/date" - app:layout_constraintBottom_toBottomOf="@id/date" - /> - </Constraint> - - <Constraint - android:id="@+id/batteryRemainingIcon"> - <Layout - android:layout_width="0dp" - android:layout_height="@dimen/new_qs_header_non_clickable_element_height" - app:layout_constraintWidth_default="wrap" - app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/date" app:layout_constraintBottom_toBottomOf="@id/date" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 751a3f8458bd..2e6c485336f3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -104,7 +104,8 @@ public class Utilities { * @return updated set of flags from InputMethodService based off {@param oldHints} * Leaves original hints unmodified */ - public static int calculateBackDispositionHints(int oldHints, int backDisposition, + public static int calculateBackDispositionHints(int oldHints, + @InputMethodService.BackDispositionMode int backDisposition, boolean imeShown, boolean showImeSwitcher) { int hints = oldHints; switch (backDisposition) { diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java index a724514f6ec9..b52ee01bfa61 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java @@ -148,7 +148,8 @@ public class CarrierTextManager { /** * The status of this lock screen. Primarily used for widgets on LockScreen. */ - private enum StatusMode { + @VisibleForTesting + protected enum StatusMode { Normal, // Normal case (sim card present, it's not locked) NetworkLocked, // SIM card is 'network locked'. SimMissing, // SIM card is missing. @@ -158,6 +159,7 @@ public class CarrierTextManager { SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM. SimIoError, // SIM card is faulty + SimRestricted, // SIM Card restricted, present but not usable due to carrier restrictions. SimUnknown // SIM card is unknown } @@ -493,6 +495,7 @@ public class CarrierTextManager { getContext().getText(R.string.keyguard_sim_error_message_short), text); break; + case SimRestricted: // fall through case SimUnknown: carrierText = null; break; @@ -535,19 +538,19 @@ public class CarrierTextManager { /** * Determine the current status of the lock screen given the SIM state and other stuff. */ - private CarrierTextManager.StatusMode getStatusForIccState(int simState) { - final boolean missingAndNotProvisioned = - !mKeyguardUpdateMonitor.isDeviceProvisioned() - && (simState == TelephonyManager.SIM_STATE_ABSENT - || simState == TelephonyManager.SIM_STATE_PERM_DISABLED); - - // Assume we're NETWORK_LOCKED if not provisioned - simState = missingAndNotProvisioned ? TelephonyManager.SIM_STATE_NETWORK_LOCKED : simState; + @VisibleForTesting + protected CarrierTextManager.StatusMode getStatusForIccState(int simState) { + if (!mKeyguardUpdateMonitor.isDeviceProvisioned() + && (simState == TelephonyManager.SIM_STATE_ABSENT + || simState == TelephonyManager.SIM_STATE_PERM_DISABLED)) { + return CarrierTextManager.StatusMode.SimMissingLocked; + } + switch (simState) { case TelephonyManager.SIM_STATE_ABSENT: return CarrierTextManager.StatusMode.SimMissing; case TelephonyManager.SIM_STATE_NETWORK_LOCKED: - return CarrierTextManager.StatusMode.SimMissingLocked; + return CarrierTextManager.StatusMode.NetworkLocked; case TelephonyManager.SIM_STATE_NOT_READY: return CarrierTextManager.StatusMode.SimNotReady; case TelephonyManager.SIM_STATE_PIN_REQUIRED: @@ -562,6 +565,8 @@ public class CarrierTextManager { return CarrierTextManager.StatusMode.SimUnknown; case TelephonyManager.SIM_STATE_CARD_IO_ERROR: return CarrierTextManager.StatusMode.SimIoError; + case TelephonyManager.SIM_STATE_CARD_RESTRICTED: + return CarrierTextManager.StatusMode.SimRestricted; } return CarrierTextManager.StatusMode.SimUnknown; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index 7acfbf64ce02..bb112175ded7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -53,6 +53,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey private boolean mDismissing; protected AsyncTask<?, ?, ?> mPendingLockCheck; protected boolean mResumed; + protected boolean mLockedOut; private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> { // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN. @@ -137,6 +138,8 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey // Prevent user from using the PIN/Password entry until scheduled deadline. protected void handleAttemptLockout(long elapsedRealtimeDeadline) { mView.setPasswordEntryEnabled(false); + mView.setPasswordEntryInputEnabled(false); + mLockedOut = true; long elapsedRealtime = SystemClock.elapsedRealtime(); long secondsInFuture = (long) Math.ceil( (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); @@ -159,6 +162,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey @Override public void onFinish() { mMessageAreaController.setMessage(""); + mLockedOut = false; resetState(); } }.start(); @@ -194,6 +198,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey protected void verifyPasswordAndUnlock() { if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. + if (mLockedOut) return; final LockscreenCredential password = mView.getEnteredCredential(); mView.setPasswordEntryInputEnabled(false); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 7a0a24a85eaf..03d9eb3455fd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -23,6 +23,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; @@ -99,6 +100,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { switch (reason) { case PROMPT_REASON_RESTART: return R.string.kg_prompt_reason_restart_password; + case PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE: + return R.string.kg_prompt_after_update_password; case PROMPT_REASON_TIMEOUT: return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_DEVICE_ADMIN: @@ -106,7 +109,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { case PROMPT_REASON_USER_REQUEST: return R.string.kg_prompt_after_user_lockdown_password; case PROMPT_REASON_PREPARE_FOR_UPDATE: - return R.string.kg_prompt_reason_timeout_password; + return R.string.kg_prompt_unattended_update_password; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_TRUSTAGENT_EXPIRED: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index ffb28b05e700..49f788ce8f75 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -236,6 +236,7 @@ public class KeyguardPatternViewController getKeyguardSecurityCallback().onCancelClicked(); }); } + mView.onDevicePostureChanged(mPostureController.getDevicePosture()); mPostureController.addCallback(mPostureCallback); } @@ -304,6 +305,9 @@ public class KeyguardPatternViewController case PROMPT_REASON_RESTART: resId = R.string.kg_prompt_reason_restart_pattern; break; + case PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE: + resId = R.string.kg_prompt_after_update_pattern; + break; case PROMPT_REASON_TIMEOUT: resId = R.string.kg_prompt_reason_timeout_pattern; break; @@ -314,7 +318,7 @@ public class KeyguardPatternViewController resId = R.string.kg_prompt_after_user_lockdown_pattern; break; case PROMPT_REASON_PREPARE_FOR_UPDATE: - resId = R.string.kg_prompt_reason_timeout_pattern; + resId = R.string.kg_prompt_unattended_update_pattern; break; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: resId = R.string.kg_prompt_reason_timeout_pattern; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 687436c5d388..38e5dc57d316 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -21,6 +21,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; @@ -113,6 +114,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView switch (reason) { case PROMPT_REASON_RESTART: return R.string.kg_prompt_reason_restart_pin; + case PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE: + return R.string.kg_prompt_after_update_pin; case PROMPT_REASON_TIMEOUT: return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_DEVICE_ADMIN: @@ -120,7 +123,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView case PROMPT_REASON_USER_REQUEST: return R.string.kg_prompt_after_user_lockdown_pin; case PROMPT_REASON_PREPARE_FOR_UPDATE: - return R.string.kg_prompt_reason_timeout_pin; + return R.string.kg_prompt_unattended_update_pin; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_TRUSTAGENT_EXPIRED: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 4211f55787d9..841b5b3a1e82 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -474,7 +474,8 @@ public class KeyguardSecurityContainer extends ConstraintLayout { return false; } // Avoid dragging the pattern view - if (mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) { + if (mSecurityViewFlipper.getSecurityView() != null + && mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) { return false; } int index = event.findPointerIndex(mActivePointerId); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index 419303d71f97..21960e219fc9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -103,6 +103,12 @@ public interface KeyguardSecurityView { int PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT = 15; /** + * Strong auth is required because the device has just booted because of an automatic + * mainline update. + */ + int PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE = 16; + + /** * Reset the view and prepare to take input. This should do things like clearing the * password or pattern and clear error messages. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 074eb74d3ea9..4b3f2814e410 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2033,11 +2033,47 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>(); /** - * When we receive a - * {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast, + * When we receive a {@link android.content.Intent#ACTION_SIM_STATE_CHANGED} broadcast, * and then pass a result via our handler to {@link KeyguardUpdateMonitor#handleSimStateChange}, * we need a single object to pass to the handler. This class helps decode * the intent and provide a {@link SimData} result. + * + * Below is the Sim state mapping matrixs: + * +---+-----------------------------------------------------+----------------------------+ + * | |Telephony FWK broadcast with action |SystemUI mapping SIM state | + * | |android.content.Intent#ACTION_SIM_STATE_CHANGED |refer to android.telephony. | + * |NO.+-------------------------+---------------------------+TelephonyManager#getSimState| + * | |EXTRA_SIM_STATE |EXTRA_SIM_LOCKED_REASON | | + * | |(Intent#XXX) |(Intent#XXX) |TelephonyManager#SimState | + * +===+=====================================================+============================+ + * |1 |SIM_STATE_UNKNOWN |always null |SIM_STATE_UNKNOWN | + * +---+-------------------------+---------------------------+----------------------------+ + * |2 |SIM_STATE_ABSENT |always null |SIM_STATE_ABSENT | + * +---+-------------------------+---------------------------+----------------------------+ + * |3 |SIM_STATE_CARD_IO_ERROR |SIM_STATE_CARD_IO_ERROR |SIM_STATE_CARD_IO_ERROR | + * +---+-------------------------+---------------------------+----------------------------+ + * |4 |SIM_STATE_CARD_RESTRICTED|SIM_STATE_CARD_RESTRICTED |SIM_STATE_CARD_RESTRICTED | + * +---+-------------------------+---------------------------+----------------------------+ + * |5 |SIM_STATE_LOCKED |SIM_LOCKED_ON_PIN |SIM_STATE_PIN_REQUIRED | + * +---+-------------------------+---------------------------+----------------------------+ + * |6 |SIM_STATE_LOCKED |SIM_LOCKED_ON_PUK |SIM_STATE_PUK_REQUIRED | + * +---+-------------------------+---------------------------+----------------------------+ + * |7 |SIM_STATE_LOCKED |SIM_LOCKED_NETWORK |SIM_STATE_NETWORK_LOCKED | + * +---+-------------------------+---------------------------+----------------------------+ + * |8 |SIM_STATE_LOCKED |SIM_ABSENT_ON_PERM_DISABLED|SIM_STATE_PERM_DISABLED | + * +---+-------------------------+---------------------------+----------------------------+ + * |9 |SIM_STATE_NOT_READY |always null |SIM_STATE_NOT_READY | + * +---+-------------------------+---------------------------+----------------------------+ + * |10 |SIM_STATE_IMSI |always null |SIM_STATE_READY | + * +---+-------------------------+---------------------------+----------------------------+ + * |11 |SIM_STATE_READY |always null |SIM_STATE_READY | + * +---+-------------------------+---------------------------+----------------------------+ + * |12 |SIM_STATE_LOADED |always null |SIM_STATE_READY | + * +---+-------------------------+---------------------------+----------------------------+ + * + * Note that, it seems #10 imsi ready case(i.e. SIM_STATE_IMSI) is never triggered from + * Android Pie(telephony FWK doesn't trigger this broadcast any more), but it is still + * OK keep this mapping logic. */ private static class SimData { public int simState; @@ -2051,26 +2087,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } static SimData fromIntent(Intent intent) { - int state; if (!Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED"); } + int state = TelephonyManager.SIM_STATE_UNKNOWN; String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE); int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0); int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SubscriptionManager.INVALID_SUBSCRIPTION_ID); if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) { - final String absentReason = intent - .getStringExtra(Intent.EXTRA_SIM_LOCKED_REASON); - - if (Intent.SIM_ABSENT_ON_PERM_DISABLED.equals( - absentReason)) { - state = TelephonyManager.SIM_STATE_PERM_DISABLED; - } else { - state = TelephonyManager.SIM_STATE_ABSENT; - } - } else if (Intent.SIM_STATE_READY.equals(stateExtra)) { - state = TelephonyManager.SIM_STATE_READY; + state = TelephonyManager.SIM_STATE_ABSENT; } else if (Intent.SIM_STATE_LOCKED.equals(stateExtra)) { final String lockedReason = intent .getStringExtra(Intent.EXTRA_SIM_LOCKED_REASON); @@ -2078,20 +2104,24 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab state = TelephonyManager.SIM_STATE_PIN_REQUIRED; } else if (Intent.SIM_LOCKED_ON_PUK.equals(lockedReason)) { state = TelephonyManager.SIM_STATE_PUK_REQUIRED; - } else { - state = TelephonyManager.SIM_STATE_UNKNOWN; + } else if (Intent.SIM_LOCKED_NETWORK.equals(lockedReason)) { + state = TelephonyManager.SIM_STATE_NETWORK_LOCKED; + } else if (Intent.SIM_ABSENT_ON_PERM_DISABLED.equals(lockedReason)) { + state = TelephonyManager.SIM_STATE_PERM_DISABLED; } - } else if (Intent.SIM_LOCKED_NETWORK.equals(stateExtra)) { - state = TelephonyManager.SIM_STATE_NETWORK_LOCKED; } else if (Intent.SIM_STATE_CARD_IO_ERROR.equals(stateExtra)) { state = TelephonyManager.SIM_STATE_CARD_IO_ERROR; - } else if (Intent.SIM_STATE_LOADED.equals(stateExtra) + } else if (Intent.SIM_STATE_CARD_RESTRICTED.equals(stateExtra)) { + state = TelephonyManager.SIM_STATE_CARD_RESTRICTED; + } else if (Intent.SIM_STATE_NOT_READY.equals(stateExtra)) { + state = TelephonyManager.SIM_STATE_NOT_READY; + } else if (Intent.SIM_STATE_READY.equals(stateExtra) + || Intent.SIM_STATE_LOADED.equals(stateExtra) || Intent.SIM_STATE_IMSI.equals(stateExtra)) { - // This is required because telephony doesn't return to "READY" after + // Mapping SIM_STATE_LOADED and SIM_STATE_IMSI to SIM_STATE_READY is required + // because telephony doesn't return to "READY" after // these state transitions. See bug 7197471. state = TelephonyManager.SIM_STATE_READY; - } else { - state = TelephonyManager.SIM_STATE_UNKNOWN; } return new SimData(state, slotId, subId); } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index abad0be637ca..d1fffaa926ea 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; @@ -23,6 +25,7 @@ import android.graphics.Point; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; @@ -68,13 +71,9 @@ public class LockIconView extends FrameLayout implements Dumpable { public LockIconView(Context context, AttributeSet attrs) { super(context, attrs); mSensorRect = new RectF(); - } - @Override - public void onFinishInflate() { - super.onFinishInflate(); - mLockIcon = findViewById(R.id.lock_icon); - mBgView = findViewById(R.id.lock_icon_bg); + addBgImageView(context, attrs); + addLockIconImageView(context, attrs); } void setDozeAmount(float dozeAmount) { @@ -184,6 +183,30 @@ public class LockIconView extends FrameLayout implements Dumpable { mLockIcon.setImageState(getLockIconState(mIconType, mAod), true); } + private void addLockIconImageView(Context context, AttributeSet attrs) { + mLockIcon = new ImageView(context, attrs); + mLockIcon.setId(R.id.lock_icon); + mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP); + 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]; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java deleted file mode 100644 index 7517deed7cbb..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.app.WallpaperManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.Paint.Style; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextClock; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ClockPlugin; - -import java.util.TimeZone; - -/** - * Controller for Stretch clock that can appear on lock screen and AOD. - */ -public class AnalogClockController implements ClockPlugin { - - /** - * Resources used to get title and thumbnail. - */ - private final Resources mResources; - - /** - * LayoutInflater used to inflate custom clock views. - */ - private final LayoutInflater mLayoutInflater; - - /** - * Extracts accent color from wallpaper. - */ - private final SysuiColorExtractor mColorExtractor; - - /** - * Computes preferred position of clock. - */ - private final SmallClockPosition mClockPosition; - - /** - * Renders preview from clock view. - */ - private final ViewPreviewer mRenderer = new ViewPreviewer(); - - /** - * Custom clock shown on AOD screen and behind stack scroller on lock. - */ - private ClockLayout mBigClockView; - private ImageClock mAnalogClock; - - /** - * Small clock shown on lock screen above stack scroller. - */ - private View mView; - private TextClock mLockClock; - - /** - * Helper to extract colors from wallpaper palette for clock face. - */ - private final ClockPalette mPalette = new ClockPalette(); - - /** - * Create a BubbleClockController instance. - * - * @param res Resources contains title and thumbnail. - * @param inflater Inflater used to inflate custom clock views. - * @param colorExtractor Extracts accent color from wallpaper. - */ - public AnalogClockController(Resources res, LayoutInflater inflater, - SysuiColorExtractor colorExtractor) { - mResources = res; - mLayoutInflater = inflater; - mColorExtractor = colorExtractor; - mClockPosition = new SmallClockPosition(inflater.getContext()); - } - - private void createViews() { - mBigClockView = (ClockLayout) mLayoutInflater.inflate(R.layout.analog_clock, null); - mAnalogClock = mBigClockView.findViewById(R.id.analog_clock); - - mView = mLayoutInflater.inflate(R.layout.digital_clock, null); - mLockClock = mView.findViewById(R.id.lock_screen_clock); - } - - @Override - public void onDestroyView() { - mBigClockView = null; - mAnalogClock = null; - mView = null; - mLockClock = null; - } - - @Override - public String getName() { - return "analog"; - } - - @Override - public String getTitle() { - return mResources.getString(R.string.clock_title_analog); - } - - @Override - public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.analog_thumbnail); - } - - @Override - public Bitmap getPreview(int width, int height) { - - // Use the big clock view for the preview - View view = getBigClockView(); - - // Initialize state of plugin before generating preview. - setDarkAmount(1f); - setTextColor(Color.WHITE); - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK); - setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - onTimeTick(); - - return mRenderer.createPreview(view, width, height); - } - - @Override - public View getView() { - if (mView == null) { - createViews(); - } - return mView; - } - - @Override - public View getBigClockView() { - if (mBigClockView == null) { - createViews(); - } - return mBigClockView; - } - - @Override - public int getPreferredY(int totalHeight) { - return mClockPosition.getPreferredY(); - } - - @Override - public void setStyle(Style style) {} - - @Override - public void setTextColor(int color) { - updateColor(); - } - - @Override - public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { - mPalette.setColorPalette(supportsDarkText, colorPalette); - updateColor(); - } - - private void updateColor() { - final int primary = mPalette.getPrimaryColor(); - final int secondary = mPalette.getSecondaryColor(); - mLockClock.setTextColor(secondary); - mAnalogClock.setClockColors(primary, secondary); - } - - @Override - public void onTimeTick() { - mAnalogClock.onTimeChanged(); - mBigClockView.onTimeChanged(); - mLockClock.refreshTime(); - } - - @Override - public void setDarkAmount(float darkAmount) { - mPalette.setDarkAmount(darkAmount); - mClockPosition.setDarkAmount(darkAmount); - mBigClockView.setDarkAmount(darkAmount); - } - - @Override - public void onTimeZoneChanged(TimeZone timeZone) { - mAnalogClock.onTimeZoneChanged(timeZone); - } - - @Override - public boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java deleted file mode 100644 index 1add1a3abf5a..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.app.WallpaperManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.Paint.Style; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextClock; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ClockPlugin; - -import java.util.TimeZone; - -/** - * Controller for Bubble clock that can appear on lock screen and AOD. - */ -public class BubbleClockController implements ClockPlugin { - - /** - * Resources used to get title and thumbnail. - */ - private final Resources mResources; - - /** - * LayoutInflater used to inflate custom clock views. - */ - private final LayoutInflater mLayoutInflater; - - /** - * Extracts accent color from wallpaper. - */ - private final SysuiColorExtractor mColorExtractor; - - /** - * Computes preferred position of clock. - */ - private final SmallClockPosition mClockPosition; - - /** - * Renders preview from clock view. - */ - private final ViewPreviewer mRenderer = new ViewPreviewer(); - - /** - * Custom clock shown on AOD screen and behind stack scroller on lock. - */ - private ClockLayout mView; - private ImageClock mAnalogClock; - - /** - * Small clock shown on lock screen above stack scroller. - */ - private View mLockClockContainer; - private TextClock mLockClock; - - /** - * Helper to extract colors from wallpaper palette for clock face. - */ - private final ClockPalette mPalette = new ClockPalette(); - - /** - * Create a BubbleClockController instance. - * - * @param res Resources contains title and thumbnail. - * @param inflater Inflater used to inflate custom clock views. - * @param colorExtractor Extracts accent color from wallpaper. - */ - public BubbleClockController(Resources res, LayoutInflater inflater, - SysuiColorExtractor colorExtractor) { - mResources = res; - mLayoutInflater = inflater; - mColorExtractor = colorExtractor; - mClockPosition = new SmallClockPosition(inflater.getContext()); - } - - private void createViews() { - mView = (ClockLayout) mLayoutInflater.inflate(R.layout.bubble_clock, null); - mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock); - - mLockClockContainer = mLayoutInflater.inflate(R.layout.digital_clock, null); - mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock); - } - - @Override - public void onDestroyView() { - mView = null; - mAnalogClock = null; - mLockClockContainer = null; - mLockClock = null; - } - - @Override - public String getName() { - return "bubble"; - } - - @Override - public String getTitle() { - return mResources.getString(R.string.clock_title_bubble); - } - - @Override - public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.bubble_thumbnail); - } - - @Override - public Bitmap getPreview(int width, int height) { - - // Use the big clock view for the preview - View view = getBigClockView(); - - // Initialize state of plugin before generating preview. - setDarkAmount(1f); - setTextColor(Color.WHITE); - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK); - setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - onTimeTick(); - - return mRenderer.createPreview(view, width, height); - } - - @Override - public View getView() { - if (mLockClockContainer == null) { - createViews(); - } - return mLockClockContainer; - } - - @Override - public View getBigClockView() { - if (mView == null) { - createViews(); - } - return mView; - } - - @Override - public int getPreferredY(int totalHeight) { - return mClockPosition.getPreferredY(); - } - - @Override - public void setStyle(Style style) {} - - @Override - public void setTextColor(int color) { - updateColor(); - } - - @Override - public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { - mPalette.setColorPalette(supportsDarkText, colorPalette); - updateColor(); - } - - private void updateColor() { - final int primary = mPalette.getPrimaryColor(); - final int secondary = mPalette.getSecondaryColor(); - mLockClock.setTextColor(secondary); - mAnalogClock.setClockColors(primary, secondary); - } - - @Override - public void setDarkAmount(float darkAmount) { - mPalette.setDarkAmount(darkAmount); - mClockPosition.setDarkAmount(darkAmount); - mView.setDarkAmount(darkAmount); - } - - @Override - public void onTimeTick() { - mAnalogClock.onTimeChanged(); - mView.onTimeChanged(); - mLockClock.refreshTime(); - } - - @Override - public void onTimeZoneChanged(TimeZone timeZone) { - mAnalogClock.onTimeZoneChanged(timeZone); - } - - @Override - public boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java deleted file mode 100644 index 0210e08bb24c..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.graphics.Bitmap; - -import java.util.function.Supplier; - -/** - * Metadata about an available clock face. - */ -final class ClockInfo { - - private final String mName; - private final Supplier<String> mTitle; - private final String mId; - private final Supplier<Bitmap> mThumbnail; - private final Supplier<Bitmap> mPreview; - - private ClockInfo(String name, Supplier<String> title, String id, - Supplier<Bitmap> thumbnail, Supplier<Bitmap> preview) { - mName = name; - mTitle = title; - mId = id; - mThumbnail = thumbnail; - mPreview = preview; - } - - /** - * Gets the non-internationalized name for the clock face. - */ - String getName() { - return mName; - } - - /** - * Gets the name (title) of the clock face to be shown in the picker app. - */ - String getTitle() { - return mTitle.get(); - } - - /** - * Gets the ID of the clock face, used by the picker to set the current selection. - */ - String getId() { - return mId; - } - - /** - * Gets a thumbnail image of the clock. - */ - Bitmap getThumbnail() { - return mThumbnail.get(); - } - - /** - * Gets a potentially realistic preview image of the clock face. - */ - Bitmap getPreview() { - return mPreview.get(); - } - - static Builder builder() { - return new Builder(); - } - - static class Builder { - private String mName; - private Supplier<String> mTitle; - private String mId; - private Supplier<Bitmap> mThumbnail; - private Supplier<Bitmap> mPreview; - - public ClockInfo build() { - return new ClockInfo(mName, mTitle, mId, mThumbnail, mPreview); - } - - public Builder setName(String name) { - mName = name; - return this; - } - - public Builder setTitle(Supplier<String> title) { - mTitle = title; - return this; - } - - public Builder setId(String id) { - mId = id; - return this; - } - - public Builder setThumbnail(Supplier<Bitmap> thumbnail) { - mThumbnail = thumbnail; - return this; - } - - public Builder setPreview(Supplier<Bitmap> preview) { - mPreview = preview; - return this; - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java deleted file mode 100644 index d44d89e63e8f..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; - -import android.content.Context; -import android.content.res.Resources; -import android.util.AttributeSet; -import android.util.MathUtils; -import android.view.View; -import android.widget.FrameLayout; - -import com.android.systemui.R; - -/** - * Positions clock faces (analog, digital, typographic) and handles pixel shifting - * to prevent screen burn-in. - */ -public class ClockLayout extends FrameLayout { - - private static final int ANALOG_CLOCK_SHIFT_FACTOR = 3; - /** - * Clock face views. - */ - private View mAnalogClock; - - /** - * Pixel shifting amplitudes used to prevent screen burn-in. - */ - private int mBurnInPreventionOffsetX; - private int mBurnInPreventionOffsetY; - - private float mDarkAmount; - - public ClockLayout(Context context) { - this(context, null); - } - - public ClockLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mAnalogClock = findViewById(R.id.analog_clock); - - // Get pixel shifting X, Y amplitudes from resources. - Resources resources = getResources(); - mBurnInPreventionOffsetX = resources.getDimensionPixelSize( - R.dimen.burn_in_prevention_offset_x); - mBurnInPreventionOffsetY = resources.getDimensionPixelSize( - R.dimen.burn_in_prevention_offset_y); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - positionChildren(); - } - - void onTimeChanged() { - positionChildren(); - } - - /** - * See {@link com.android.systemui.plugins.ClockPlugin#setDarkAmount(float)}. - */ - void setDarkAmount(float darkAmount) { - mDarkAmount = darkAmount; - positionChildren(); - } - - private void positionChildren() { - final float offsetX = MathUtils.lerp(0f, - getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - mBurnInPreventionOffsetX, - mDarkAmount); - final float offsetY = MathUtils.lerp(0f, - getBurnInOffset(mBurnInPreventionOffsetY * 2, false) - - 0.5f * mBurnInPreventionOffsetY, - mDarkAmount); - - // Put the analog clock in the middle of the screen. - if (mAnalogClock != null) { - mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth())) - + ANALOG_CLOCK_SHIFT_FACTOR * offsetX); - mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) - + ANALOG_CLOCK_SHIFT_FACTOR * offsetY); - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java deleted file mode 100644 index 122c52138b08..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.annotation.Nullable; -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.ArrayMap; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; - -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManager.DockEventListener; -import com.android.systemui.plugins.ClockPlugin; -import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; -import com.android.systemui.settings.UserTracker; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -import javax.inject.Inject; - -/** - * Manages custom clock faces for AOD and lock screen. - * - * @deprecated Migrate to ClockRegistry - */ -@SysUISingleton -@Deprecated -public final class ClockManager { - - private static final String TAG = "ClockOptsProvider"; - - private final AvailableClocks mPreviewClocks; - private final List<Supplier<ClockPlugin>> mBuiltinClocks = new ArrayList<>(); - - private final Context mContext; - private final ContentResolver mContentResolver; - private final SettingsWrapper mSettingsWrapper; - private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - private final UserTracker mUserTracker; - private final Executor mMainExecutor; - - /** - * Observe settings changes to know when to switch the clock face. - */ - private final ContentObserver mContentObserver = - new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange, Collection<Uri> uris, - int flags, int userId) { - if (Objects.equals(userId, - mUserTracker.getUserId())) { - reload(); - } - } - }; - - /** - * Observe user changes and react by potentially loading the custom clock for the new user. - */ - private final UserTracker.Callback mUserChangedCallback = - new UserTracker.Callback() { - @Override - public void onUserChanged(int newUser, @NonNull Context userContext) { - reload(); - } - }; - - private final PluginManager mPluginManager; - @Nullable private final DockManager mDockManager; - - /** - * Observe changes to dock state to know when to switch the clock face. - */ - private final DockEventListener mDockEventListener = - new DockEventListener() { - @Override - public void onEvent(int event) { - mIsDocked = (event == DockManager.STATE_DOCKED - || event == DockManager.STATE_DOCKED_HIDE); - reload(); - } - }; - - /** - * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face - * to show. - */ - private boolean mIsDocked; - - /** - * Listeners for onClockChanged event. - * - * Each listener must receive a separate clock plugin instance. Otherwise, there could be - * problems like attempting to attach a view that already has a parent. To deal with this issue, - * each listener is associated with a collection of available clocks. When onClockChanged is - * fired the current clock plugin instance is retrieved from that listeners available clocks. - */ - private final Map<ClockChangedListener, AvailableClocks> mListeners = new ArrayMap<>(); - - private final int mWidth; - private final int mHeight; - - @Inject - public ClockManager(Context context, LayoutInflater layoutInflater, - PluginManager pluginManager, SysuiColorExtractor colorExtractor, - @Nullable DockManager dockManager, UserTracker userTracker, - @Main Executor mainExecutor) { - this(context, layoutInflater, pluginManager, colorExtractor, - context.getContentResolver(), userTracker, mainExecutor, - new SettingsWrapper(context.getContentResolver()), dockManager); - } - - @VisibleForTesting - ClockManager(Context context, LayoutInflater layoutInflater, - PluginManager pluginManager, SysuiColorExtractor colorExtractor, - ContentResolver contentResolver, UserTracker userTracker, Executor mainExecutor, - SettingsWrapper settingsWrapper, DockManager dockManager) { - mContext = context; - mPluginManager = pluginManager; - mContentResolver = contentResolver; - mSettingsWrapper = settingsWrapper; - mUserTracker = userTracker; - mMainExecutor = mainExecutor; - mDockManager = dockManager; - mPreviewClocks = new AvailableClocks(); - - Resources res = context.getResources(); - - addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); - - // Store the size of the display for generation of clock preview. - DisplayMetrics dm = res.getDisplayMetrics(); - mWidth = dm.widthPixels; - mHeight = dm.heightPixels; - } - - /** - * Add listener to be notified when clock implementation should change. - */ - public void addOnClockChangedListener(ClockChangedListener listener) { - if (mListeners.isEmpty()) { - register(); - } - AvailableClocks availableClocks = new AvailableClocks(); - for (int i = 0; i < mBuiltinClocks.size(); i++) { - availableClocks.addClockPlugin(mBuiltinClocks.get(i).get()); - } - mListeners.put(listener, availableClocks); - mPluginManager.addPluginListener(availableClocks, ClockPlugin.class, true); - reload(); - } - - /** - * Remove listener added with {@link addOnClockChangedListener}. - */ - public void removeOnClockChangedListener(ClockChangedListener listener) { - AvailableClocks availableClocks = mListeners.remove(listener); - mPluginManager.removePluginListener(availableClocks); - if (mListeners.isEmpty()) { - unregister(); - } - } - - /** - * Get information about available clock faces. - */ - List<ClockInfo> getClockInfos() { - return mPreviewClocks.getInfo(); - } - - /** - * Get the current clock. - * @return current custom clock or null for default. - */ - @Nullable - ClockPlugin getCurrentClock() { - return mPreviewClocks.getCurrentClock(); - } - - @VisibleForTesting - boolean isDocked() { - return mIsDocked; - } - - @VisibleForTesting - ContentObserver getContentObserver() { - return mContentObserver; - } - - @VisibleForTesting - void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { - ClockPlugin plugin = pluginSupplier.get(); - mPreviewClocks.addClockPlugin(plugin); - mBuiltinClocks.add(pluginSupplier); - } - - private void register() { - mPluginManager.addPluginListener(mPreviewClocks, ClockPlugin.class, true); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), - false, mContentObserver, UserHandle.USER_ALL); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), - false, mContentObserver, UserHandle.USER_ALL); - mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); - if (mDockManager != null) { - mDockManager.addListener(mDockEventListener); - } - } - - private void unregister() { - mPluginManager.removePluginListener(mPreviewClocks); - mContentResolver.unregisterContentObserver(mContentObserver); - mUserTracker.removeCallback(mUserChangedCallback); - if (mDockManager != null) { - mDockManager.removeListener(mDockEventListener); - } - } - - private void reload() { - mPreviewClocks.reloadCurrentClock(); - mListeners.forEach((listener, clocks) -> { - clocks.reloadCurrentClock(); - final ClockPlugin clock = clocks.getCurrentClock(); - if (Looper.myLooper() == Looper.getMainLooper()) { - listener.onClockChanged(clock instanceof DefaultClockController ? null : clock); - } else { - mMainHandler.post(() -> listener.onClockChanged( - clock instanceof DefaultClockController ? null : clock)); - } - }); - } - - /** - * Listener for events that should cause the custom clock face to change. - */ - public interface ClockChangedListener { - /** - * Called when custom clock should change. - * - * @param clock Custom clock face to use. A null value indicates the default clock face. - */ - void onClockChanged(ClockPlugin clock); - } - - /** - * Collection of available clocks. - */ - private final class AvailableClocks implements PluginListener<ClockPlugin> { - - /** - * Map from expected value stored in settings to plugin for custom clock face. - */ - private final Map<String, ClockPlugin> mClocks = new ArrayMap<>(); - - /** - * Metadata about available clocks, such as name and preview images. - */ - private final List<ClockInfo> mClockInfo = new ArrayList<>(); - - /** - * Active ClockPlugin. - */ - @Nullable private ClockPlugin mCurrentClock; - - @Override - public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { - addClockPlugin(plugin); - reloadIfNeeded(plugin); - } - - @Override - public void onPluginDisconnected(ClockPlugin plugin) { - removeClockPlugin(plugin); - reloadIfNeeded(plugin); - } - - /** - * Get the current clock. - * @return current custom clock or null for default. - */ - @Nullable - ClockPlugin getCurrentClock() { - return mCurrentClock; - } - - /** - * Get information about available clock faces. - */ - List<ClockInfo> getInfo() { - return mClockInfo; - } - - /** - * Adds a clock plugin to the collection of available clocks. - * - * @param plugin The plugin to add. - */ - void addClockPlugin(ClockPlugin plugin) { - final String id = plugin.getClass().getName(); - mClocks.put(plugin.getClass().getName(), plugin); - mClockInfo.add(ClockInfo.builder() - .setName(plugin.getName()) - .setTitle(plugin::getTitle) - .setId(id) - .setThumbnail(plugin::getThumbnail) - .setPreview(() -> plugin.getPreview(mWidth, mHeight)) - .build()); - } - - private void removeClockPlugin(ClockPlugin plugin) { - final String id = plugin.getClass().getName(); - mClocks.remove(id); - for (int i = 0; i < mClockInfo.size(); i++) { - if (id.equals(mClockInfo.get(i).getId())) { - mClockInfo.remove(i); - break; - } - } - } - - private void reloadIfNeeded(ClockPlugin plugin) { - final boolean wasCurrentClock = plugin == mCurrentClock; - reloadCurrentClock(); - final boolean isCurrentClock = plugin == mCurrentClock; - if (wasCurrentClock || isCurrentClock) { - ClockManager.this.reload(); - } - } - - /** - * Update the current clock. - */ - void reloadCurrentClock() { - mCurrentClock = getClockPlugin(); - } - - private ClockPlugin getClockPlugin() { - ClockPlugin plugin = null; - if (ClockManager.this.isDocked()) { - final String name = mSettingsWrapper.getDockedClockFace( - mUserTracker.getUserId()); - if (name != null) { - plugin = mClocks.get(name); - if (plugin != null) { - return plugin; - } - } - } - final String name = mSettingsWrapper.getLockScreenCustomClockFace( - mUserTracker.getUserId()); - if (name != null) { - plugin = mClocks.get(name); - } - return plugin; - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java deleted file mode 100644 index b6413cb61deb..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import android.os.ParcelFileDescriptor.AutoCloseOutputStream; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.FileNotFoundException; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Provider; - -/** - * Exposes custom clock face options and provides realistic preview images. - * - * APIs: - * - * /list_options: List the available clock faces, which has the following columns - * name: name of the clock face - * title: title of the clock face - * id: value used to set the clock face - * thumbnail: uri of the thumbnail image, should be /thumbnail/{name} - * preview: uri of the preview image, should be /preview/{name} - * - * /thumbnail/{id}: Opens a file stream for the thumbnail image for clock face {id}. - * - * /preview/{id}: Opens a file stream for the preview image for clock face {id}. - */ -public final class ClockOptionsProvider extends ContentProvider { - - private static final String TAG = "ClockOptionsProvider"; - private static final String KEY_LIST_OPTIONS = "/list_options"; - private static final String KEY_PREVIEW = "preview"; - private static final String KEY_THUMBNAIL = "thumbnail"; - private static final String COLUMN_NAME = "name"; - private static final String COLUMN_TITLE = "title"; - private static final String COLUMN_ID = "id"; - private static final String COLUMN_THUMBNAIL = "thumbnail"; - private static final String COLUMN_PREVIEW = "preview"; - private static final String MIME_TYPE_PNG = "image/png"; - private static final String CONTENT_SCHEME = "content"; - private static final String AUTHORITY = "com.android.keyguard.clock"; - - @Inject - public Provider<List<ClockInfo>> mClockInfosProvider; - - @VisibleForTesting - ClockOptionsProvider(Provider<List<ClockInfo>> clockInfosProvider) { - mClockInfosProvider = clockInfosProvider; - } - - @Override - public boolean onCreate() { - return true; - } - - @Override - public String getType(Uri uri) { - List<String> segments = uri.getPathSegments(); - if (segments.size() > 0 && (KEY_PREVIEW.equals(segments.get(0)) - || KEY_THUMBNAIL.equals(segments.get(0)))) { - return MIME_TYPE_PNG; - } - return "vnd.android.cursor.dir/clock_faces"; - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) { - if (!KEY_LIST_OPTIONS.equals(uri.getPath())) { - return null; - } - MatrixCursor cursor = new MatrixCursor(new String[] { - COLUMN_NAME, COLUMN_TITLE, COLUMN_ID, COLUMN_THUMBNAIL, COLUMN_PREVIEW}); - List<ClockInfo> clocks = mClockInfosProvider.get(); - for (int i = 0; i < clocks.size(); i++) { - ClockInfo clock = clocks.get(i); - cursor.newRow() - .add(COLUMN_NAME, clock.getName()) - .add(COLUMN_TITLE, clock.getTitle()) - .add(COLUMN_ID, clock.getId()) - .add(COLUMN_THUMBNAIL, createThumbnailUri(clock)) - .add(COLUMN_PREVIEW, createPreviewUri(clock)); - } - return cursor; - } - - @Override - public Uri insert(Uri uri, ContentValues initialValues) { - return null; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - List<String> segments = uri.getPathSegments(); - if (segments.size() != 2 || !(KEY_PREVIEW.equals(segments.get(0)) - || KEY_THUMBNAIL.equals(segments.get(0)))) { - throw new FileNotFoundException("Invalid preview url"); - } - String id = segments.get(1); - if (TextUtils.isEmpty(id)) { - throw new FileNotFoundException("Invalid preview url, missing id"); - } - ClockInfo clock = null; - List<ClockInfo> clocks = mClockInfosProvider.get(); - for (int i = 0; i < clocks.size(); i++) { - if (id.equals(clocks.get(i).getId())) { - clock = clocks.get(i); - break; - } - } - if (clock == null) { - throw new FileNotFoundException("Invalid preview url, id not found"); - } - return openPipeHelper(uri, MIME_TYPE_PNG, null, KEY_PREVIEW.equals(segments.get(0)) - ? clock.getPreview() : clock.getThumbnail(), new MyWriter()); - } - - private Uri createThumbnailUri(ClockInfo clock) { - return new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(KEY_THUMBNAIL) - .appendPath(clock.getId()) - .build(); - } - - private Uri createPreviewUri(ClockInfo clock) { - return new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(KEY_PREVIEW) - .appendPath(clock.getId()) - .build(); - } - - private static class MyWriter implements ContentProvider.PipeDataWriter<Bitmap> { - @Override - public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, - Bundle opts, Bitmap bitmap) { - try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, os); - } catch (Exception e) { - Log.w(TAG, "fail to write to pipe", e); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt b/packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt deleted file mode 100644 index 5c5493a0c200..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.graphics.Color -import android.util.MathUtils - -private const val PRIMARY_INDEX = 5 -private const val SECONDARY_DARK_INDEX = 8 -private const val SECONDARY_LIGHT_INDEX = 2 - -/** - * A helper class to extract colors from a clock face. - */ -class ClockPalette { - - private var darkAmount: Float = 0f - private var accentPrimary: Int = Color.WHITE - private var accentSecondaryLight: Int = Color.WHITE - private var accentSecondaryDark: Int = Color.BLACK - private val lightHSV: FloatArray = FloatArray(3) - private val darkHSV: FloatArray = FloatArray(3) - private val hsv: FloatArray = FloatArray(3) - - /** Returns a color from the palette as an RGB packed int. */ - fun getPrimaryColor(): Int { - return accentPrimary - } - - /** Returns either a light or dark color from the palette as an RGB packed int. */ - fun getSecondaryColor(): Int { - Color.colorToHSV(accentSecondaryLight, lightHSV) - Color.colorToHSV(accentSecondaryDark, darkHSV) - for (i in 0..2) { - hsv[i] = MathUtils.lerp(darkHSV[i], lightHSV[i], darkAmount) - } - return Color.HSVToColor(hsv) - } - - /** See {@link ClockPlugin#setColorPalette}. */ - fun setColorPalette(supportsDarkText: Boolean, colorPalette: IntArray?) { - if (colorPalette == null || colorPalette.isEmpty()) { - accentPrimary = Color.WHITE - accentSecondaryLight = Color.WHITE - accentSecondaryDark = if (supportsDarkText) Color.BLACK else Color.WHITE - return - } - val length = colorPalette.size - accentPrimary = colorPalette[Math.max(0, length - PRIMARY_INDEX)] - accentSecondaryLight = colorPalette[Math.max(0, length - SECONDARY_LIGHT_INDEX)] - accentSecondaryDark = colorPalette[Math.max(0, - length - if (supportsDarkText) SECONDARY_DARK_INDEX else SECONDARY_LIGHT_INDEX)] - } - - /** See {@link ClockPlugin#setDarkAmount}. */ - fun setDarkAmount(darkAmount: Float) { - this.darkAmount = darkAmount - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java deleted file mode 100644 index 3c3f4759614b..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.view.View; - -/** - * Controls transition to dark state by cross fading between views. - */ -final class CrossFadeDarkController { - - private final View mFadeInView; - private final View mFadeOutView; - - /** - * Creates a new controller that fades between views. - * - * @param fadeInView View to fade in when transitioning to AOD. - * @param fadeOutView View to fade out when transitioning to AOD. - */ - CrossFadeDarkController(View fadeInView, View fadeOutView) { - mFadeInView = fadeInView; - mFadeOutView = fadeOutView; - } - - /** - * Sets the amount the system has transitioned to the dark state. - * - * @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen. - */ - void setDarkAmount(float darkAmount) { - mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f)); - if (darkAmount == 0f) { - mFadeInView.setVisibility(View.GONE); - } else { - if (mFadeInView.getVisibility() == View.GONE) { - mFadeInView.setVisibility(View.VISIBLE); - } - } - mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount)); - if (darkAmount == 1f) { - mFadeOutView.setVisibility(View.GONE); - } else { - if (mFadeOutView.getVisibility() == View.GONE) { - mFadeOutView.setVisibility(View.VISIBLE); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java deleted file mode 100644 index c81935a15a15..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.app.WallpaperManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.Paint.Style; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ClockPlugin; - -import java.util.TimeZone; - -/** - * Plugin for the default clock face used only to provide a preview. - */ -public class DefaultClockController implements ClockPlugin { - - /** - * Resources used to get title and thumbnail. - */ - private final Resources mResources; - - /** - * LayoutInflater used to inflate custom clock views. - */ - private final LayoutInflater mLayoutInflater; - - /** - * Extracts accent color from wallpaper. - */ - private final SysuiColorExtractor mColorExtractor; - - /** - * Renders preview from clock view. - */ - private final ViewPreviewer mRenderer = new ViewPreviewer(); - - /** - * Root view of preview. - */ - private View mView; - - /** - * Text clock in preview view hierarchy. - */ - private TextView mTextTime; - - /** - * Date showing below time in preview view hierarchy. - */ - private TextView mTextDate; - - /** - * Create a DefaultClockController instance. - * - * @param res Resources contains title and thumbnail. - * @param inflater Inflater used to inflate custom clock views. - * @param colorExtractor Extracts accent color from wallpaper. - */ - public DefaultClockController(Resources res, LayoutInflater inflater, - SysuiColorExtractor colorExtractor) { - mResources = res; - mLayoutInflater = inflater; - mColorExtractor = colorExtractor; - } - - private void createViews() { - mView = mLayoutInflater.inflate(R.layout.default_clock_preview, null); - mTextTime = mView.findViewById(R.id.time); - mTextDate = mView.findViewById(R.id.date); - } - - @Override - public void onDestroyView() { - mView = null; - mTextTime = null; - mTextDate = null; - } - - @Override - public String getName() { - return "default"; - } - - @Override - public String getTitle() { - return mResources.getString(R.string.clock_title_default); - } - - @Override - public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.default_thumbnail); - } - - @Override - public Bitmap getPreview(int width, int height) { - - // Use the big clock view for the preview - View view = getBigClockView(); - - // Initialize state of plugin before generating preview. - setDarkAmount(1f); - setTextColor(Color.WHITE); - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK); - setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - onTimeTick(); - - return mRenderer.createPreview(view, width, height); - } - - @Override - public View getView() { - return null; - } - - @Override - public View getBigClockView() { - if (mView == null) { - createViews(); - } - return mView; - } - - @Override - public int getPreferredY(int totalHeight) { - return totalHeight / 2; - } - - @Override - public void setStyle(Style style) {} - - @Override - public void setTextColor(int color) { - mTextTime.setTextColor(color); - mTextDate.setTextColor(color); - } - - @Override - public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {} - - @Override - public void onTimeTick() { - } - - @Override - public void setDarkAmount(float darkAmount) {} - - @Override - public void onTimeZoneChanged(TimeZone timeZone) {} - - @Override - public boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java deleted file mode 100644 index 34c041bbb2dc..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.Context; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.android.systemui.R; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.TimeZone; - -/** - * Clock composed of two images that rotate with the time. - * - * The images are the clock hands. ImageClock expects two child ImageViews - * with ids hour_hand and minute_hand. - */ -public class ImageClock extends FrameLayout { - - private ImageView mHourHand; - private ImageView mMinuteHand; - private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); - private String mDescFormat; - private TimeZone mTimeZone; - - public ImageClock(Context context) { - this(context, null); - } - - public ImageClock(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); - } - - /** - * Call when the time changes to update the rotation of the clock hands. - */ - public void onTimeChanged() { - mTime.setTimeInMillis(System.currentTimeMillis()); - final float hourAngle = mTime.get(Calendar.HOUR) * 30f + mTime.get(Calendar.MINUTE) * 0.5f; - mHourHand.setRotation(hourAngle); - final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f; - mMinuteHand.setRotation(minuteAngle); - setContentDescription(DateFormat.format(mDescFormat, mTime)); - invalidate(); - } - - /** - * Call when the time zone has changed to update clock hands. - * - * @param timeZone The updated time zone that will be used. - */ - public void onTimeZoneChanged(TimeZone timeZone) { - mTimeZone = timeZone; - mTime.setTimeZone(timeZone); - } - - /** - * Sets the colors to use on the clock face. - * @param dark Darker color obtained from color palette. - * @param light Lighter color obtained from color palette. - */ - public void setClockColors(int dark, int light) { - mHourHand.setColorFilter(dark); - mMinuteHand.setColorFilter(light); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mHourHand = findViewById(R.id.hour_hand); - mMinuteHand = findViewById(R.id.minute_hand); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); - onTimeChanged(); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java deleted file mode 100644 index 096e94348429..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.annotation.Nullable; -import android.content.ContentResolver; -import android.provider.Settings; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Wrapper around Settings used for testing. - */ -public class SettingsWrapper { - - private static final String TAG = "ClockFaceSettings"; - private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE; - private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE; - private static final String CLOCK_FIELD = "clock"; - - private final ContentResolver mContentResolver; - private final Migration mMigration; - - SettingsWrapper(ContentResolver contentResolver) { - this(contentResolver, new Migrator(contentResolver)); - } - - @VisibleForTesting - SettingsWrapper(ContentResolver contentResolver, Migration migration) { - mContentResolver = contentResolver; - mMigration = migration; - } - - /** - * Gets the value stored in settings for the custom clock face. - * - * @param userId ID of the user. - */ - String getLockScreenCustomClockFace(int userId) { - return decode( - Settings.Secure.getStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, userId), - userId); - } - - /** - * Gets the value stored in settings for the clock face to use when docked. - * - * @param userId ID of the user. - */ - String getDockedClockFace(int userId) { - return Settings.Secure.getStringForUser(mContentResolver, DOCKED_CLOCK_FACE, userId); - } - - /** - * Decodes the string stored in settings, which should be formatted as JSON. - * @param value String stored in settings. If value is not JSON, then the settings is - * overwritten with JSON containing the prior value. - * @return ID of the clock face to show on AOD and lock screen. If value is not JSON, the value - * is returned. - */ - @VisibleForTesting - String decode(@Nullable String value, int userId) { - if (value == null) { - return value; - } - JSONObject json; - try { - json = new JSONObject(value); - } catch (JSONException ex) { - Log.e(TAG, "Settings value is not valid JSON", ex); - // The settings value isn't JSON since it didn't parse so migrate the value to JSON. - // TODO(b/135674383): Remove this migration path in the following release. - mMigration.migrate(value, userId); - return value; - } - try { - return json.getString(CLOCK_FIELD); - } catch (JSONException ex) { - Log.e(TAG, "JSON object does not contain clock field.", ex); - return null; - } - } - - interface Migration { - void migrate(String value, int userId); - } - - /** - * Implementation of {@link Migration} that writes valid JSON back to Settings. - */ - private static final class Migrator implements Migration { - - private final ContentResolver mContentResolver; - - Migrator(ContentResolver contentResolver) { - mContentResolver = contentResolver; - } - - /** - * Migrate settings values that don't parse by converting to JSON format. - * - * Values in settings must be JSON to be backed up and restored. To help users maintain - * their current settings, convert existing values into the JSON format. - * - * TODO(b/135674383): Remove this migration code in the following release. - */ - @Override - public void migrate(String value, int userId) { - try { - JSONObject json = new JSONObject(); - json.put(CLOCK_FIELD, value); - Settings.Secure.putStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, - json.toString(), - userId); - } catch (JSONException ex) { - Log.e(TAG, "Failed migrating settings value to JSON format", ex); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java b/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java deleted file mode 100644 index 4e51b98b0a4c..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard.clock; - -import android.content.Context; -import android.util.MathUtils; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.policy.SystemBarUtils; -import com.android.systemui.R; - -/** - * Computes preferred position of clock by considering height of status bar and lock icon. - */ -class SmallClockPosition { - - /** - * Dimensions used to determine preferred clock position. - */ - private final int mStatusBarHeight; - private final int mKeyguardLockPadding; - private final int mKeyguardLockHeight; - private final int mBurnInOffsetY; - - /** - * Amount of transition between AOD and lock screen. - */ - private float mDarkAmount; - - SmallClockPosition(Context context) { - this(SystemBarUtils.getStatusBarHeight(context), - context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_padding), - context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_height), - context.getResources().getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y) - ); - } - - @VisibleForTesting - SmallClockPosition(int statusBarHeight, int lockPadding, int lockHeight, int burnInY) { - mStatusBarHeight = statusBarHeight; - mKeyguardLockPadding = lockPadding; - mKeyguardLockHeight = lockHeight; - mBurnInOffsetY = burnInY; - } - - /** - * See {@link ClockPlugin#setDarkAmount}. - */ - void setDarkAmount(float darkAmount) { - mDarkAmount = darkAmount; - } - - /** - * Gets the preferred Y position accounting for status bar and lock icon heights. - */ - int getPreferredY() { - // On AOD, clock needs to appear below the status bar with enough room for pixel shifting - int aodY = mStatusBarHeight + mKeyguardLockHeight + 2 * mKeyguardLockPadding - + mBurnInOffsetY; - // On lock screen, clock needs to appear below the lock icon - int lockY = mStatusBarHeight + mKeyguardLockHeight + 2 * mKeyguardLockPadding; - return (int) MathUtils.lerp(lockY, aodY, mDarkAmount); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java deleted file mode 100644 index abd0dd28dabc..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.annotation.Nullable; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; - -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; - -/** - * Creates a preview image ({@link Bitmap}) of a {@link View} for a custom clock face. - */ -final class ViewPreviewer { - - private static final String TAG = "ViewPreviewer"; - - /** - * Handler used to run {@link View#draw(Canvas)} on the main thread. - */ - private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - - /** - * Generate a realistic preview of a clock face. - * - * @param view view is used to generate preview image. - * @param width width of the preview image, should be the same as device width in pixels. - * @param height height of the preview image, should be the same as device height in pixels. - * @return bitmap of view. - */ - @Nullable - Bitmap createPreview(View view, int width, int height) { - if (view == null) { - return null; - } - FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() { - @Override - public Bitmap call() { - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - - // Draw clock view hierarchy to canvas. - Canvas canvas = new Canvas(bitmap); - canvas.drawColor(Color.BLACK); - dispatchVisibilityAggregated(view, true); - view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); - view.layout(0, 0, width, height); - view.draw(canvas); - - return bitmap; - } - }); - - if (Looper.myLooper() == Looper.getMainLooper()) { - task.run(); - } else { - mMainHandler.post(task); - } - - try { - return task.get(); - } catch (Exception e) { - Log.e(TAG, "Error completing task", e); - return null; - } - } - - private void dispatchVisibilityAggregated(View view, boolean isVisible) { - // Similar to View.dispatchVisibilityAggregated implementation. - final boolean thisVisible = view.getVisibility() == View.VISIBLE; - if (thisVisible || !isVisible) { - view.onVisibilityAggregated(isVisible); - } - - if (view instanceof ViewGroup) { - isVisible = thisVisible && isVisible; - ViewGroup vg = (ViewGroup) view; - int count = vg.getChildCount(); - - for (int i = 0; i < count; i++) { - dispatchVisibilityAggregated(vg.getChildAt(i), isVisible); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java index d2b10d687f04..83fc2789dc91 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java @@ -71,7 +71,8 @@ public abstract class ClockRegistryModule { context.getString(R.string.lockscreen_clock_id_fallback), logBuffer, /* keepAllLoaded = */ false, - /* subTag = */ "System"); + /* subTag = */ "System", + /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK)); registry.registerListeners(); return registry; } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index be5bb07089dd..1f1b154ef1c8 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -33,7 +33,6 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.Preconditions; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.clock.ClockManager; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; @@ -323,7 +322,6 @@ public class Dependency { @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail; @Inject @Main Lazy<Executor> mMainExecutor; @Inject @Background Lazy<Executor> mBackgroundExecutor; - @Inject Lazy<ClockManager> mClockManager; @Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper; @Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper; @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper; @@ -517,7 +515,6 @@ public class Dependency { mProviders.put(SmartReplyController.class, mSmartReplyController::get); mProviders.put(RemoteInputQuickSettingsDisabler.class, mRemoteInputQuickSettingsDisabler::get); - mProviders.put(ClockManager.class, mClockManager::get); mProviders.put(PrivacyItemController.class, mPrivacyItemController::get); mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get); mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt index 99dd6b6cd673..670c1fa45e5c 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt @@ -49,8 +49,11 @@ import kotlin.math.floor * When the HWC of the device supports Composition.DISPLAY_DECORATION, we use this layer to draw * screen decorations. */ -class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDecorationSupport) : - DisplayCutoutBaseView(context) { +class ScreenDecorHwcLayer( + context: Context, + displayDecorationSupport: DisplayDecorationSupport, + private val debug: Boolean, +) : DisplayCutoutBaseView(context) { val colorMode: Int private val useInvertedAlphaColor: Boolean private val color: Int @@ -74,7 +77,7 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec throw IllegalArgumentException("Attempting to use unsupported mode " + "${PixelFormat.formatToString(displayDecorationSupport.format)}") } - if (DEBUG_COLOR) { + if (debug) { color = Color.GREEN bgColor = Color.TRANSPARENT colorMode = ActivityInfo.COLOR_MODE_DEFAULT @@ -106,7 +109,7 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec override fun onAttachedToWindow() { super.onAttachedToWindow() parent.requestTransparentRegion(this) - if (!DEBUG_COLOR) { + if (!debug) { viewRootImpl.setDisplayDecoration(true) } @@ -143,12 +146,12 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec override fun gatherTransparentRegion(region: Region?): Boolean { region?.let { calculateTransparentRect() - if (DEBUG_COLOR) { + if (debug) { // Since we're going to draw a rectangle where the layer would // normally be transparent, treat the transparent region as // empty. We still want this method to be called, though, so // that it calculates the transparent rect at the right time - // to match !DEBUG_COLOR. + // to match ![debug] region.setEmpty() } else { region.op(transparentRect, Region.Op.INTERSECT) @@ -421,8 +424,4 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec ipw.println("roundedCornerBottomSize=$roundedCornerBottomSize") ipw.decreaseIndent() } - - companion object { - private val DEBUG_COLOR = ScreenDecorations.DEBUG_COLOR - } } diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index ea0f343e80f4..67d4a2e25051 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -71,6 +71,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.decor.CutoutDecorProviderFactory; +import com.android.systemui.decor.DebugRoundedCornerDelegate; import com.android.systemui.decor.DecorProvider; import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.DecorProviderKt; @@ -78,14 +79,12 @@ import com.android.systemui.decor.FaceScanningProviderFactory; import com.android.systemui.decor.OverlayWindow; import com.android.systemui.decor.PrivacyDotDecorProviderFactory; import com.android.systemui.decor.RoundedCornerDecorProviderFactory; -import com.android.systemui.decor.RoundedCornerResDelegate; +import com.android.systemui.decor.RoundedCornerResDelegateImpl; import com.android.systemui.log.ScreenDecorationsLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.events.PrivacyDotViewController; -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.ThreadFactory; import com.android.systemui.util.settings.SecureSettings; @@ -105,19 +104,17 @@ import javax.inject.Inject; * for antialiasing and emulation purposes. */ @SysUISingleton -public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { - private static final boolean DEBUG = false; +public class ScreenDecorations implements CoreStartable, Dumpable { + private static final boolean DEBUG_LOGGING = false; private static final String TAG = "ScreenDecorations"; - public static final String SIZE = "sysui_rounded_size"; - public static final String PADDING = "sysui_rounded_content_padding"; // Provide a way for factory to disable ScreenDecorations to run the Display tests. private static final boolean DEBUG_DISABLE_SCREEN_DECORATIONS = SystemProperties.getBoolean("debug.disable_screen_decorations", false); private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS = SystemProperties.getBoolean("debug.screenshot_rounded_corners", false); - private static final boolean VERBOSE = false; - static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS; + private boolean mDebug = DEBUG_SCREENSHOT_ROUNDED_CORNERS; + private int mDebugColor = Color.RED; private static final int[] DISPLAY_CUTOUT_IDS = { R.id.display_cutout, @@ -134,7 +131,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { protected boolean mIsRegistered; private final Context mContext; private final Executor mMainExecutor; - private final TunerService mTunerService; private final SecureSettings mSecureSettings; @VisibleForTesting DisplayTracker.Callback mDisplayListener; @@ -147,9 +143,13 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { public final int mFaceScanningViewId; @VisibleForTesting - protected RoundedCornerResDelegate mRoundedCornerResDelegate; + protected RoundedCornerResDelegateImpl mRoundedCornerResDelegate; @VisibleForTesting protected DecorProviderFactory mRoundedCornerFactory; + @VisibleForTesting + protected DebugRoundedCornerDelegate mDebugRoundedCornerDelegate = + new DebugRoundedCornerDelegate(); + protected DecorProviderFactory mDebugRoundedCornerFactory; private CutoutDecorProviderFactory mCutoutFactory; private int mProviderRefreshToken = 0; @VisibleForTesting @@ -315,7 +315,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { public ScreenDecorations(Context context, @Main Executor mainExecutor, SecureSettings secureSettings, - TunerService tunerService, UserTracker userTracker, DisplayTracker displayTracker, PrivacyDotViewController dotViewController, @@ -327,7 +326,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mContext = context; mMainExecutor = mainExecutor; mSecureSettings = secureSettings; - mTunerService = tunerService; mUserTracker = userTracker; mDisplayTracker = displayTracker; mDotViewController = dotViewController; @@ -365,16 +363,47 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mAuthController.addCallback(mAuthControllerCallback); } + /** + * Change the value of {@link ScreenDecorations#mDebug}. This operation is heavyweight, since + * it requires essentially re-init-ing this screen decorations process with the debug + * information taken into account. + */ + @VisibleForTesting + protected void setDebug(boolean debug) { + if (mDebug == debug) { + return; + } + + mDebug = debug; + if (!mDebug) { + mDebugRoundedCornerDelegate.removeDebugState(); + } + + mExecutor.execute(() -> { + // Re-trigger all of the screen decorations setup here so that the debug values + // can be picked up + removeAllOverlays(); + removeHwcOverlay(); + startOnScreenDecorationsThread(); + updateColorInversionDefault(); + }); + } + private boolean isPrivacyDotEnabled() { return mDotFactory.getHasProviders(); } @NonNull - private List<DecorProvider> getProviders(boolean hasHwLayer) { + @VisibleForTesting + protected List<DecorProvider> getProviders(boolean hasHwLayer) { List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders()); decorProviders.addAll(mFaceScanningFactory.getProviders()); if (!hasHwLayer) { - decorProviders.addAll(mRoundedCornerFactory.getProviders()); + if (mDebug && mDebugRoundedCornerFactory.getHasProviders()) { + decorProviders.addAll(mDebugRoundedCornerFactory.getProviders()); + } else { + decorProviders.addAll(mRoundedCornerFactory.getProviders()); + } decorProviders.addAll(mCutoutFactory.getProviders()); } return decorProviders; @@ -416,11 +445,13 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mDisplayMode = mDisplayInfo.getMode(); mDisplayUniqueId = mDisplayInfo.uniqueId; mDisplayCutout = mDisplayInfo.displayCutout; - mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(), - mDisplayUniqueId); + mRoundedCornerResDelegate = + new RoundedCornerResDelegateImpl(mContext.getResources(), mDisplayUniqueId); mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio( getPhysicalPixelDisplaySizeRatio()); mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); + mDebugRoundedCornerFactory = + new RoundedCornerDecorProviderFactory(mDebugRoundedCornerDelegate); mCutoutFactory = getCutoutFactory(); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); updateHwLayerRoundedCornerDrawable(); @@ -444,15 +475,12 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { // - we are trying to redraw. This because WM resized our window and told us to. // - the config change has been dispatched, so WM is no longer deferring layout. mPendingConfigChange = true; - if (DEBUG) { - if (mRotation != newRotation) { - Log.i(TAG, "Rotation changed, deferring " + newRotation - + ", staying at " + mRotation); - } - if (displayModeChanged(mDisplayMode, newDisplayMode)) { - Log.i(TAG, "Resolution changed, deferring " + newDisplayMode - + ", staying at " + mDisplayMode); - } + if (mRotation != newRotation) { + mLogger.logRotationChangeDeferred(mRotation, newRotation); + } + if (displayModeChanged(mDisplayMode, newDisplayMode)) { + mLogger.logDisplayModeChanged( + newDisplayMode.getModeId(), mDisplayMode.getModeId()); } if (mOverlays != null) { @@ -608,12 +636,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { return; } - mMainExecutor.execute(() -> { - Trace.beginSection("ScreenDecorations#addTunable"); - mTunerService.addTunable(this, SIZE); - Trace.endSection(); - }); - // Watch color inversion and invert the overlay as needed. if (mColorInversionSetting == null) { mColorInversionSetting = new SettingObserver(mSecureSettings, mHandler, @@ -632,12 +654,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mUserTracker.addCallback(mUserChangedCallback, mExecutor); mIsRegistered = true; } else { - mMainExecutor.execute(() -> { - Trace.beginSection("ScreenDecorations#removeTunable"); - mTunerService.removeTunable(this); - Trace.endSection(); - }); - if (mColorInversionSetting != null) { mColorInversionSetting.setListening(false); } @@ -777,7 +793,8 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { } mScreenDecorHwcWindow = (ViewGroup) LayoutInflater.from(mContext).inflate( R.layout.screen_decor_hwc_layer, null); - mScreenDecorHwcLayer = new ScreenDecorHwcLayer(mContext, mHwcScreenDecorationSupport); + mScreenDecorHwcLayer = + new ScreenDecorHwcLayer(mContext, mHwcScreenDecorationSupport, mDebug); mScreenDecorHwcWindow.addView(mScreenDecorHwcLayer, new FrameLayout.LayoutParams( MATCH_PARENT, MATCH_PARENT, Gravity.TOP | Gravity.START)); mWindowManager.addView(mScreenDecorHwcWindow, getHwcWindowLayoutParams()); @@ -825,7 +842,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { lp.height = MATCH_PARENT; lp.setTitle("ScreenDecorHwcOverlay"); lp.gravity = Gravity.TOP | Gravity.START; - if (!DEBUG_COLOR) { + if (!mDebug) { lp.setColorMode(ActivityInfo.COLOR_MODE_A8); } return lp; @@ -933,19 +950,42 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { new UserTracker.Callback() { @Override public void onUserChanged(int newUser, @NonNull Context userContext) { - if (DEBUG) { - Log.d(TAG, "UserSwitched newUserId=" + newUser); - } + mLogger.logUserSwitched(newUser); // update color inversion setting to the new user mColorInversionSetting.setUserId(newUser); updateColorInversion(mColorInversionSetting.getValue()); } }; + /** + * Use the current value of {@link ScreenDecorations#mColorInversionSetting} and passes it + * to {@link ScreenDecorations#updateColorInversion} + */ + private void updateColorInversionDefault() { + int inversion = 0; + if (mColorInversionSetting != null) { + inversion = mColorInversionSetting.getValue(); + } + + updateColorInversion(inversion); + } + + /** + * Update the tint color of screen decoration assets. Defaults to Color.BLACK. In the case of + * a color inversion being set, use Color.WHITE (which inverts to black). + * + * When {@link ScreenDecorations#mDebug} is {@code true}, this value is updated to use + * {@link ScreenDecorations#mDebugColor}, and does not handle inversion. + * + * @param colorsInvertedValue if non-zero, assume that colors are inverted, and use Color.WHITE + * for screen decoration tint + */ private void updateColorInversion(int colorsInvertedValue) { mTintColor = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK; - if (DEBUG_COLOR) { - mTintColor = Color.RED; + if (mDebug) { + mTintColor = mDebugColor; + mDebugRoundedCornerDelegate.setColor(mTintColor); + //TODO(b/285941724): update the hwc layer color here too (or disable it in debug mode) } updateOverlayProviderViews(new Integer[] { @@ -986,7 +1026,9 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { int oldRotation = mRotation; mPendingConfigChange = false; updateConfiguration(); - if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); + if (oldRotation != mRotation) { + mLogger.logRotationChanged(oldRotation, mRotation); + } setupDecorations(); if (mOverlays != null) { // Updating the layout params ensures that ViewRootImpl will call relayoutWindow(), @@ -1016,6 +1058,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { if (DEBUG_DISABLE_SCREEN_DECORATIONS) { return; } + ipw.println("mDebug:" + mDebug); ipw.println("mIsPrivacyDotEnabled:" + isPrivacyDotEnabled()); ipw.println("shouldOptimizeOverlayVisibility:" + shouldOptimizeVisibility()); @@ -1071,6 +1114,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { } } mRoundedCornerResDelegate.dump(pw, args); + mDebugRoundedCornerDelegate.dump(pw); } @VisibleForTesting @@ -1093,8 +1137,9 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mRotation = newRotation; mDisplayMode = newMod; mDisplayCutout = newCutout; - mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio( - getPhysicalPixelDisplaySizeRatio()); + float ratio = getPhysicalPixelDisplaySizeRatio(); + mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(ratio); + mDebugRoundedCornerDelegate.setPhysicalPixelDisplaySizeRatio(ratio); if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingConfigChange = false; mScreenDecorHwcLayer.updateConfiguration(mDisplayUniqueId); @@ -1117,7 +1162,8 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { } private boolean hasRoundedCorners() { - return mRoundedCornerFactory.getHasProviders(); + return mRoundedCornerFactory.getHasProviders() + || mDebugRoundedCornerFactory.getHasProviders(); } private boolean shouldOptimizeVisibility() { @@ -1170,41 +1216,17 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { Trace.endSection(); } - @Override - public void onTuningChanged(String key, String newValue) { - if (DEBUG_DISABLE_SCREEN_DECORATIONS) { - Log.i(TAG, "ScreenDecorations is disabled"); - return; - } - mExecutor.execute(() -> { - if (mOverlays == null || !SIZE.equals(key)) { - return; - } - Trace.beginSection("ScreenDecorations#onTuningChanged"); - try { - final int sizeFactor = Integer.parseInt(newValue); - mRoundedCornerResDelegate.setTuningSizeFactor(sizeFactor); - } catch (NumberFormatException e) { - mRoundedCornerResDelegate.setTuningSizeFactor(null); - } - updateOverlayProviderViews(new Integer[] { - R.id.rounded_corner_top_left, - R.id.rounded_corner_top_right, - R.id.rounded_corner_bottom_left, - R.id.rounded_corner_bottom_right - }); - updateHwLayerRoundedCornerExistAndSize(); - Trace.endSection(); - }); - } - private void updateHwLayerRoundedCornerDrawable() { if (mScreenDecorHwcLayer == null) { return; } - final Drawable topDrawable = mRoundedCornerResDelegate.getTopRoundedDrawable(); - final Drawable bottomDrawable = mRoundedCornerResDelegate.getBottomRoundedDrawable(); + Drawable topDrawable = mRoundedCornerResDelegate.getTopRoundedDrawable(); + Drawable bottomDrawable = mRoundedCornerResDelegate.getBottomRoundedDrawable(); + if (mDebug && (mDebugRoundedCornerFactory.getHasProviders())) { + topDrawable = mDebugRoundedCornerDelegate.getTopRoundedDrawable(); + bottomDrawable = mDebugRoundedCornerDelegate.getBottomRoundedDrawable(); + } if (topDrawable == null || bottomDrawable == null) { return; @@ -1216,11 +1238,19 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { if (mScreenDecorHwcLayer == null) { return; } - mScreenDecorHwcLayer.updateRoundedCornerExistenceAndSize( - mRoundedCornerResDelegate.getHasTop(), - mRoundedCornerResDelegate.getHasBottom(), - mRoundedCornerResDelegate.getTopRoundedSize().getWidth(), - mRoundedCornerResDelegate.getBottomRoundedSize().getWidth()); + if (mDebug && mDebugRoundedCornerFactory.getHasProviders()) { + mScreenDecorHwcLayer.updateRoundedCornerExistenceAndSize( + mDebugRoundedCornerDelegate.getHasTop(), + mDebugRoundedCornerDelegate.getHasBottom(), + mDebugRoundedCornerDelegate.getTopRoundedSize().getWidth(), + mDebugRoundedCornerDelegate.getBottomRoundedSize().getWidth()); + } else { + mScreenDecorHwcLayer.updateRoundedCornerExistenceAndSize( + mRoundedCornerResDelegate.getHasTop(), + mRoundedCornerResDelegate.getHasBottom(), + mRoundedCornerResDelegate.getTopRoundedSize().getWidth(), + mRoundedCornerResDelegate.getBottomRoundedSize().getWidth()); + } } @VisibleForTesting @@ -1247,7 +1277,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { paint.setColor(mColor); paint.setStyle(Paint.Style.FILL); - if (DEBUG) { + if (DEBUG_LOGGING) { getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG, getWindowTitleByPos(pos) + " drawn in rot " + mRotation)); } @@ -1440,7 +1470,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mView.getViewTreeObserver().removeOnPreDrawListener(this); if (mTargetRotation == mRotation && !displayModeChanged(mDisplayMode, mTargetDisplayMode)) { - if (DEBUG) { + if (DEBUG_LOGGING) { final String title = mPosition < 0 ? "ScreenDecorHwcLayer" : getWindowTitleByPos(mPosition); Log.i(TAG, title + " already in target rot " @@ -1456,7 +1486,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { // This changes the window attributes - we need to restart the traversal for them to // take effect. updateConfiguration(); - if (DEBUG) { + if (DEBUG_LOGGING) { final String title = mPosition < 0 ? "ScreenDecorHwcLayer" : getWindowTitleByPos(mPosition); Log.i(TAG, title @@ -1491,7 +1521,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { final Display.Mode displayMode = mDisplayInfo.getMode(); if ((displayRotation != mRotation || displayModeChanged(mDisplayMode, displayMode)) && !mPendingConfigChange) { - if (DEBUG) { + if (DEBUG_LOGGING) { if (displayRotation != mRotation) { Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot " + displayRotation + ". Restarting draw"); diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index c4ebee2a9197..0530aed912bc 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -77,7 +77,9 @@ class AuthenticationRepositoryImpl @Inject constructor() : AuthenticationReposit override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() private val _authenticationMethod = - MutableStateFlow<AuthenticationMethodModel>(AuthenticationMethodModel.Pin(1234)) + MutableStateFlow<AuthenticationMethodModel>( + AuthenticationMethodModel.Pin(listOf(1, 2, 3, 4), autoConfirm = false) + ) override val authenticationMethod: StateFlow<AuthenticationMethodModel> = _authenticationMethod.asStateFlow() diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index dd9dcbedd6fc..20e82f70d8f5 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -122,14 +122,36 @@ constructor( /** * Attempts to authenticate the user and unlock the device. * + * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method + * supports auto-confirming, and the input's length is at least the code's length. Otherwise, + * `null` is returned. + * * @param input The input from the user to try to authenticate with. This can be a list of * different things, based on the current authentication method. - * @return `true` if the authentication succeeded and the device is now unlocked; `false` - * otherwise. + * @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit + * request to validate. + * @return `true` if the authentication succeeded and the device is now unlocked; `false` when + * authentication failed, `null` if the check was not performed. */ - fun authenticate(input: List<Any>): Boolean { + fun authenticate(input: List<Any>, tryAutoConfirm: Boolean = false): Boolean? { + val authMethod = this.authenticationMethod.value + if (tryAutoConfirm) { + if ((authMethod as? AuthenticationMethodModel.Pin)?.autoConfirm != true) { + // Do not attempt to authenticate unless the PIN lock is set to auto-confirm. + return null + } + + if (input.size < authMethod.code.size) { + // Do not attempt to authenticate if the PIN has not yet the required amount of + // digits. This intentionally only skip for shorter PINs; if the PIN is longer, the + // layer above might have throttled this check, and the PIN should be rejected via + // the auth code below. + return null + } + } + val isSuccessful = - when (val authMethod = this.authenticationMethod.value) { + when (authMethod) { is AuthenticationMethodModel.Pin -> input.asCode() == authMethod.code is AuthenticationMethodModel.Password -> input.asPassword() == authMethod.password is AuthenticationMethodModel.Pattern -> input.asPattern() == authMethod.coordinates @@ -180,21 +202,17 @@ constructor( * Returns a PIN code from the given list. It's assumed the given list elements are all * [Int] in the range [0-9]. */ - private fun List<Any>.asCode(): Long? { + private fun List<Any>.asCode(): List<Int>? { if (isEmpty() || size > DevicePolicyManager.MAX_PASSWORD_LENGTH) { return null } - var code = 0L - map { - require(it is Int && it in 0..9) { - "Pin is required to be Int in range [0..9], but got $it" - } - it + return map { + require(it is Int && it in 0..9) { + "Pin is required to be Int in range [0..9], but got $it" } - .forEach { integer -> code = code * 10 + integer } - - return code + it + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt index e4fbf9af35ea..1016b6b9a99e 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt @@ -16,6 +16,8 @@ package com.android.systemui.authentication.shared.model +import androidx.annotation.VisibleForTesting + /** Enumerates all known authentication methods. */ sealed class AuthenticationMethodModel( /** @@ -38,7 +40,16 @@ sealed class AuthenticationMethodModel( * In practice, a pin is restricted to 16 decimal digits , see * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH] */ - data class Pin(val code: Long) : AuthenticationMethodModel(isSecure = true) + data class Pin(val code: List<Int>, val autoConfirm: Boolean) : + AuthenticationMethodModel(isSecure = true) { + + /** Convenience constructor for tests only. */ + @VisibleForTesting + constructor( + code: Long, + autoConfirm: Boolean = false + ) : this(code.toString(10).map { it - '0' }, autoConfirm) {} + } data class Password(val password: String) : AuthenticationMethodModel(isSecure = true) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 7f706859abb3..d8348eda3d97 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -374,7 +374,6 @@ public class AuthContainerView extends LinearLayout if (Utils.isBiometricAllowed(config.mPromptInfo)) { mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication( config.mPromptInfo, - config.mRequireConfirmation, config.mUserId, config.mOperationId, new BiometricModalities(fpProps, faceProps)); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index e58876a2632c..76e48e9ef711 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -1057,8 +1057,16 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } private String getNotRecognizedString(@Modality int modality) { - return mContext.getString(modality == TYPE_FACE - ? R.string.biometric_face_not_recognized : R.string.biometric_not_recognized); + final int messageRes; + final int userId = mCurrentDialogArgs.argi1; + if (isFaceAuthEnrolled(userId) && isFingerprintEnrolled(userId)) { + messageRes = modality == TYPE_FACE + ? R.string.biometric_face_not_recognized + : R.string.fingerprint_error_not_match; + } else { + messageRes = R.string.biometric_not_recognized; + } + return mContext.getString(messageRes); } private String getErrorString(@Modality int modality, int error, int vendorCode) { @@ -1216,8 +1224,11 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, final PromptInfo promptInfo = (PromptInfo) args.arg1; final int[] sensorIds = (int[]) args.arg3; + + // TODO(b/251476085): remove these unused parameters (replaced with SSOT elsewhere) final boolean credentialAllowed = (boolean) args.arg4; final boolean requireConfirmation = (boolean) args.arg5; + final int userId = args.argi1; final String opPackageName = (String) args.arg6; final long operationId = args.argl1; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt index b72801d3b5fe..5218537394de 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt @@ -16,11 +16,13 @@ constructor( @Main private val mainExecutor: Executor, ) { private var action: Action? = null + private var panelState: Int = -1 @MainThread fun enable(onPanelInteraction: Runnable) { if (action == null) { action = Action(onPanelInteraction) + shadeExpansionStateManager.addStateListener(this::onPanelStateChanged) shadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged) } else { Log.e(TAG, "Already enabled") @@ -32,6 +34,8 @@ constructor( if (action != null) { Log.i(TAG, "Disable dectector") action = null + panelState = -1 + shadeExpansionStateManager.removeStateListener(this::onPanelStateChanged) shadeExpansionStateManager.removeExpansionListener(this::onPanelExpansionChanged) } } @@ -40,13 +44,34 @@ constructor( private fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) = mainExecutor.execute { action?.let { - if (event.tracking || (event.expanded && event.fraction > 0)) { - Log.i(TAG, "Detected panel interaction, event: $event") + if (event.tracking || (event.expanded && event.fraction > 0 && panelState == 1)) { + Log.i(TAG, "onPanelExpansionChanged, event: $event") it.onPanelInteraction.run() disable() } } } + + @AnyThread + private fun onPanelStateChanged(state: Int) = + mainExecutor.execute { + // When device owner set screen lock type as Swipe, and install work profile with + // pin/pattern/password & fingerprint or face, if work profile allow user to verify + // by BP, it is possible that BP will be displayed when keyguard is closing, in this + // case event.expanded = true and event.fraction > 0, so BP will be closed, adding + // panel state into consideration is workaround^2, this workaround works because + // onPanelStateChanged is earlier than onPanelExpansionChanged + + // we don't want to close BP in below case + // + // | Action | tracking | expanded | fraction | panelState | + // | HeadsUp | NA | NA | NA | 1 | + // | b/285111529 | false | true | > 0 | 2 | + + // Note: HeadsUp behavior was changed, so we can't got onPanelExpansionChanged now + panelState = state + Log.i(TAG, "onPanelStateChanged, state: $state") + } } private data class Action(val onPanelInteraction: Runnable) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java index 6db266f4f1cb..9d8dcc1efdd8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java @@ -21,6 +21,8 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FACE_REENROLL_DIALOG; import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -30,6 +32,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricSourceType; +import android.hardware.biometrics.BiometricStateListener; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; @@ -42,7 +47,6 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.KeyguardStateController; - import java.util.Optional; import javax.inject.Inject; @@ -69,6 +73,8 @@ public class BiometricNotificationService implements CoreStartable { private final NotificationManager mNotificationManager; private final BiometricNotificationBroadcastReceiver mBroadcastReceiver; private final FingerprintReEnrollNotification mFingerprintReEnrollNotification; + private final FingerprintManager mFingerprintManager; + private final FaceManager mFaceManager; private NotificationChannel mNotificationChannel; private boolean mFaceNotificationQueued; private boolean mFingerprintNotificationQueued; @@ -119,14 +125,29 @@ public class BiometricNotificationService implements CoreStartable { } }; + private final BiometricStateListener mFaceStateListener = new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mNotificationManager.cancelAsUser(TAG, FACE_NOTIFICATION_ID, UserHandle.CURRENT); + } + }; + + private final BiometricStateListener mFingerprintStateListener = new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mNotificationManager.cancelAsUser(TAG, FINGERPRINT_NOTIFICATION_ID, UserHandle.CURRENT); + } + }; @Inject - public BiometricNotificationService(Context context, - KeyguardUpdateMonitor keyguardUpdateMonitor, - KeyguardStateController keyguardStateController, - Handler handler, NotificationManager notificationManager, - BiometricNotificationBroadcastReceiver biometricNotificationBroadcastReceiver, - Optional<FingerprintReEnrollNotification> fingerprintReEnrollNotification) { + public BiometricNotificationService(@NonNull Context context, + @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, + @NonNull KeyguardStateController keyguardStateController, + @NonNull Handler handler, @NonNull NotificationManager notificationManager, + @NonNull BiometricNotificationBroadcastReceiver biometricNotificationBroadcastReceiver, + @NonNull Optional<FingerprintReEnrollNotification> fingerprintReEnrollNotification, + @Nullable FingerprintManager fingerprintManager, + @Nullable FaceManager faceManager) { mContext = context; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardStateController = keyguardStateController; @@ -135,6 +156,8 @@ public class BiometricNotificationService implements CoreStartable { mBroadcastReceiver = biometricNotificationBroadcastReceiver; mFingerprintReEnrollNotification = fingerprintReEnrollNotification.orElse( new FingerprintReEnrollNotificationImpl()); + mFingerprintManager = fingerprintManager; + mFaceManager = faceManager; } @Override @@ -148,9 +171,16 @@ public class BiometricNotificationService implements CoreStartable { intentFilter.addAction(ACTION_SHOW_FACE_REENROLL_DIALOG); mContext.registerReceiver(mBroadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED_UNAUDITED); + if (mFingerprintManager != null) { + mFingerprintManager.registerBiometricStateListener(mFingerprintStateListener); + } + if (mFaceManager != null) { + mFaceManager.registerBiometricStateListener(mFaceStateListener); + } } private void queueFaceReenrollNotification() { + Log.d(TAG, "Face re-enroll notification queued."); mFaceNotificationQueued = true; final String title = mContext.getString(R.string.face_re_enroll_notification_title); final String content = mContext.getString( @@ -163,6 +193,7 @@ public class BiometricNotificationService implements CoreStartable { } private void queueFingerprintReenrollNotification() { + Log.d(TAG, "Fingerprint re-enroll notification queued."); mFingerprintNotificationQueued = true; final String title = mContext.getString(R.string.fingerprint_re_enroll_notification_title); final String content = mContext.getString( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt index ddf1457e385c..a5e846ad61ca 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt @@ -17,6 +17,8 @@ package com.android.systemui.biometrics.dagger import com.android.settingslib.udfps.UdfpsUtils +import com.android.systemui.biometrics.data.repository.FaceSettingsRepository +import com.android.systemui.biometrics.data.repository.FaceSettingsRepositoryImpl import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl import com.android.systemui.biometrics.data.repository.PromptRepository @@ -47,6 +49,10 @@ interface BiometricsModule { @Binds @SysUISingleton + fun faceSettings(impl: FaceSettingsRepositoryImpl): FaceSettingsRepository + + @Binds + @SysUISingleton fun biometricPromptRepository(impl: PromptRepositoryImpl): PromptRepository @Binds diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepository.kt new file mode 100644 index 000000000000..3d5ed823f771 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepository.kt @@ -0,0 +1,57 @@ +/* + * 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.data.repository + +import android.os.Handler +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.util.settings.SecureSettings +import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject + +/** + * Repository for the global state of users Face Unlock preferences. + * + * Largely a wrapper around [SecureSettings]'s proxy to Settings.Secure. + */ +interface FaceSettingsRepository { + + /** Get Settings for the given user [id]. */ + fun forUser(id: Int?): FaceUserSettingsRepository +} + +@SysUISingleton +class FaceSettingsRepositoryImpl +@Inject +constructor( + @Main private val mainHandler: Handler, + private val secureSettings: SecureSettings, +) : FaceSettingsRepository { + + private val userSettings = ConcurrentHashMap<Int, FaceUserSettingsRepository>() + + override fun forUser(id: Int?): FaceUserSettingsRepository = + if (id != null) { + userSettings.computeIfAbsent(id) { _ -> + FaceUserSettingsRepositoryImpl(id, mainHandler, secureSettings).also { repo -> + repo.start() + } + } + } else { + FaceUserSettingsRepositoryImpl.Empty + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt new file mode 100644 index 000000000000..68c4a10fcfad --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt @@ -0,0 +1,88 @@ +/* + * 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.data.repository + +import android.database.ContentObserver +import android.os.Handler +import android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.util.settings.SecureSettings +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOf + +/** Settings for a user. */ +interface FaceUserSettingsRepository { + /** The user's id. */ + val userId: Int + + /** If BiometricPrompt should always require confirmation (overrides app's preference). */ + val alwaysRequireConfirmationInApps: Flow<Boolean> +} + +class FaceUserSettingsRepositoryImpl( + override val userId: Int, + @Main private val mainHandler: Handler, + private val secureSettings: SecureSettings, +) : FaceUserSettingsRepository { + + /** Indefinitely subscribe to user preference changes. */ + fun start() { + watch( + FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, + _alwaysRequireConfirmationInApps, + ) + } + + private var _alwaysRequireConfirmationInApps = MutableStateFlow(false) + override val alwaysRequireConfirmationInApps: Flow<Boolean> = + _alwaysRequireConfirmationInApps.asStateFlow() + + /** Defaults to use when no user is specified. */ + object Empty : FaceUserSettingsRepository { + override val userId = -1 + override val alwaysRequireConfirmationInApps = flowOf(false) + } + + private fun watch( + key: String, + toUpdate: MutableStateFlow<Boolean>, + defaultValue: Boolean = false, + ) = secureSettings.watch(userId, mainHandler, key, defaultValue) { v -> toUpdate.value = v } +} + +private fun SecureSettings.watch( + userId: Int, + handler: Handler, + key: String, + defaultValue: Boolean = false, + onChange: (Boolean) -> Unit, +) { + fun fetch(): Boolean = getIntForUser(key, if (defaultValue) 1 else 0, userId) > 0 + + registerContentObserverForUser( + key, + false /* notifyForDescendants */, + object : ContentObserver(handler) { + override fun onChange(selfChange: Boolean) = onChange(fetch()) + }, + userId + ) + + onChange(fetch()) +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt index b4dc272b71da..b35fbbc7bb32 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt @@ -1,3 +1,19 @@ +/* + * 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.data.repository import android.hardware.biometrics.PromptInfo @@ -12,6 +28,10 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map /** * A repository for the global state of BiometricPrompt. @@ -40,7 +60,7 @@ interface PromptRepository { * * Note: overlaps/conflicts with [PromptInfo.isConfirmationRequested], which needs clean up. */ - val isConfirmationRequired: StateFlow<Boolean> + val isConfirmationRequired: Flow<Boolean> /** Update the prompt configuration, which should be set before [isShowing]. */ fun setPrompt( @@ -48,7 +68,6 @@ interface PromptRepository { userId: Int, gatekeeperChallenge: Long?, kind: PromptKind, - requireConfirmation: Boolean = false, ) /** Unset the prompt info. */ @@ -56,8 +75,12 @@ interface PromptRepository { } @SysUISingleton -class PromptRepositoryImpl @Inject constructor(private val authController: AuthController) : - PromptRepository { +class PromptRepositoryImpl +@Inject +constructor( + private val faceSettings: FaceSettingsRepository, + private val authController: AuthController, +) : PromptRepository { override val isShowing: Flow<Boolean> = conflatedCallbackFlow { val callback = @@ -85,21 +108,30 @@ class PromptRepositoryImpl @Inject constructor(private val authController: AuthC private val _kind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.Biometric()) override val kind = _kind.asStateFlow() - private val _isConfirmationRequired: MutableStateFlow<Boolean> = MutableStateFlow(false) - override val isConfirmationRequired = _isConfirmationRequired.asStateFlow() + private val _faceSettings = + _userId.map { id -> faceSettings.forUser(id) }.distinctUntilChanged() + private val _faceSettingAlwaysRequireConfirmation = + _faceSettings.flatMapLatest { it.alwaysRequireConfirmationInApps }.distinctUntilChanged() + + private val _isConfirmationRequired = _promptInfo.map { it?.isConfirmationRequested ?: false } + override val isConfirmationRequired = + combine(_isConfirmationRequired, _faceSettingAlwaysRequireConfirmation) { + appRequiresConfirmation, + forceRequireConfirmation -> + forceRequireConfirmation || appRequiresConfirmation + } + .distinctUntilChanged() override fun setPrompt( promptInfo: PromptInfo, userId: Int, gatekeeperChallenge: Long?, kind: PromptKind, - requireConfirmation: Boolean, ) { _kind.value = kind _userId.value = userId _challenge.value = gatekeeperChallenge _promptInfo.value = promptInfo - _isConfirmationRequired.value = requireConfirmation } override fun unsetPrompt() { @@ -107,7 +139,6 @@ class PromptRepositoryImpl @Inject constructor(private val authController: AuthC _userId.value = null _challenge.value = null _kind.value = PromptKind.Biometric() - _isConfirmationRequired.value = false } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt index e6e07f9d7794..be99dd92f1bd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt @@ -59,13 +59,15 @@ interface PromptSelectorInteractor { */ val credentialKind: Flow<PromptKind> - /** If the API caller requested explicit confirmation after successful authentication. */ - val isConfirmationRequested: Flow<Boolean> + /** + * If the API caller or the user's personal preferences require explicit confirmation after + * successful authentication. + */ + val isConfirmationRequired: Flow<Boolean> /** Use biometrics for authentication. */ fun useBiometricsForAuthentication( promptInfo: PromptInfo, - requireConfirmation: Boolean, userId: Int, challenge: Long, modalities: BiometricModalities, @@ -114,10 +116,8 @@ constructor( } } - override val isConfirmationRequested: Flow<Boolean> = - promptRepository.promptInfo - .map { info -> info?.isConfirmationRequested ?: false } - .distinctUntilChanged() + override val isConfirmationRequired: Flow<Boolean> = + promptRepository.isConfirmationRequired.distinctUntilChanged() override val isCredentialAllowed: Flow<Boolean> = promptRepository.promptInfo @@ -142,7 +142,6 @@ constructor( override fun useBiometricsForAuthentication( promptInfo: PromptInfo, - requireConfirmation: Boolean, userId: Int, challenge: Long, modalities: BiometricModalities @@ -152,7 +151,6 @@ constructor( userId = userId, gatekeeperChallenge = challenge, kind = PromptKind.Biometric(modalities), - requireConfirmation = requireConfirmation, ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 34c8d9f6e894..6a7431e54034 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -158,7 +158,7 @@ object BiometricViewBinder { view.updateFingerprintAffordanceSize(iconController) } if (iconController is HackyCoexIconController) { - iconController.faceMode = !viewModel.isConfirmationRequested.first() + iconController.faceMode = !viewModel.isConfirmationRequired.first() } // the icon controller must be created before this happens for the legacy diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 0fffee61318a..05a536236de5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -61,8 +61,11 @@ constructor( /** If the user has successfully authenticated and confirmed (when explicitly required). */ val isAuthenticated: Flow<PromptAuthState> = _isAuthenticated.asStateFlow() - /** If the API caller requested explicit confirmation after successful authentication. */ - val isConfirmationRequested: Flow<Boolean> = interactor.isConfirmationRequested + /** + * If the API caller or the user's personal preferences require explicit confirmation after + * successful authentication. + */ + val isConfirmationRequired: Flow<Boolean> = interactor.isConfirmationRequired /** The kind of credential the user has. */ val credentialKind: Flow<PromptKind> = interactor.credentialKind @@ -91,7 +94,7 @@ constructor( _forceLargeSize, _forceMediumSize, modalities, - interactor.isConfirmationRequested, + interactor.isConfirmationRequired, fingerprintStartMode, ) { forceLarge, forceMedium, modalities, confirmationRequired, fpStartMode -> when { @@ -383,7 +386,7 @@ constructor( private suspend fun needsExplicitConfirmation(modality: BiometricModality): Boolean { val availableModalities = modalities.first() - val confirmationRequested = interactor.isConfirmationRequested.first() + val confirmationRequested = interactor.isConfirmationRequired.first() if (availableModalities.hasFaceAndFingerprint) { // coex only needs confirmation when face is successful, unless it happens on the diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index c833defdc444..256c63515fc8 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -149,19 +149,28 @@ constructor( * If the input is correct, the device will be unlocked and the lock screen and bouncer will be * dismissed and hidden. * + * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method + * supports auto-confirming, and the input's length is at least the code's length. Otherwise, + * `null` is returned. + * * @param input The input from the user to try to authenticate with. This can be a list of * different things, based on the current authentication method. - * @return `true` if the authentication succeeded and the device is now unlocked; `false` - * otherwise. + * @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit + * request to validate. + * @return `true` if the authentication succeeded and the device is now unlocked; `false` when + * authentication failed, `null` if the check was not performed. */ fun authenticate( input: List<Any>, - ): Boolean { + tryAutoConfirm: Boolean = false, + ): Boolean? { if (repository.throttling.value != null) { return false } - val isAuthenticated = authenticationInteractor.authenticate(input) + val isAuthenticated = + authenticationInteractor.authenticate(input, tryAutoConfirm) ?: return null + val failedAttempts = authenticationInteractor.failedAuthenticationAttempts.value when { isAuthenticated -> { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index 55929b566cf1..0146e406703c 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -50,7 +50,7 @@ class PasswordBouncerViewModel( /** Notifies that the user has pressed the key for attempting to authenticate the password. */ fun onAuthenticateKeyPressed() { - if (!interactor.authenticate(password.value.toCharArray().toList())) { + if (interactor.authenticate(password.value.toCharArray().toList()) != true) { showFailureAnimation() } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index d9ef75db6103..700703ee3560 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -153,9 +153,8 @@ class PatternBouncerViewModel( /** Notifies that the user has ended the drag gesture across the dot grid. */ fun onDragEnd() { - val isSuccessfullyAuthenticated = - interactor.authenticate(_selectedDots.value.map { it.toCoordinate() }) - if (!isSuccessfullyAuthenticated) { + val pattern = _selectedDots.value.map { it.toCoordinate() } + if (interactor.authenticate(pattern) != true) { showFailureAnimation() } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index 94d3d193e9cc..1944c74f1d6a 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -16,14 +16,19 @@ package com.android.systemui.bouncer.ui.viewmodel +import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +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 /** Holds UI state and handles user input for the PIN code bouncer UI. */ class PinBouncerViewModel( - private val applicationScope: CoroutineScope, + applicationScope: CoroutineScope, private val interactor: BouncerInteractor, isInputEnabled: StateFlow<Boolean>, ) : @@ -34,6 +39,42 @@ class PinBouncerViewModel( private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList()) val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries + /** The length of the hinted PIN, or null if pin length hint should not be shown. */ + val hintedPinLength: StateFlow<Int?> = + interactor.authenticationMethod + .map { authMethod -> computeHintedPinLength(authMethod) } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = computeHintedPinLength(interactor.authenticationMethod.value), + ) + + /** Appearance of the backspace button. */ + val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> = + combine(interactor.authenticationMethod, mutablePinEntries) { authMethod, enteredPin -> + computeBackspaceButtonAppearance(authMethod, enteredPin) + } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = + computeBackspaceButtonAppearance( + interactor.authenticationMethod.value, + mutablePinEntries.value + ), + ) + + /** Appearance of the confirm button. */ + val confirmButtonAppearance: StateFlow<ActionButtonAppearance> = + interactor.authenticationMethod + .map { authMethod -> computeConfirmButtonAppearance(authMethod) } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = + computeConfirmButtonAppearance(interactor.authenticationMethod.value), + ) + /** Notifies that the UI has been shown to the user. */ fun onShown() { interactor.resetMessage() @@ -46,6 +87,8 @@ class PinBouncerViewModel( } mutablePinEntries.value += EnteredKey(input) + + tryAuthenticate(useAutoConfirm = true) } /** Notifies that the user clicked the backspace button. */ @@ -63,14 +106,72 @@ class PinBouncerViewModel( /** Notifies that the user clicked the "enter" button. */ fun onAuthenticateButtonClicked() { - if (!interactor.authenticate(mutablePinEntries.value.map { it.input })) { + tryAuthenticate(useAutoConfirm = false) + } + + private fun tryAuthenticate(useAutoConfirm: Boolean) { + val pinCode = mutablePinEntries.value.map { it.input } + val isSuccess = interactor.authenticate(pinCode, useAutoConfirm) ?: return + + if (!isSuccess) { showFailureAnimation() } mutablePinEntries.value = emptyList() } + + private fun isAutoConfirmEnabled(authMethodModel: AuthenticationMethodModel): Boolean { + return (authMethodModel as? AuthenticationMethodModel.Pin)?.autoConfirm == true + } + + private fun autoConfirmPinLength(authMethodModel: AuthenticationMethodModel): Int? { + if (!isAutoConfirmEnabled(authMethodModel)) return null + + return (authMethodModel as? AuthenticationMethodModel.Pin)?.code?.size + } + + private fun computeHintedPinLength(authMethodModel: AuthenticationMethodModel): Int? { + // Hinting is enabled for 6-digit codes only + return autoConfirmPinLength(authMethodModel).takeIf { it == HINTING_PASSCODE_LENGTH } + } + + private fun computeBackspaceButtonAppearance( + authMethodModel: AuthenticationMethodModel, + enteredPin: List<EnteredKey> + ): ActionButtonAppearance { + val isAutoConfirmEnabled = isAutoConfirmEnabled(authMethodModel) + val isEmpty = enteredPin.isEmpty() + + return when { + isAutoConfirmEnabled && isEmpty -> ActionButtonAppearance.Hidden + isAutoConfirmEnabled -> ActionButtonAppearance.Subtle + else -> ActionButtonAppearance.Shown + } + } + private fun computeConfirmButtonAppearance( + authMethodModel: AuthenticationMethodModel + ): ActionButtonAppearance { + return if (isAutoConfirmEnabled(authMethodModel)) { + ActionButtonAppearance.Hidden + } else { + ActionButtonAppearance.Shown + } + } } +/** Appearance of pin-pad action buttons. */ +enum class ActionButtonAppearance { + /** Button must not be shown. */ + Hidden, + /** Button is shown, but with no background to make it less prominent. */ + Subtle, + /** Button is shown. */ + Shown, +} + +/** Auto-confirm passcodes of exactly 6 digits show a length hint, see http://shortn/_IXlmSNbDh6 */ +private const val HINTING_PASSCODE_LENGTH = 6 + private var nextSequenceNumber = 1 /** diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 757ebf45e9ad..ffd836b3230f 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -18,6 +18,7 @@ package com.android.systemui.clipboardoverlay; import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; + import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED; @@ -33,6 +34,7 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT; import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT; +import static com.android.systemui.flags.Flags.CLIPBOARD_SHARED_TRANSITIONS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -47,6 +49,7 @@ import android.hardware.input.InputManager; import android.net.Uri; import android.os.Looper; import android.provider.DeviceConfig; +import android.util.Log; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputMonitor; @@ -54,6 +57,7 @@ import android.view.MotionEvent; import android.view.WindowInsets; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; @@ -73,7 +77,8 @@ import javax.inject.Inject; /** * Controls state and UI for the overlay that appears when something is added to the clipboard */ -public class ClipboardOverlayController implements ClipboardListener.ClipboardOverlay { +public class ClipboardOverlayController implements ClipboardListener.ClipboardOverlay, + ClipboardOverlayView.ClipboardOverlayCallbacks { private static final String TAG = "ClipboardOverlayCtrlr"; /** Constants for screenshot/copy deconflicting */ @@ -92,6 +97,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private final FeatureFlags mFeatureFlags; private final Executor mBgExecutor; private final ClipboardImageLoader mClipboardImageLoader; + private final ClipboardTransitionExecutor mTransitionExecutor; private final ClipboardOverlayView mView; @@ -179,10 +185,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv ClipboardOverlayUtils clipboardUtils, @Background Executor bgExecutor, ClipboardImageLoader clipboardImageLoader, + ClipboardTransitionExecutor transitionExecutor, UiEventLogger uiEventLogger) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mClipboardImageLoader = clipboardImageLoader; + mTransitionExecutor = transitionExecutor; mClipboardLogger = new ClipboardLogger(uiEventLogger); @@ -200,7 +208,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardUtils = clipboardUtils; mBgExecutor = bgExecutor; - mView.setCallbacks(mClipboardCallbacks); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + mView.setCallbacks(this); + } else { + mView.setCallbacks(mClipboardCallbacks); + } mWindow.withWindowAttached(() -> { mWindow.setContentView(mView); @@ -209,16 +221,24 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv }); mTimeoutHandler.setOnTimeoutRunnable(() -> { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_TIMED_OUT); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT); + animateOut(); + } }); mCloseDialogsReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + animateOut(); + } } } }; @@ -229,8 +249,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv @Override public void onReceive(Context context, Intent intent) { if (SCREENSHOT_ACTION.equals(intent.getAction())) { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + animateOut(); + } } } }; @@ -457,8 +481,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv remoteAction.ifPresent(action -> { mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN); mView.post(() -> mView.setActionChip(action, () -> { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_ACTION_TAPPED); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED); + animateOut(); + } })); }); } @@ -500,8 +528,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { if (!mView.isInTouchRegion( (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_TAP_OUTSIDE); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE); + animateOut(); + } } } } @@ -551,12 +583,16 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mEnterAnimator.start(); } + private void finish(ClipboardOverlayEvent event) { + finish(event, null); + } + private void animateOut() { if (mExitAnimator != null && mExitAnimator.isRunning()) { return; } - Animator anim = mView.getExitAnimation(); - anim.addListener(new AnimatorListenerAdapter() { + mExitAnimator = mView.getExitAnimation(); + mExitAnimator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @Override @@ -573,8 +609,47 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv } } }); - mExitAnimator = anim; - anim.start(); + mExitAnimator.start(); + } + + private void finish(ClipboardOverlayEvent event, @Nullable Intent intent) { + if (mExitAnimator != null && mExitAnimator.isRunning()) { + return; + } + mExitAnimator = mView.getExitAnimation(); + mExitAnimator.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + super.onAnimationCancel(animation); + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (!mCancelled) { + mClipboardLogger.logSessionComplete(event); + if (intent != null) { + mContext.startActivity(intent); + } + hideImmediate(); + } + } + }); + mExitAnimator.start(); + } + + private void finishWithSharedTransition(ClipboardOverlayEvent event, Intent intent) { + if (mExitAnimator != null && mExitAnimator.isRunning()) { + return; + } + mClipboardLogger.logSessionComplete(event); + mExitAnimator = mView.getFadeOutAnimation(); + mExitAnimator.start(); + mTransitionExecutor.startSharedTransition( + mWindow, mView.getPreview(), intent, this::hideImmediate); } void hideImmediate() { @@ -613,6 +688,76 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardLogger.reset(); } + @Override + public void onDismissButtonTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_DISMISS_TAPPED); + } + } + + @Override + public void onRemoteCopyButtonTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED, + IntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext)); + } + } + + @Override + public void onShareButtonTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + if (mClipboardModel.getType() != ClipboardModel.Type.OTHER) { + finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED, + IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext)); + } + } + } + + @Override + public void onPreviewTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + switch (mClipboardModel.getType()) { + case TEXT: + finish(CLIPBOARD_OVERLAY_EDIT_TAPPED, + IntentCreator.getTextEditorIntent(mContext)); + break; + case IMAGE: + finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, + IntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext)); + break; + default: + Log.w(TAG, "Got preview tapped callback for non-editable type " + + mClipboardModel.getType()); + } + } + } + + @Override + public void onMinimizedViewTapped() { + animateFromMinimized(); + } + + @Override + public void onInteraction() { + if (!mClipboardModel.isRemote()) { + mTimeoutHandler.resetTimeout(); + } + } + + @Override + public void onSwipeDismissInitiated(Animator animator) { + if (mExitAnimator != null && mExitAnimator.isRunning()) { + mExitAnimator.cancel(); + } + mExitAnimator = animator; + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED); + } + + @Override + public void onDismissComplete() { + hideImmediate(); + } + static class ClipboardLogger { private final UiEventLogger mUiEventLogger; private String mClipSource; diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java index 28c57d31a4f3..a76d2ea816a7 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java @@ -254,6 +254,10 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { }); } + View getPreview() { + return mClipboardPreview; + } + void showImagePreview(@Nullable Bitmap thumbnail) { if (thumbnail == null) { mHiddenPreview.setText(mContext.getString(R.string.clipboard_text_hidden)); @@ -368,6 +372,19 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { return enterAnim; } + Animator getFadeOutAnimation() { + ValueAnimator alphaAnim = ValueAnimator.ofFloat(1, 0); + alphaAnim.addUpdateListener(animation -> { + float alpha = (float) animation.getAnimatedValue(); + mActionContainer.setAlpha(alpha); + mActionContainerBackground.setAlpha(alpha); + mPreviewBorder.setAlpha(alpha); + mDismissButton.setAlpha(alpha); + }); + alphaAnim.setDuration(300); + return alphaAnim; + } + Animator getExitAnimation() { TimeInterpolator linearInterpolator = new LinearInterpolator(); TimeInterpolator scaleInterpolator = new PathInterpolator(.3f, 0, 1f, 1f); diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt new file mode 100644 index 000000000000..0b8e83edc88a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt @@ -0,0 +1,93 @@ +/* + * 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.clipboardoverlay + +import android.app.ActivityOptions +import android.app.ExitTransitionCoordinator +import android.content.Context +import android.content.Intent +import android.os.RemoteException +import android.util.Log +import android.util.Pair +import android.view.IRemoteAnimationFinishedCallback +import android.view.IRemoteAnimationRunner +import android.view.RemoteAnimationAdapter +import android.view.RemoteAnimationTarget +import android.view.View +import android.view.Window +import android.view.WindowManagerGlobal +import com.android.internal.app.ChooserActivity +import com.android.systemui.settings.DisplayTracker +import javax.inject.Inject + +class ClipboardTransitionExecutor +@Inject +constructor(val context: Context, val displayTracker: DisplayTracker) { + fun startSharedTransition(window: Window, view: View, intent: Intent, onReady: Runnable) { + val transition: Pair<ActivityOptions, ExitTransitionCoordinator> = + ActivityOptions.startSharedElementAnimation( + window, + object : ExitTransitionCoordinator.ExitTransitionCallbacks { + override fun isReturnTransitionAllowed(): Boolean { + return false + } + + override fun hideSharedElements() { + onReady.run() + } + + override fun onFinish() {} + }, + null, + Pair.create(view, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME) + ) + transition.second.startExit() + context.startActivity(intent, transition.first.toBundle()) + val runner = RemoteAnimationAdapter(NULL_ACTIVITY_TRANSITION, 0, 0) + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionRemote(runner, displayTracker.defaultDisplayId) + } catch (e: Exception) { + Log.e(TAG, "Error overriding clipboard app transition", e) + } + } + + private val TAG: String = "ClipboardTransitionExec" + + /** + * This is effectively a no-op, but we need something non-null to pass in, in order to + * successfully override the pending activity entrance animation. + */ + private val NULL_ACTIVITY_TRANSITION: IRemoteAnimationRunner.Stub = + object : IRemoteAnimationRunner.Stub() { + override fun onAnimationStart( + transit: Int, + apps: Array<RemoteAnimationTarget>, + wallpapers: Array<RemoteAnimationTarget>, + nonApps: Array<RemoteAnimationTarget>, + finishedCallback: IRemoteAnimationFinishedCallback + ) { + try { + finishedCallback.onAnimationFinished() + } catch (e: RemoteException) { + Log.e(TAG, "Error finishing screenshot remote animation", e) + } + } + + override fun onAnimationCancelled() {} + } +} diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt index 4173bdc3c261..b15c60e62ead 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt @@ -24,6 +24,9 @@ import androidx.lifecycle.LifecycleOwner import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.util.time.SystemClock /** @@ -66,4 +69,11 @@ interface BaseComposeFacade { viewModel: MultiShadeViewModel, clock: SystemClock, ): View + + /** Create a [View] to represent [viewModel] on screen. */ + fun createSceneContainerView( + context: Context, + viewModel: SceneContainerViewModel, + sceneByKey: Map<SceneKey, Scene>, + ): View } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 2262d8ab2000..f68bd49230d9 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -40,7 +40,6 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.rotationlock.RotationLockModule; -import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -104,7 +103,6 @@ import javax.inject.Named; QSModule.class, ReferenceScreenshotModule.class, RotationLockModule.class, - SceneContainerFrameworkModule.class, StatusBarEventsModule.class, StartCentralSurfacesModule.class, VolumeModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 52355f34a71d..d82bf587212e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -16,7 +16,6 @@ package com.android.systemui.dagger; -import com.android.keyguard.clock.ClockOptionsProvider; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.CoreStartable; import com.android.systemui.Dependency; @@ -252,10 +251,5 @@ public interface SysUIComponent { /** * Member injection into the supplied argument. */ - void inject(ClockOptionsProvider clockOptionsProvider); - - /** - * Member injection into the supplied argument. - */ void inject(PeopleProvider peopleProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 4ff16b8f29e3..9ab9e7a76a25 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -23,7 +23,6 @@ import android.service.dreams.IDreamManager; import androidx.annotation.Nullable; import com.android.internal.statusbar.IStatusBarService; -import com.android.keyguard.clock.ClockInfoModule; import com.android.keyguard.dagger.ClockRegistryModule; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.BootCompleteCache; @@ -39,6 +38,7 @@ import com.android.systemui.biometrics.FingerprintReEnrollNotification; import com.android.systemui.biometrics.UdfpsDisplayModeProvider; import com.android.systemui.biometrics.dagger.BiometricsModule; import com.android.systemui.biometrics.dagger.UdfpsModule; +import com.android.systemui.bouncer.ui.BouncerViewModule; import com.android.systemui.classifier.FalsingModule; import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule; import com.android.systemui.common.ui.data.repository.CommonRepositoryModule; @@ -53,7 +53,6 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FlagsModule; import com.android.systemui.keyboard.KeyboardModule; -import com.android.systemui.bouncer.ui.BouncerViewModule; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.log.dagger.MonitorLog; import com.android.systemui.log.table.TableLogBuffer; @@ -74,6 +73,7 @@ import com.android.systemui.qs.QSFragmentStartableModule; import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; import com.android.systemui.retail.dagger.RetailModeModule; +import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenrecord.ScreenRecordModule; import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; @@ -88,6 +88,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.connectivity.ConnectivityModule; +import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -160,7 +161,6 @@ import javax.inject.Named; BiometricsModule.class, BouncerViewModule.class, ClipboardOverlayModule.class, - ClockInfoModule.class, ClockRegistryModule.class, CommonRepositoryModule.class, ConnectivityModule.class, @@ -168,6 +168,7 @@ import javax.inject.Named; DreamModule.class, ControlsModule.class, DemoModeModule.class, + DisableFlagsModule.class, FalsingModule.class, FlagsModule.class, SystemPropertiesFlagsModule.class, @@ -186,6 +187,7 @@ import javax.inject.Named; QRCodeScannerModule.class, QSFragmentStartableModule.class, RetailModeModule.class, + SceneContainerFrameworkModule.class, ScreenshotModule.class, SensorModule.class, SecurityRepositoryModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt new file mode 100644 index 000000000000..4069bc7d73d0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt @@ -0,0 +1,198 @@ +/* + * 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.decor + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PixelFormat +import android.graphics.drawable.Drawable +import android.util.Size +import java.io.PrintWriter + +/** + * Rounded corner delegate that handles incoming debug commands and can convert them to path + * drawables to be shown instead of the system-defined rounded corners. + * + * These debug corners are expected to supersede the system-defined corners + */ +class DebugRoundedCornerDelegate : RoundedCornerResDelegate { + override var hasTop: Boolean = false + private set + override var topRoundedDrawable: Drawable? = null + private set + override var topRoundedSize: Size = Size(0, 0) + private set + + override var hasBottom: Boolean = false + private set + override var bottomRoundedDrawable: Drawable? = null + private set + override var bottomRoundedSize: Size = Size(0, 0) + private set + + override var physicalPixelDisplaySizeRatio: Float = 1f + set(value) { + if (field == value) { + return + } + field = value + reloadMeasures() + } + + var color: Int = Color.RED + set(value) { + if (field == value) { + return + } + + field = value + paint.color = field + } + + var paint = + Paint().apply { + color = Color.RED + style = Paint.Style.FILL + } + + override fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + // nop -- debug corners draw the same on every display + } + + fun applyNewDebugCorners( + topCorner: DebugRoundedCornerModel, + bottomCorner: DebugRoundedCornerModel, + ) { + hasTop = true + topRoundedDrawable = topCorner.toPathDrawable(paint) + topRoundedSize = topCorner.size() + + hasBottom = true + bottomRoundedDrawable = bottomCorner.toPathDrawable(paint) + bottomRoundedSize = bottomCorner.size() + } + + /** + * Remove accumulated debug state by clearing out the drawables and setting [hasTop] and + * [hasBottom] to false. + */ + fun removeDebugState() { + hasTop = false + topRoundedDrawable = null + topRoundedSize = Size(0, 0) + + hasBottom = false + bottomRoundedDrawable = null + bottomRoundedSize = Size(0, 0) + } + + /** + * Scaling here happens when the display resolution is changed. This logic is exactly the same + * as in [RoundedCornerResDelegateImpl] + */ + private fun reloadMeasures() { + topRoundedDrawable?.let { topRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) } + bottomRoundedDrawable?.let { + bottomRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) + } + + if (physicalPixelDisplaySizeRatio != 1f) { + if (topRoundedSize.width != 0) { + topRoundedSize = + Size( + (physicalPixelDisplaySizeRatio * topRoundedSize.width + 0.5f).toInt(), + (physicalPixelDisplaySizeRatio * topRoundedSize.height + 0.5f).toInt() + ) + } + if (bottomRoundedSize.width != 0) { + bottomRoundedSize = + Size( + (physicalPixelDisplaySizeRatio * bottomRoundedSize.width + 0.5f).toInt(), + (physicalPixelDisplaySizeRatio * bottomRoundedSize.height + 0.5f).toInt() + ) + } + } + } + + fun dump(pw: PrintWriter) { + pw.println("DebugRoundedCornerDelegate state:") + pw.println(" hasTop=$hasTop") + pw.println(" hasBottom=$hasBottom") + pw.println(" topRoundedSize(w,h)=(${topRoundedSize.width},${topRoundedSize.height})") + pw.println( + " bottomRoundedSize(w,h)=(${bottomRoundedSize.width},${bottomRoundedSize.height})" + ) + pw.println(" physicalPixelDisplaySizeRatio=$physicalPixelDisplaySizeRatio") + } +} + +/** Encapsulates the data coming in from the command line args and turns into a [PathDrawable] */ +data class DebugRoundedCornerModel( + val path: Path, + val width: Int, + val height: Int, + val scaleX: Float, + val scaleY: Float, +) { + fun size() = Size(width, height) + + fun toPathDrawable(paint: Paint) = + PathDrawable( + path, + width, + height, + scaleX, + scaleY, + paint, + ) +} + +/** + * PathDrawable accepts paths from the command line via [DebugRoundedCornerModel], and renders them + * in the canvas provided by the screen decor rounded corner provider + */ +class PathDrawable( + val path: Path, + val width: Int, + val height: Int, + val scaleX: Float = 1f, + val scaleY: Float = 1f, + val paint: Paint, +) : Drawable() { + private var cf: ColorFilter? = null + + override fun draw(canvas: Canvas) { + if (scaleX != 1f || scaleY != 1f) { + canvas.scale(scaleX, scaleY) + } + canvas.drawPath(path, paint) + } + + override fun getIntrinsicHeight(): Int = height + override fun getIntrinsicWidth(): Int = width + + override fun getOpacity(): Int = PixelFormat.OPAQUE + + override fun setAlpha(alpha: Int) {} + + override fun setColorFilter(colorFilter: ColorFilter?) { + cf = colorFilter + } +} diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt index 8b4aeefb6ed4..c64766a3eb3c 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt @@ -27,44 +27,50 @@ import com.android.systemui.Dumpable import com.android.systemui.R import java.io.PrintWriter -class RoundedCornerResDelegate( +interface RoundedCornerResDelegate { + val hasTop: Boolean + val topRoundedDrawable: Drawable? + val topRoundedSize: Size + + val hasBottom: Boolean + val bottomRoundedDrawable: Drawable? + val bottomRoundedSize: Size + + var physicalPixelDisplaySizeRatio: Float + + fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) +} + +/** + * Delegate for the device-default rounded corners. These will always be loaded from the config + * values `R.array.config_roundedCornerTopDrawableArray` and `R.drawable.rounded_corner_top` + */ +class RoundedCornerResDelegateImpl( private val res: Resources, private var displayUniqueId: String? -) : Dumpable { - - private val density: Float - get() = res.displayMetrics.density +) : RoundedCornerResDelegate, Dumpable { private var reloadToken: Int = 0 - var hasTop: Boolean = false + override var hasTop: Boolean = false private set - var hasBottom: Boolean = false + override var hasBottom: Boolean = false private set - var topRoundedDrawable: Drawable? = null + override var topRoundedDrawable: Drawable? = null private set - var bottomRoundedDrawable: Drawable? = null + override var bottomRoundedDrawable: Drawable? = null private set - var topRoundedSize = Size(0, 0) + override var topRoundedSize = Size(0, 0) private set - var bottomRoundedSize = Size(0, 0) + override var bottomRoundedSize = Size(0, 0) private set - var tuningSizeFactor: Int? = null - set(value) { - if (field == value) { - return - } - field = value - reloadMeasures() - } - - var physicalPixelDisplaySizeRatio: Float = 1f + override var physicalPixelDisplaySizeRatio: Float = 1f set(value) { if (field == value) { return @@ -78,7 +84,7 @@ class RoundedCornerResDelegate( reloadMeasures() } - fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + override fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { if (displayUniqueId != newDisplayUniqueId) { displayUniqueId = newDisplayUniqueId newReloadToken ?.let { reloadToken = it } @@ -122,19 +128,6 @@ class RoundedCornerResDelegate( bottomRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) } - tuningSizeFactor?.let { - if (it <= 0) { - return - } - val length: Int = (it * density).toInt() - if (topRoundedSize.width > 0) { - topRoundedSize = Size(length, length) - } - if (bottomRoundedSize.width > 0) { - bottomRoundedSize = Size(length, length) - } - } - if (physicalPixelDisplaySizeRatio != 1f) { if (topRoundedSize.width != 0) { topRoundedSize = Size( diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index d173fe5aef03..b50c6dea4a22 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -249,10 +249,18 @@ object Flags { @JvmField val DELAY_BOUNCER = unreleasedFlag(235, "delay_bouncer", teamfood = true) + + /** Keyguard Migration */ + /** Migrate the indication area to the new keyguard root view. */ // TODO(b/280067944): Tracking bug. @JvmField - val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area") + val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true) + + /** Migrate the lock icon view to the new keyguard root view. */ + // TODO(b/286552209): Tracking bug. + @JvmField + val MIGRATE_LOCK_ICON = unreleasedFlag(238, "migrate_lock_icon") /** Whether to listen for fingerprint authentication over keyguard occluding activities. */ // TODO(b/283260512): Tracking bug. @@ -262,7 +270,11 @@ object Flags { /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */ // TODO(b/286563884): Tracking bug @JvmField - val KEYGUARD_TALKBACK_FIX = unreleasedFlag(238, "keyguard_talkback_fix") + val KEYGUARD_TALKBACK_FIX = releasedFlag(238, "keyguard_talkback_fix") + + // TODO(b/287268101): Tracking bug. + @JvmField + val TRANSIT_CLOCK = unreleasedFlag(239, "lockscreen_custom_transit_clock") // 300 - power menu // TODO(b/254512600): Tracking Bug @@ -478,7 +490,7 @@ object Flags { @Keep @JvmField val WM_CAPTION_ON_SHELL = - sysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", default = false) + sysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", default = true) @Keep @JvmField @@ -667,8 +679,7 @@ object Flags { @JvmField val ENABLE_USI_BATTERY_NOTIFICATIONS = releasedFlag(2302, "enable_usi_battery_notifications") - @JvmField val ENABLE_STYLUS_EDUCATION = - unreleasedFlag(2303, "enable_stylus_education", teamfood = true) + @JvmField val ENABLE_STYLUS_EDUCATION = releasedFlag(2303, "enable_stylus_education") // 2400 - performance tools and debugging info // TODO(b/238923086): Tracking Bug @@ -730,6 +741,12 @@ object Flags { val ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD = releasedFlag(2900, "zj_285570694_lockscreen_transition_from_aod") + // 3000 - dream + // TODO(b/285059790) : Tracking Bug + @JvmField + val LOCKSCREEN_WALLPAPER_DREAM_ENABLED = + unreleasedFlag(3000, name = "enable_lockscreen_wallpaper_dream") + // TODO(b/283084712): Tracking Bug @JvmField val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations") @@ -739,6 +756,11 @@ object Flags { val BIGPICTURE_NOTIFICATION_LAZY_LOADING = unreleasedFlag(283447257, "bigpicture_notification_lazy_loading") + // TODO(b/283740863): Tracking Bug + @JvmField + val ENABLE_NEW_PRIVACY_DIALOG = + unreleasedFlag(283740863, "enable_new_privacy_dialog", teamfood = false) + // 2900 - CentralSurfaces-related flags // TODO(b/285174336): Tracking Bug diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 52483646f01e..040ee7938f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -250,6 +250,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms protected Handler mMainHandler; private int mSmallestScreenWidthDp; + private int mOrientation; private final Optional<CentralSurfaces> mCentralSurfacesOptional; private final ShadeController mShadeController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -394,6 +395,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mRingerModeTracker = ringerModeTracker; mMainHandler = handler; mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp; + mOrientation = resources.getConfiguration().orientation; mCentralSurfacesOptional = centralSurfacesOptional; mShadeController = shadeController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -750,8 +752,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @Override public void onConfigChanged(Configuration newConfig) { if (mDialog != null && mDialog.isShowing() - && (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp)) { + && (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp + || newConfig.orientation != mOrientation)) { mSmallestScreenWidthDp = newConfig.smallestScreenWidthDp; + mOrientation = newConfig.orientation; mDialog.refreshDialog(); } } @@ -2312,6 +2316,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setTitle(getContext().getString( + com.android.systemui.R.string.accessibility_quick_settings_power_menu)); initializeLayout(); mWindowDimAmount = getWindow().getAttributes().dimAmount; getOnBackInvokedDispatcher().registerOnBackInvokedCallback( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 94227bccfced..e6053fb3e352 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -80,6 +80,8 @@ import com.android.wm.shell.util.CounterRotator; import com.android.wm.shell.util.TransitionUtil; import java.util.ArrayList; +import java.util.Map; +import java.util.WeakHashMap; import javax.inject.Inject; @@ -187,12 +189,13 @@ public class KeyguardService extends Service { final IRemoteAnimationRunner runner, final boolean lockscreenLiveWallpaperEnabled) { return new IRemoteTransition.Stub() { + @GuardedBy("mLeashMap") private final ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = new ArrayMap<>(); private final CounterRotator mCounterRotator = new CounterRotator(); - @GuardedBy("mLeashMap") - private IRemoteTransitionFinishedCallback mFinishCallback = null; + private final Map<IBinder, IRemoteTransitionFinishedCallback> mFinishCallbacks = + new WeakHashMap<>(); @Override public void startAnimation(IBinder transition, TransitionInfo info, @@ -200,40 +203,38 @@ public class KeyguardService extends Service { throws RemoteException { Slog.d(TAG, "Starts IRemoteAnimationRunner: info=" + info); + final RemoteAnimationTarget[] apps; + final RemoteAnimationTarget[] wallpapers; + final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0]; synchronized (mLeashMap) { - final RemoteAnimationTarget[] apps = - wrap(info, false /* wallpapers */, t, mLeashMap, mCounterRotator); - final RemoteAnimationTarget[] wallpapers = - wrap(info, true /* wallpapers */, t, mLeashMap, mCounterRotator); - final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0]; - - // Set alpha back to 1 for the independent changes because we will be animating - // children instead. - for (TransitionInfo.Change chg : info.getChanges()) { - if (TransitionInfo.isIndependent(chg, info)) { - t.setAlpha(chg.getLeash(), 1.f); - } - } - initAlphaForAnimationTargets(t, apps); - if (lockscreenLiveWallpaperEnabled) { - initAlphaForAnimationTargets(t, wallpapers); + apps = wrap(info, false /* wallpapers */, t, mLeashMap, mCounterRotator); + wallpapers = wrap(info, true /* wallpapers */, t, mLeashMap, mCounterRotator); + mFinishCallbacks.put(transition, finishCallback); + } + + // Set alpha back to 1 for the independent changes because we will be animating + // children instead. + for (TransitionInfo.Change chg : info.getChanges()) { + if (TransitionInfo.isIndependent(chg, info)) { + t.setAlpha(chg.getLeash(), 1.f); } - t.apply(); - mFinishCallback = finishCallback; - runner.onAnimationStart( - getTransitionOldType(info.getType(), info.getFlags(), apps), - apps, wallpapers, nonApps, - new IRemoteAnimationFinishedCallback.Stub() { - @Override - public void onAnimationFinished() throws RemoteException { - synchronized (mLeashMap) { - Slog.d(TAG, "Finish IRemoteAnimationRunner."); - finish(); - } - } - } - ); } + initAlphaForAnimationTargets(t, apps); + if (lockscreenLiveWallpaperEnabled) { + initAlphaForAnimationTargets(t, wallpapers); + } + t.apply(); + + runner.onAnimationStart( + getTransitionOldType(info.getType(), info.getFlags(), apps), + apps, wallpapers, nonApps, + new IRemoteAnimationFinishedCallback.Stub() { + @Override + public void onAnimationFinished() throws RemoteException { + Slog.d(TAG, "Finish IRemoteAnimationRunner."); + finish(transition); + } + }); } public void mergeAnimation(IBinder candidateTransition, TransitionInfo candidateInfo, @@ -247,10 +248,8 @@ public class KeyguardService extends Service { } try { - synchronized (mLeashMap) { - runner.onAnimationCancelled(); - finish(); - } + runner.onAnimationCancelled(); + finish(currentTransition); } catch (RemoteException e) { // nothing, we'll just let it finish on its own I guess. } @@ -264,18 +263,21 @@ public class KeyguardService extends Service { } } - @GuardedBy("mLeashMap") - private void finish() throws RemoteException { + private void finish(IBinder transition) throws RemoteException { + IRemoteTransitionFinishedCallback finishCallback = null; SurfaceControl.Transaction finishTransaction = null; - if (mCounterRotator.getSurface() != null - && mCounterRotator.getSurface().isValid()) { - finishTransaction = new SurfaceControl.Transaction(); - mCounterRotator.cleanUp(finishTransaction); + + synchronized (mLeashMap) { + if (mCounterRotator.getSurface() != null + && mCounterRotator.getSurface().isValid()) { + finishTransaction = new SurfaceControl.Transaction(); + mCounterRotator.cleanUp(finishTransaction); + } + mLeashMap.clear(); + finishCallback = mFinishCallbacks.remove(transition); } - mLeashMap.clear(); - final IRemoteTransitionFinishedCallback finishCallback = mFinishCallback; + if (finishCallback != null) { - mFinishCallback = null; finishCallback.onTransitionFinished(null /* wct */, finishTransaction); } else if (finishTransaction != null) { finishTransaction.apply(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 05c23aefe974..e8881a482765 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -47,9 +47,10 @@ constructor( private var indicationAreaHandle: DisposableHandle? = null override fun start() { - bindIndicationArea( + val notificationPanel = notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup - ) + bindIndicationArea(notificationPanel) + bindLockIconView(notificationPanel) } fun bindIndicationArea(legacyParent: ViewGroup) { @@ -74,4 +75,16 @@ constructor( indicationController ) } + + private fun bindLockIconView(legacyParent: ViewGroup) { + if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) { + legacyParent.requireViewById<View>(R.id.lock_icon_view).let { + legacyParent.removeView(it) + } + } else { + keyguardRootView.findViewById<View?>(R.id.lock_icon_view)?.let { + keyguardRootView.removeView(it) + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index e7581255bebc..a5d209680a79 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -138,6 +138,7 @@ import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.flags.SystemPropertiesHelper; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.log.SessionTracker; @@ -171,6 +172,7 @@ import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -284,6 +286,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * keyguard to show even if it is disabled for the current user. */ public static final String OPTION_FORCE_SHOW = "force_show"; + public static final String SYS_BOOT_REASON_PROP = "sys.boot.reason.last"; + public static final String REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"; private final DreamOverlayStateController mDreamOverlayStateController; /** The stream type that the lock sounds are tied to. */ @@ -322,6 +326,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final SecureSettings mSecureSettings; private final SystemSettings mSystemSettings; private final SystemClock mSystemClock; + private SystemPropertiesHelper mSystemPropertiesHelper; /** * Used to keep the device awake while to ensure the keyguard finishes opening before @@ -853,7 +858,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, strongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(currentUser); if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) { - return KeyguardSecurityView.PROMPT_REASON_RESTART; + String reasonForReboot = mSystemPropertiesHelper.get(SYS_BOOT_REASON_PROP); + if (Objects.equals(reasonForReboot, REBOOT_MAINLINE_UPDATE)) { + return KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE; + } else { + return KeyguardSecurityView.PROMPT_REASON_RESTART; + } } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) { return KeyguardSecurityView.PROMPT_REASON_TIMEOUT; } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN) != 0) { @@ -1250,7 +1260,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, @Override public void onPrimaryBouncerShowingChanged() { synchronized (KeyguardViewMediator.this) { - if (mKeyguardStateController.isPrimaryBouncerShowing()) { + if (mKeyguardStateController.isPrimaryBouncerShowing() + && !mKeyguardStateController.isKeyguardGoingAway()) { mPendingPinLock = false; } adjustStatusBarLocked(mKeyguardStateController.isPrimaryBouncerShowing(), false); @@ -1314,7 +1325,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, SystemSettings systemSettings, SystemClock systemClock, @Main CoroutineDispatcher mainDispatcher, - Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel) { + Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, + SystemPropertiesHelper systemPropertiesHelper) { mContext = context; mUserTracker = userTracker; mFalsingCollector = falsingCollector; @@ -1331,6 +1343,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mSecureSettings = secureSettings; mSystemSettings = systemSettings; mSystemClock = systemClock; + mSystemPropertiesHelper = systemPropertiesHelper; mStatusBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mKeyguardDisplayManager = keyguardDisplayManager; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt index a44df7e11fa1..d8eb81caa76c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt @@ -16,6 +16,8 @@ package com.android.systemui.keyguard.dagger +import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.NoopDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor import dagger.Binds @@ -32,4 +34,9 @@ import dagger.Module interface KeyguardFaceAuthNotSupportedModule { @Binds fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor + + @Binds + fun deviceEntryFaceAuthRepository( + impl: NoopDeviceEntryFaceAuthRepository + ): DeviceEntryFaceAuthRepository } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 6d6205cde53f..61bacbda98a5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -42,6 +42,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.SystemPropertiesHelper; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -140,7 +141,8 @@ public class KeyguardModule { SystemSettings systemSettings, SystemClock systemClock, @Main CoroutineDispatcher mainDispatcher, - Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel) { + Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, + SystemPropertiesHelper systemPropertiesHelper) { return new KeyguardViewMediator( context, uiEventLogger, @@ -179,7 +181,8 @@ public class KeyguardModule { systemSettings, systemClock, mainDispatcher, - dreamingToLockscreenTransitionViewModel); + dreamingToLockscreenTransitionViewModel, + systemPropertiesHelper); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index f9e9a9305df3..128057ae6b62 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -335,6 +335,11 @@ constructor( biometricSettingsRepository.isCurrentUserInLockdown.isFalse(), "userHasNotLockedDownDevice", tableLogBuffer + ), + logAndObserve( + keyguardRepository.isKeyguardShowing, + "isKeyguardShowing", + tableLogBuffer ) ) .reduce(::and) @@ -598,7 +603,7 @@ constructor( const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L /** Number of allowed retries whenever there is a face hardware error */ - const val HAL_ERROR_RETRY_MAX = 20 + const val HAL_ERROR_RETRY_MAX = 5 /** Timeout before retries whenever there is a HAL error. */ const val HAL_ERROR_RETRY_TIMEOUT = 500L // ms diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt new file mode 100644 index 000000000000..abe59b76816f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt @@ -0,0 +1,71 @@ +/* + * 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.keyguard.data.repository + +import com.android.keyguard.FaceAuthUiEvent +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.AuthenticationStatus +import com.android.systemui.keyguard.shared.model.DetectionStatus +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.emptyFlow + +/** + * Implementation of the repository that noops all face auth operations. + * + * This is required for SystemUI variants that do not support face authentication but still inject + * other SysUI components that depend on [DeviceEntryFaceAuthRepository]. + */ +@SysUISingleton +class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceAuthRepository { + override val isAuthenticated: Flow<Boolean> + get() = emptyFlow() + + private val _canRunFaceAuth = MutableStateFlow(false) + override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth + + override val authenticationStatus: Flow<AuthenticationStatus> + get() = emptyFlow() + + override val detectionStatus: Flow<DetectionStatus> + get() = emptyFlow() + + private val _isLockedOut = MutableStateFlow(false) + override val isLockedOut: StateFlow<Boolean> = _isLockedOut + + private val _isAuthRunning = MutableStateFlow(false) + override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning + + override val isBypassEnabled: Flow<Boolean> + get() = emptyFlow() + + /** + * Trigger face authentication. + * + * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be + * ignored if face authentication is already running. Results should be propagated through + * [authenticationStatus] + * + * Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false. + */ + override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {} + + /** Stop currently running face authentication or detection. */ + override fun cancel() {} +} 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 1c2e85b0fd3a..b92d10474ccd 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 @@ -346,6 +346,10 @@ constructor( ?.largeClock ?.events ?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView)) + clockController.clock + ?.smallClock + ?.events + ?.onTargetRegionChanged(KeyguardClockSwitch.getSmallClockRegion(parentView)) } } parentView.addOnLayoutChangeListener(layoutChangeListener) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt index abf0e80bd87a..a62f383ed704 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt @@ -23,6 +23,7 @@ import android.view.Gravity import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout +import com.android.keyguard.LockIconView import com.android.systemui.R /** Provides a container for all keyguard ui content. */ @@ -37,6 +38,7 @@ class KeyguardRootView( init { addIndicationTextArea() + addLockIconView() } private fun addIndicationTextArea() { @@ -54,6 +56,17 @@ class KeyguardRootView( ) } + private fun addLockIconView() { + val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view } + addView( + view, + LayoutParams( + WRAP_CONTENT, + WRAP_CONTENT, + ) + ) + } + private fun Int.dp(): Int { return context.resources.getDimensionPixelSize(this) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt new file mode 100644 index 000000000000..712900161d48 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt @@ -0,0 +1,65 @@ +/* + * 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.keyguard.util + +import android.hardware.biometrics.BiometricFaceConstants +import android.hardware.biometrics.BiometricFingerprintConstants +import android.hardware.biometrics.BiometricSourceType +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +@SysUISingleton +class IndicationHelper +@Inject +constructor( + val keyguardUpdateMonitor: KeyguardUpdateMonitor, +) { + fun shouldSuppressErrorMsg(biometricSource: BiometricSourceType, msgId: Int): Boolean { + return when (biometricSource) { + BiometricSourceType.FINGERPRINT -> + (isPrimaryAuthRequired() && !isFingerprintLockoutErrorMsg(msgId)) || + msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED || + msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED || + msgId == BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED + BiometricSourceType.FACE -> + (isPrimaryAuthRequired() && !isFaceLockoutErrorMsg(msgId)) || + msgId == BiometricFaceConstants.FACE_ERROR_CANCELED || + msgId == BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS + else -> false + } + } + + private fun isFingerprintLockoutErrorMsg(msgId: Int): Boolean { + return msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT || + msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT + } + + fun isFaceLockoutErrorMsg(msgId: Int): Boolean { + return msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT || + msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT + } + + private fun isPrimaryAuthRequired(): Boolean { + // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong + // as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to + // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the + // check of whether non-strong biometric is allowed since strong biometrics can still be + // used. + return !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt index f7277842c026..27301e92eca2 100644 --- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt @@ -21,9 +21,9 @@ import android.graphics.Rect import android.graphics.RectF import androidx.core.graphics.toRectF import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.dagger.ScreenDecorationsLog import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject @@ -164,4 +164,49 @@ constructor( fun cameraProtectionEvent(@CompileTimeConstant cameraProtectionEvent: String) { logBuffer.log(TAG, DEBUG, cameraProtectionEvent) } + + fun logRotationChangeDeferred(currentRot: Int, newRot: Int) { + logBuffer.log( + TAG, + INFO, + { + int1 = currentRot + int2 = newRot + }, + { "Rotation changed, deferring $int2, staying at $int2" }, + ) + } + + fun logRotationChanged(oldRot: Int, newRot: Int) { + logBuffer.log( + TAG, + INFO, + { + int1 = oldRot + int2 = newRot + }, + { "Rotation changed from $int1 to $int2" } + ) + } + + fun logDisplayModeChanged(currentMode: Int, newMode: Int) { + logBuffer.log( + TAG, + INFO, + { + int1 = currentMode + int2 = newMode + }, + { "Resolution changed, deferring mode change to $int2, staying at $int1" }, + ) + } + + fun logUserSwitched(newUser: Int) { + logBuffer.log( + TAG, + DEBUG, + { int1 = newUser }, + { "UserSwitched newUserId=$int1. Updating color inversion setting" }, + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt new file mode 100644 index 000000000000..e1412919ce46 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt @@ -0,0 +1,25 @@ +/* + * 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.log.dagger + +import com.android.systemui.log.LogBuffer +import javax.inject.Qualifier + +/** A [LogBuffer] for changes to [DisableFlagsRepository]. */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class DisableFlagsRepositoryLog diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 0261ee53a0ff..3497285d6929 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -224,6 +224,15 @@ public class LogModule { false /* systrace */); } + /** Provides a logging buffer for the disable flags repository. */ + @Provides + @SysUISingleton + @DisableFlagsRepositoryLog + public static LogBuffer provideDisableFlagsRepositoryLogBuffer(LogBufferFactory factory) { + return factory.create("DisableFlagsRepository", 40 /* maxSize */, + false /* systrace */); + } + /** Provides a logging buffer for logs related to swipe up gestures. */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt index 1e9a466ccdce..70b5e75e6048 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt @@ -21,6 +21,8 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration +import android.database.ContentObserver +import android.provider.Settings import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.util.Log import android.util.MathUtils @@ -64,6 +66,7 @@ import com.android.systemui.util.Utils import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.animation.requiresRemeasuring import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.time.SystemClock import com.android.systemui.util.traceSection import java.io.PrintWriter @@ -105,6 +108,7 @@ constructor( private val mediaFlags: MediaFlags, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val globalSettings: GlobalSettings, ) : Dumpable { /** The current width of the carousel */ var currentCarouselWidth: Int = 0 @@ -169,6 +173,13 @@ constructor( private var carouselLocale: Locale? = null + private val animationScaleObserver: ContentObserver = + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() } + } + } + /** Whether the media card currently has the "expanded" layout */ @VisibleForTesting var currentlyExpanded = true @@ -529,6 +540,12 @@ constructor( listenForAnyStateToGoneKeyguardTransition(this) } } + + // Notifies all active players about animation scale changes. + globalSettings.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), + animationScaleObserver + ) } private fun inflateSettingsButton() { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index 0819d0d36e9a..35082fd5122a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -34,7 +34,6 @@ import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; -import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Color; @@ -252,13 +251,6 @@ public class MediaControlPanel { private boolean mWasPlaying = false; private boolean mButtonClicked = false; - private ContentObserver mAnimationScaleObserver = new ContentObserver(null) { - @Override - public void onChange(boolean selfChange) { - updateAnimatorDurationScale(); - } - }; - /** * Initialize a new control panel * @@ -318,10 +310,6 @@ public class MediaControlPanel { mFeatureFlags = featureFlags; mGlobalSettings = globalSettings; - mGlobalSettings.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), - mAnimationScaleObserver - ); updateAnimatorDurationScale(); } @@ -405,7 +393,10 @@ public class MediaControlPanel { updateSeekBarVisibility(); } - private void updateAnimatorDurationScale() { + /** + * Reloads animator duration scale. + */ + void updateAnimatorDurationScale() { if (mSeekBarObserver != null) { mSeekBarObserver.setAnimationEnabled( mGlobalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index 0a5b4b3066a0..7712690de195 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -105,6 +105,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements private WallpaperColors mWallpaperColors; private boolean mShouldLaunchLeBroadcastDialog; private boolean mIsLeBroadcastCallbackRegistered; + private boolean mDismissing; MediaOutputBaseAdapter mAdapter; @@ -265,13 +266,22 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mDevicesRecyclerView.setHasFixedSize(false); // Init bottom buttons mDoneButton.setOnClickListener(v -> dismiss()); - mStopButton.setOnClickListener(v -> { - mMediaOutputController.releaseSession(); - dismiss(); - }); + mStopButton.setOnClickListener(v -> onStopButtonClick()); mAppButton.setOnClickListener(mMediaOutputController::tryToLaunchMediaApplication); mMediaMetadataSectionLayout.setOnClickListener( mMediaOutputController::tryToLaunchMediaApplication); + + mDismissing = false; + } + + @Override + public void dismiss() { + // TODO(287191450): remove this once expensive binder calls are removed from refresh(). + // Due to these binder calls on the UI thread, calling refresh() during dismissal causes + // significant frame drops for the dismissal animation. Since the dialog is going away + // anyway, we use this state to turn refresh() into a no-op. + mDismissing = true; + super.dismiss(); } @Override @@ -299,7 +309,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements } void refresh(boolean deviceSetChanged) { - if (mMediaOutputController.isRefreshing()) { + // TODO(287191450): remove binder calls in this method from the UI thread. + // If the dialog is going away or is already refreshing, do nothing. + if (mDismissing || mMediaOutputController.isRefreshing()) { return; } mMediaOutputController.setRefreshing(true); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index 19b32e93274b..f3865f52e863 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -28,6 +28,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.SysUISingleton; @@ -36,11 +37,14 @@ import com.android.systemui.dagger.SysUISingleton; */ @SysUISingleton public class MediaOutputDialog extends MediaOutputBaseDialog { - final UiEventLogger mUiEventLogger; + private final DialogLaunchAnimator mDialogLaunchAnimator; + private final UiEventLogger mUiEventLogger; MediaOutputDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender, - MediaOutputController mediaOutputController, UiEventLogger uiEventLogger) { + MediaOutputController mediaOutputController, DialogLaunchAnimator dialogLaunchAnimator, + UiEventLogger uiEventLogger) { super(context, broadcastSender, mediaOutputController); + mDialogLaunchAnimator = dialogLaunchAnimator; mUiEventLogger = uiEventLogger; mAdapter = new MediaOutputAdapter(mMediaOutputController); if (!aboveStatusbar) { @@ -138,6 +142,7 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { } } else { mMediaOutputController.releaseSession(); + mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations(); dismiss(); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt index 802488668f16..4c168ecb81af 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -28,11 +28,11 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.broadcast.BroadcastSender +import com.android.systemui.flags.FeatureFlags import com.android.systemui.media.nearby.NearbyMediaDevicesManager import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection -import com.android.systemui.flags.FeatureFlags import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import java.util.Optional import javax.inject.Inject @@ -71,7 +71,8 @@ class MediaOutputDialogFactory @Inject constructor( dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager, powerExemptionManager, keyGuardManager, featureFlags, userTracker) val dialog = - MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller, uiEventLogger) + MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller, + dialogLaunchAnimator, uiEventLogger) mediaOutputDialog = dialog // Show the dialog. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java index 8225c47d904b..99c591f25edb 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java @@ -462,7 +462,7 @@ public final class NavBarHelper implements * @return Whether the IME is shown on top of the screen given the {@code vis} flag of * {@link InputMethodService} and the keyguard states. */ - public boolean isImeShown(int vis) { + public boolean isImeShown(@InputMethodService.ImeWindowVisibility int vis) { View shadeWindowView = mNotificationShadeWindowController.getWindowRootView(); boolean isKeyguardShowing = mKeyguardStateController.isShowing(); boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow() diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 682335e0b419..5bae1cba4ac4 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -66,6 +66,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.inputmethodservice.InputMethodService; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -1047,8 +1048,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements // ----- CommandQueue Callbacks ----- @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { if (displayId != mDisplayId) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index b96ca7ac2961..cecf043c572e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -224,7 +224,6 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION; } }); - controller.overrideIconTintForNavMode(true); return controller; } @@ -339,8 +338,9 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { boolean imeShown = mNavBarHelper.isImeShown(vis); if (!imeShown) { // Count imperceptible changes as visible so we transition taskbar out quickly. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index cf192f9a1272..7b86d0a6ebce 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -264,6 +264,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private boolean mGestureBlockingActivityRunning; private boolean mIsNewBackAffordanceEnabled; private boolean mIsTrackpadGestureFeaturesEnabled; + private boolean mIsTrackpadThreeFingerSwipe; private boolean mIsButtonForcedVisible; private InputMonitor mInputMonitor; @@ -986,18 +987,21 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack (int) mEndPoint.x, (int) mEndPoint.y, mEdgeWidthLeft + mLeftInset, mDisplaySize.x - (mEdgeWidthRight + mRightInset), - mUseMLModel ? mMLResults : -2, logPackageName); + mUseMLModel ? mMLResults : -2, logPackageName, + mIsTrackpadThreeFingerSwipe ? SysUiStatsLog.BACK_GESTURE__INPUT_TYPE__TRACKPAD + : SysUiStatsLog.BACK_GESTURE__INPUT_TYPE__TOUCH); } private void onMotionEvent(MotionEvent ev) { int action = ev.getActionMasked(); - boolean isTrackpadThreeFingerSwipe = isTrackpadThreeFingerSwipe( - mIsTrackpadGestureFeaturesEnabled, ev); if (action == MotionEvent.ACTION_DOWN) { if (DEBUG_MISSING_GESTURE) { Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev); } + mIsTrackpadThreeFingerSwipe = isTrackpadThreeFingerSwipe( + mIsTrackpadGestureFeaturesEnabled, ev); + // 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(); @@ -1005,7 +1009,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); - if (isTrackpadThreeFingerSwipe) { + if (mIsTrackpadThreeFingerSwipe) { // Since trackpad gestures don't have zones, this will be determined later by the // direction of the gesture. {@code mIsOnLeftEdge} is set to false to begin with. mDeferSetIsOnLeftEdge = true; @@ -1021,11 +1025,11 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack && !mGestureBlockingActivityRunning && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) && !isTrackpadScroll(mIsTrackpadGestureFeaturesEnabled, ev); - if (isTrackpadThreeFingerSwipe) { + if (mIsTrackpadThreeFingerSwipe) { // Trackpad back gestures don't have zones, so we don't need to check if the down // event is within insets. mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture( - isTrackpadThreeFingerSwipe); + true /* isTrackpadEvent */); } else { mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) @@ -1036,7 +1040,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mEdgeBackPlugin.onMotionEvent(ev); dispatchToBackAnimation(ev); } - if (mLogGesture || isTrackpadThreeFingerSwipe) { + if (mLogGesture || mIsTrackpadThreeFingerSwipe) { mDownPoint.set(ev.getX(), ev.getY()); mEndPoint.set(-1, -1); mThresholdCrossed = false; @@ -1050,7 +1054,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack "Gesture [%d [%s],alw=%B, t3fs=%B, left=%B, defLeft=%B, backAlw=%B, disbld=%B," + " qsDisbld=%b, blkdAct=%B, pip=%B," + " disp=%s, wl=%d, il=%d, wr=%d, ir=%d, excl=%s]", - curTime, curTimeStr, mAllowGesture, isTrackpadThreeFingerSwipe, + curTime, curTimeStr, mAllowGesture, mIsTrackpadThreeFingerSwipe, mIsOnLeftEdge, mDeferSetIsOnLeftEdge, mIsBackGestureAllowed, QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisabledForQuickstep, mGestureBlockingActivityRunning, mIsInPip, mDisplaySize, @@ -1059,7 +1063,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack if (!mThresholdCrossed) { mEndPoint.x = (int) ev.getX(); mEndPoint.y = (int) ev.getY(); - if (action == MotionEvent.ACTION_POINTER_DOWN && !isTrackpadThreeFingerSwipe) { + if (action == MotionEvent.ACTION_POINTER_DOWN && !mIsTrackpadThreeFingerSwipe) { if (mAllowGesture) { logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH); if (DEBUG_MISSING_GESTURE) { @@ -1071,7 +1075,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mLogGesture = false; return; } else if (action == MotionEvent.ACTION_MOVE) { - if (isTrackpadThreeFingerSwipe && mDeferSetIsOnLeftEdge) { + if (mIsTrackpadThreeFingerSwipe && mDeferSetIsOnLeftEdge) { // mIsOnLeftEdge is determined by the relative position between the down // and the current motion event for trackpad gestures instead of zoning. mIsOnLeftEdge = mEndPoint.x > mDownPoint.x; diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 17124901e4de..d511d8aea019 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -114,11 +114,18 @@ public class QSCustomizer extends LinearLayout { mQs = qs; } + private void reloadAdapterTileHeight(@Nullable RecyclerView.Adapter adapter) { + if (adapter instanceof TileAdapter) { + ((TileAdapter) adapter).reloadTileHeight(); + } + } + /** Animate and show QSCustomizer panel. * @param x,y Location on screen of {@code edit} button to determine center of animation. */ void show(int x, int y, TileAdapter tileAdapter) { if (!isShown) { + reloadAdapterTileHeight(tileAdapter); mRecyclerView.getLayoutManager().scrollToPosition(0); int[] containerLocation = findViewById(R.id.customize_container).getLocationOnScreen(); mX = x - containerLocation[0]; @@ -136,6 +143,7 @@ public class QSCustomizer extends LinearLayout { void showImmediately() { if (!isShown) { + reloadAdapterTileHeight(mRecyclerView.getAdapter()); mRecyclerView.getLayoutManager().scrollToPosition(0); setVisibility(VISIBLE); mClipper.cancelAnimator(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 6a05684a74e8..596475eedb43 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -43,9 +43,11 @@ import androidx.recyclerview.widget.RecyclerView.State; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.android.internal.logging.UiEventLogger; +import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.qs.QSEditEvent; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.TileLayout; import com.android.systemui.qs.customize.TileAdapter.Holder; import com.android.systemui.qs.customize.TileQueryHelper.TileInfo; import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener; @@ -114,6 +116,9 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private RecyclerView mRecyclerView; private int mNumColumns; + private TextView mTempTextView; + private int mMinTileViewHeight; + @Inject public TileAdapter( @QSThemedContext Context context, @@ -129,6 +134,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID); mAccessibilityDelegate = new TileAdapterDelegate(); mSizeLookup.setSpanIndexCacheEnabled(true); + mTempTextView = new TextView(context); + mMinTileViewHeight = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_height); } @Override @@ -318,6 +325,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta @Override public void onBindViewHolder(final Holder holder, int position) { + if (holder.mTileView != null) { + holder.mTileView.setMinimumHeight(mMinTileViewHeight); + } + if (holder.getItemViewType() == TYPE_HEADER) { setSelectableForHeaders(holder.itemView); return; @@ -860,4 +871,19 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta - buttonMinWidth - res.getDimensionPixelSize(R.dimen.qs_tile_margin_top_bottom); } + + /** + * Re-estimate the tile view height based under current font scaling. Like + * {@link TileLayout#estimateCellHeight()}, the tile view height would be estimated with 2 + * labels as general case. + */ + public void reloadTileHeight() { + final int minHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height); + FontSizeUtils.updateFontSize(mTempTextView, R.dimen.qs_tile_text_size); + int unspecifiedSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + mTempTextView.measure(unspecifiedSpec, unspecifiedSpec); + int padding = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_padding); + int estimatedTileViewHeight = mTempTextView.getMeasuredHeight() * 2 + padding * 2; + mMinTileViewHeight = Math.max(minHeight, estimatedTileViewHeight); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 9d2c8acd9522..ec1258049486 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -173,6 +173,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi private AccessPointController mAccessPointController; private IntentFilter mConnectionStateFilter; @VisibleForTesting + @Nullable InternetDialogCallback mCallback; private UiEventLogger mUiEventLogger; private BroadcastDispatcher mBroadcastDispatcher; @@ -730,7 +731,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi ActivityLaunchAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(view); - if (controller == null) { + if (controller == null && mCallback != null) { mCallback.dismissDialog(); } @@ -1101,7 +1102,9 @@ public class InternetDialogController implements AccessPointController.AccessPoi mHasWifiEntries = false; } - mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries); + if (mCallback != null) { + mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries); + } } @Override @@ -1123,34 +1126,46 @@ public class InternetDialogController implements AccessPointController.AccessPoi @Override public void onServiceStateChanged(@NonNull ServiceState serviceState) { - mCallback.onServiceStateChanged(serviceState); + if (mCallback != null) { + mCallback.onServiceStateChanged(serviceState); + } } @Override public void onDataConnectionStateChanged(int state, int networkType) { - mCallback.onDataConnectionStateChanged(state, networkType); + if (mCallback != null) { + mCallback.onDataConnectionStateChanged(state, networkType); + } } @Override public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) { - mCallback.onSignalStrengthsChanged(signalStrength); + if (mCallback != null) { + mCallback.onSignalStrengthsChanged(signalStrength); + } } @Override public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { mSubIdTelephonyDisplayInfoMap.put(mSubId, telephonyDisplayInfo); - mCallback.onDisplayInfoChanged(telephonyDisplayInfo); + if (mCallback != null) { + mCallback.onDisplayInfoChanged(telephonyDisplayInfo); + } } @Override public void onUserMobileDataStateChanged(boolean enabled) { - mCallback.onUserMobileDataStateChanged(enabled); + if (mCallback != null) { + mCallback.onUserMobileDataStateChanged(enabled); + } } @Override public void onCarrierNetworkChange(boolean active) { mCarrierNetworkChangeMode = active; - mCallback.onCarrierNetworkChange(active); + if (mCallback != null) { + mCallback.onCarrierNetworkChange(active); + } } } @@ -1177,14 +1192,18 @@ public class InternetDialogController implements AccessPointController.AccessPoi scanWifiAccessPoints(); } // update UI - mCallback.onCapabilitiesChanged(network, capabilities); + if (mCallback != null) { + mCallback.onCapabilitiesChanged(network, capabilities); + } } @Override @WorkerThread public void onLost(@NonNull Network network) { mHasEthernet = false; - mCallback.onLost(network); + if (mCallback != null) { + mCallback.onLost(network); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index 752471d83735..0a9839e2f18b 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -16,7 +16,7 @@ package com.android.systemui.scene -import com.android.systemui.scene.data.model.SceneContainerConfigModule +import com.android.systemui.scene.shared.model.SceneContainerConfigModule import com.android.systemui.scene.ui.composable.SceneModule import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModelModule import dagger.Module diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt index 61b162b014d8..1ebeced5fae6 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt @@ -16,7 +16,7 @@ package com.android.systemui.scene.data.repository -import com.android.systemui.scene.data.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject @@ -28,11 +28,9 @@ import kotlinx.coroutines.flow.asStateFlow class SceneContainerRepository @Inject constructor( - containerConfigurations: Set<SceneContainerConfig>, + private val containerConfigByName: Map<String, SceneContainerConfig>, ) { - private val containerConfigByName: Map<String, SceneContainerConfig> = - containerConfigurations.associateBy { config -> config.name } private val containerVisibilityByName: Map<String, MutableStateFlow<Boolean>> = containerConfigByName .map { (containerName, _) -> containerName to MutableStateFlow(true) } @@ -48,21 +46,6 @@ constructor( .map { (containerName, _) -> containerName to MutableStateFlow(1f) } .toMap() - init { - val repeatedContainerNames = - containerConfigurations - .groupingBy { config -> config.name } - .eachCount() - .filter { (_, count) -> count > 1 } - check(repeatedContainerNames.isEmpty()) { - "Container names must be unique. The following container names appear more than once: ${ - repeatedContainerNames - .map { (name, count) -> "\"$name\" appears $count times" } - .joinToString(", ") - }" - } - } - /** * Returns the keys to all scenes in the container with the given name. * diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt index d0769ebe941e..0327edbb06b4 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 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. @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.android.systemui.scene.data.model - -import com.android.systemui.scene.shared.model.SceneKey +package com.android.systemui.scene.shared.model /** Models the configuration of a single scene container. */ data class SceneContainerConfig( diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfigModule.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt index 0af80949f95e..7562a5a848d8 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfigModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 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. @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.android.systemui.scene.data.model +package com.android.systemui.scene.shared.model import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.scene.shared.model.SceneContainerNames -import com.android.systemui.scene.shared.model.SceneKey import dagger.Module import dagger.Provides import javax.inject.Named diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt index 8f001ec6b5e2..9b5898f42279 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt @@ -2,6 +2,74 @@ package com.android.systemui.scene.ui.view import android.content.Context import android.util.AttributeSet +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.OnBackPressedDispatcherOwner +import androidx.activity.setViewTreeOnBackPressedDispatcherOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.compose.ComposeFacade +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel +import kotlinx.coroutines.launch /** A root view of the main SysUI window that supports scenes. */ -class SceneWindowRootView(context: Context?, attrs: AttributeSet?) : WindowRootView(context, attrs)
\ No newline at end of file +class SceneWindowRootView( + context: Context, + attrs: AttributeSet?, +) : + WindowRootView( + context, + attrs, + ) { + fun init( + viewModel: SceneContainerViewModel, + containerConfig: SceneContainerConfig, + scenes: Set<Scene>, + ) { + val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key } + val sortedSceneByKey: Map<SceneKey, Scene> = buildMap { + containerConfig.sceneKeys.forEach { sceneKey -> + val scene = + checkNotNull(unsortedSceneByKey[sceneKey]) { + "Scene not found for key \"$sceneKey\"!" + } + + put(sceneKey, scene) + } + } + + repeatWhenAttached { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + setViewTreeOnBackPressedDispatcherOwner( + object : OnBackPressedDispatcherOwner { + override val onBackPressedDispatcher = + OnBackPressedDispatcher().apply { + setOnBackInvokedDispatcher(viewRootImpl.onBackInvokedDispatcher) + } + + override fun getLifecycle(): Lifecycle { + return this@repeatWhenAttached.lifecycle + } + } + ) + + addView( + ComposeFacade.createSceneContainerView( + context = context, + viewModel = viewModel, + sceneByKey = sortedSceneByKey, + ) + ) + } + + // Here when destroyed. + removeAllViews() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt index a0f966705381..11e1eb910b1f 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt @@ -2,7 +2,52 @@ package com.android.systemui.scene.ui.view import android.content.Context import android.util.AttributeSet +import android.view.View import android.widget.FrameLayout +import com.android.systemui.compose.ComposeFacade -/** A view that can serve as the root of the main SysUI window. */ -open class WindowRootView(context: Context?, attrs: AttributeSet?) : FrameLayout(context, attrs)
\ No newline at end of file +/** + * A view that can serve as the root of the main SysUI window (but might not, see below for more + * information regarding this confusing comment). + * + * Naturally, only one view may ever be at the root of a view hierarchy tree. Under certain + * conditions, the view hierarchy tree in the scene-containing window view may actually have one + * [WindowRootView] acting as the true root view and another [WindowRootView] which doesn't and is, + * instead, a child of the true root view. To discern which one is which, please use the [isRoot] + * method. + */ +open class WindowRootView( + context: Context, + attrs: AttributeSet?, +) : + FrameLayout( + context, + attrs, + ) { + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + if (ComposeFacade.isComposeAvailable() && isRoot()) { + ComposeFacade.composeInitializer().onAttachedToWindow(this) + } + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + + if (ComposeFacade.isComposeAvailable() && isRoot()) { + ComposeFacade.composeInitializer().onDetachedFromWindow(this) + } + } + + /** + * Returns `true` if this view is the true root of the view-hierarchy; `false` otherwise. + * + * Please see the class-level documentation to understand why this is possible. + */ + private fun isRoot(): Boolean { + // TODO(b/283300105): remove this check once there's only one subclass of WindowRootView. + return parent.let { it !is View || it.id == android.R.id.content } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt index 7e0f50400299..a9b3d0a1f3aa 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt @@ -31,8 +31,7 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain val constraintAlpha = if (visible) 0f else 1f return ConstraintsChanges( qqsConstraintsChanges = { - setAlpha(R.id.statusIcons, constraintAlpha) - setAlpha(R.id.batteryRemainingIcon, constraintAlpha) + setAlpha(R.id.shade_header_system_icons, constraintAlpha) } ) } @@ -45,14 +44,15 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain R.id.barrier, ConstraintSet.START, 0, - R.id.statusIcons, + R.id.shade_header_system_icons, R.id.privacy_container ) - connect(R.id.statusIcons, ConstraintSet.START, R.id.date, ConstraintSet.END) + connect(R.id.shade_header_system_icons, ConstraintSet.START, R.id.date, + ConstraintSet.END) connect(R.id.privacy_container, ConstraintSet.START, R.id.date, ConstraintSet.END) - constrainWidth(R.id.statusIcons, ViewGroup.LayoutParams.WRAP_CONTENT) + constrainWidth(R.id.shade_header_system_icons, ViewGroup.LayoutParams.WRAP_CONTENT) constrainedWidth(R.id.date, true) - constrainedWidth(R.id.statusIcons, true) + constrainedWidth(R.id.shade_header_system_icons, true) } ) } @@ -84,7 +84,7 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain setGuidelineEnd(centerEnd, offsetFromEdge) connect(R.id.date, ConstraintSet.END, centerStart, ConstraintSet.START) connect( - R.id.statusIcons, + R.id.shade_header_system_icons, ConstraintSet.START, centerEnd, ConstraintSet.END @@ -96,7 +96,7 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain ConstraintSet.END ) constrainedWidth(R.id.date, true) - constrainedWidth(R.id.statusIcons, true) + constrainedWidth(R.id.shade_header_system_icons, true) }, qsConstraintsChanges = { setGuidelineBegin(centerStart, offsetFromEdge) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index e06f37baa131..2ef9e0772d93 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1032,7 +1032,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump new NsslHeightChangedListener()); mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( mOnEmptySpaceClickListener); - mQsController.initNotificationStackScrollLayoutController(); + mQsController.init(); mShadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged); mShadeHeadsUpTracker.addTrackingHeadsUpListener( mNotificationStackScrollLayoutController::setTrackingHeadsUp); @@ -1820,6 +1820,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state. setClosing(true); + mUpdateFlingOnLayout = false; if (delayed) { mNextCollapseSpeedUpFactor = speedUpFactor; this.mView.postDelayed(mFlingCollapseRunnable, 120); @@ -4858,7 +4859,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && mQsController.handleTouch( event, isFullyCollapsed(), isShadeOrQsHeightAnimationRunning())) { - mShadeLog.logMotionEvent(event, "onTouch: handleQsTouch handled event"); + if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { + mShadeLog.logMotionEvent(event, "onTouch: handleQsTouch handled event"); + } return true; } if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { @@ -4872,7 +4875,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } handled |= handleTouch(event); - mShadeLog.logOnTouchEventLastReturn(event, !mDozing, handled); return !mDozing || handled; } @@ -5059,7 +5061,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } break; } - mShadeLog.logHandleTouchLastReturn(event, !mGestureWaitForTouchSlop, mTracking); return !mGestureWaitForTouchSlop || mTracking; } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java index c9122c77c1d4..2b62b7d67c2a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java @@ -58,7 +58,6 @@ import android.widget.FrameLayout; import com.android.internal.view.FloatingActionMode; import com.android.internal.widget.floatingtoolbar.FloatingToolbar; import com.android.systemui.R; -import com.android.systemui.compose.ComposeFacade; import com.android.systemui.scene.ui.view.WindowRootView; /** @@ -149,18 +148,6 @@ public class NotificationShadeWindowView extends WindowRootView { protected void onAttachedToWindow() { super.onAttachedToWindow(); setWillNotDraw(!DEBUG); - - if (ComposeFacade.INSTANCE.isComposeAvailable()) { - ComposeFacade.INSTANCE.composeInitializer().onAttachedToWindow(this); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (ComposeFacade.INSTANCE.isComposeAvailable()) { - ComposeFacade.INSTANCE.composeInitializer().onDetachedFromWindow(this); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index ade59d7d85bb..5c41d572149c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -58,6 +58,7 @@ import com.android.systemui.log.BouncerLogger; import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor; import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor; import com.android.systemui.multishade.ui.view.MultiShadeView; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -113,6 +114,7 @@ public class NotificationShadeWindowViewController { private PhoneStatusBarViewController mStatusBarViewController; private final CentralSurfaces mService; private final BackActionInteractor mBackActionInteractor; + private final PowerInteractor mPowerInteractor; private final NotificationShadeWindowController mNotificationShadeWindowController; private DragDownHelper mDragDownHelper; private boolean mExpandingBelowNotch; @@ -147,6 +149,7 @@ public class NotificationShadeWindowViewController { LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, BackActionInteractor backActionInteractor, + PowerInteractor powerInteractor, NotificationShadeWindowController controller, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, KeyguardUnlockAnimationController keyguardUnlockAnimationController, @@ -179,6 +182,7 @@ public class NotificationShadeWindowViewController { mBackActionInteractor = backActionInteractor; mLockIconViewController.init(); mService = centralSurfaces; + mPowerInteractor = powerInteractor; mNotificationShadeWindowController = controller; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mAmbientState = ambientState; @@ -311,8 +315,7 @@ public class NotificationShadeWindowViewController { /* onGestureDetectedRunnable */ () -> { mService.userActivity(); - mService.wakeUpIfDozing( - mClock.uptimeMillis(), + mPowerInteractor.wakeUpIfDozing( "LOCK_ICON_TOUCH", PowerManager.WAKE_REASON_GESTURE); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index a33bc1e8cc8a..1361c9f25eff 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -78,6 +78,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.ShadeRepository; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -101,6 +102,7 @@ import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.LargeScreenUtils; +import com.android.systemui.util.kotlin.JavaAdapter; import dagger.Lazy; @@ -153,6 +155,8 @@ public class QuickSettingsController implements Dumpable { private final FeatureFlags mFeatureFlags; private final InteractionJankMonitor mInteractionJankMonitor; private final ShadeRepository mShadeRepository; + private final ShadeInteractor mShadeInteractor; + private final JavaAdapter mJavaAdapter; private final FalsingManager mFalsingManager; private final AccessibilityManager mAccessibilityManager; private final MetricsLogger mMetricsLogger; @@ -342,6 +346,8 @@ public class QuickSettingsController implements Dumpable { DumpManager dumpManager, KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, ShadeRepository shadeRepository, + ShadeInteractor shadeInteractor, + JavaAdapter javaAdapter, CastController castController ) { mPanelViewControllerLazy = panelViewControllerLazy; @@ -387,6 +393,8 @@ public class QuickSettingsController implements Dumpable { mFeatureFlags = featureFlags; mInteractionJankMonitor = interactionJankMonitor; mShadeRepository = shadeRepository; + mShadeInteractor = shadeInteractor; + mJavaAdapter = javaAdapter; mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback()); dumpManager.registerDumpable(this); @@ -459,7 +467,13 @@ public class QuickSettingsController implements Dumpable { } // TODO (b/265054088): move this and others to a CoreStartable - void initNotificationStackScrollLayoutController() { + void init() { + initNotificationStackScrollLayoutController(); + mJavaAdapter.alwaysCollectFlow( + mShadeInteractor.isExpandToQsEnabled(), this::setExpansionEnabledPolicy); + } + + private void initNotificationStackScrollLayoutController() { mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( new NsslOverscrollTopChangedListener()); mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged); @@ -883,7 +897,7 @@ public class QuickSettingsController implements Dumpable { } /** */ - public void setExpansionEnabledPolicy(boolean expansionEnabledPolicy) { + private void setExpansionEnabledPolicy(boolean expansionEnabledPolicy) { mExpansionEnabledPolicy = expansionEnabledPolicy; if (mQs != null) { mQs.setHeaderClickable(isExpansionEnabled()); @@ -1177,7 +1191,6 @@ public class QuickSettingsController implements Dumpable { mClippingAnimationEndBounds.left, fraction); int animTop = (int) MathUtils.lerp(startTop, mClippingAnimationEndBounds.top, fraction); - logClippingTopBound("interpolated top bound", top); int animRight = (int) MathUtils.lerp(startRight, mClippingAnimationEndBounds.right, fraction); int animBottom = (int) MathUtils.lerp(startBottom, @@ -1207,7 +1220,8 @@ public class QuickSettingsController implements Dumpable { if (mIsFullWidth) { clipStatusView = qsVisible; float screenCornerRadius = - mRecordingController.isRecording() || mCastController.hasConnectedCastDevice() + !mSplitShadeEnabled || mRecordingController.isRecording() + || mCastController.hasConnectedCastDevice() ? 0 : mScreenCornerRadius; radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); @@ -1328,8 +1342,6 @@ public class QuickSettingsController implements Dumpable { // the screen without clipping. return -mAmbientState.getStackTopMargin(); } else { - logNotificationsClippingTopBound(qsTop, - mNotificationStackScrollLayoutController.getTop()); return qsTop - mNotificationStackScrollLayoutController.getTop(); } } @@ -1371,25 +1383,21 @@ public class QuickSettingsController implements Dumpable { keyguardNotificationStaticPadding, maxQsPadding) : maxQsPadding; topPadding = (int) MathUtils.lerp((float) getMinExpansionHeight(), (float) max, expandedFraction); - logNotificationsTopPadding("keyguard and expandImmediate", topPadding); return topPadding; } else if (isSizeChangeAnimationRunning()) { topPadding = Math.max((int) mSizeChangeAnimator.getAnimatedValue(), keyguardNotificationStaticPadding); - logNotificationsTopPadding("size change animation running", topPadding); return topPadding; } else if (keyguardShowing) { // We can only do the smoother transition on Keyguard when we also are not collapsing // from a scrolled quick settings. topPadding = MathUtils.lerp((float) keyguardNotificationStaticPadding, (float) (getMaxExpansionHeight()), computeExpansionFraction()); - logNotificationsTopPadding("keyguard", topPadding); return topPadding; } else { topPadding = Math.max(mQsFrameTranslateController.getNotificationsTopPadding( mExpansionHeight, mNotificationStackScrollLayoutController), mQuickQsHeaderHeight); - logNotificationsTopPadding("default case", topPadding); return topPadding; } } @@ -1437,38 +1445,6 @@ public class QuickSettingsController implements Dumpable { - mAmbientState.getScrollY()); } - /** TODO(b/273591201): remove after bug resolved */ - private void logNotificationsTopPadding(String message, float rawPadding) { - int padding = ((int) rawPadding / 10) * 10; - if (mBarState != KEYGUARD && padding != mLastNotificationsTopPadding && !mExpanded) { - mLastNotificationsTopPadding = padding; - mShadeLog.logNotificationsTopPadding(message, padding); - } - } - - /** TODO(b/273591201): remove after bug resolved */ - private void logClippingTopBound(String message, int top) { - top = (top / 10) * 10; - if (mBarState != KEYGUARD && mShadeExpandedFraction == 1 - && top != mLastClippingTopBound && !mExpanded) { - mLastClippingTopBound = top; - mShadeLog.logClippingTopBound(message, top); - } - } - - /** TODO(b/273591201): remove after bug resolved */ - private void logNotificationsClippingTopBound(int top, int nsslTop) { - top = (top / 10) * 10; - nsslTop = (nsslTop / 10) * 10; - if (mBarState == SHADE && mShadeExpandedFraction == 1 - && (top != mLastNotificationsClippingTopBound - || nsslTop != mLastNotificationsClippingTopBoundNssl) && !mExpanded) { - mLastNotificationsClippingTopBound = top; - mLastNotificationsClippingTopBoundNssl = nsslTop; - mShadeLog.logNotificationsClippingTopBound(top, nsslTop); - } - } - private int calculateTopClippingBound(int qsPanelBottomY) { int top; if (mSplitShadeEnabled) { @@ -1478,7 +1454,6 @@ public class QuickSettingsController implements Dumpable { // If we're transitioning, let's use the actual value. The else case // can be wrong during transitions when waiting for the keyguard to unlock top = mTransitionToFullShadePosition; - logClippingTopBound("set while transitioning to full shade", top); } else { final float notificationTop = getEdgePosition(); if (mBarState == KEYGUARD) { @@ -1487,10 +1462,8 @@ public class QuickSettingsController implements Dumpable { // this should go away once we unify the stackY position and don't have // to do this min anymore below. top = qsPanelBottomY; - logClippingTopBound("bypassing keyguard", top); } else { top = (int) Math.min(qsPanelBottomY, notificationTop); - logClippingTopBound("keyguard default case", top); } } else { top = (int) notificationTop; @@ -1498,14 +1471,12 @@ public class QuickSettingsController implements Dumpable { } // TODO (b/265193930): remove dependency on NPVC top += mPanelViewControllerLazy.get().getOverStretchAmount(); - logClippingTopBound("including overstretch", top); // Correction for instant expansion caused by HUN pull down/ float minFraction = mPanelViewControllerLazy.get().getMinFraction(); if (minFraction > 0f && minFraction < 1f) { float realFraction = (mShadeExpandedFraction - minFraction) / (1f - minFraction); top *= MathUtils.saturate(realFraction / minFraction); - logClippingTopBound("after adjusted fraction", top); } } return top; @@ -1645,15 +1616,11 @@ public class QuickSettingsController implements Dumpable { // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS. if (!mSplitShadeEnabled && !mLastShadeFlingWasExpanding && computeExpansionFraction() <= 0.01 && mShadeExpandedFraction < 1.0) { - mShadeLog.logMotionEvent(event, - "handleQsTouch: shade touched while shade collapsing, QS tracking disabled"); mTracking = false; } if (!isExpandImmediate() && mTracking) { onTouch(event); if (!mConflictingExpansionGesture && !mSplitShadeEnabled) { - mShadeLog.logMotionEvent(event, - "handleQsTouch: not immediate expand or conflicting gesture"); return true; } } @@ -1747,7 +1714,6 @@ public class QuickSettingsController implements Dumpable { break; case MotionEvent.ACTION_MOVE: - mShadeLog.logMotionEvent(event, "onQsTouch: move action, setting QS expansion"); setExpansionHeight(h + mInitialHeightOnTouch); // TODO (b/265193930): remove dependency on NPVC if (h >= mPanelViewControllerLazy.get().getFalsingThreshold()) { @@ -1835,17 +1801,14 @@ public class QuickSettingsController implements Dumpable { final float h = y - mInitialTouchY; trackMovement(event); if (mTracking) { - // Already tracking because onOverscrolled was called. We need to update here // so we don't stop for a frame until the next touch event gets handled in // onTouchEvent. setExpansionHeight(h + mInitialHeightOnTouch); trackMovement(event); return true; - } else { - mShadeLog.logMotionEvent(event, - "onQsIntercept: move ignored because qs tracking disabled"); } + // TODO (b/265193930): remove dependency on NPVC float touchSlop = event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE @@ -1869,7 +1832,7 @@ public class QuickSettingsController implements Dumpable { } else { mShadeLog.logQsTrackingNotStarted(mInitialTouchY, y, h, touchSlop, getExpanded(), mPanelViewControllerLazy.get().isKeyguardShowing(), - isExpansionEnabled()); + isExpansionEnabled(), event.getDownTime()); } break; diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt index 2b772e372f77..2da8d5f4d921 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt @@ -54,7 +54,8 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) { touchSlop: Float, qsExpanded: Boolean, keyguardShowing: Boolean, - qsExpansionEnabled: Boolean + qsExpansionEnabled: Boolean, + downTime: Long ) { buffer.log( TAG, @@ -67,10 +68,11 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) { bool1 = qsExpanded bool2 = keyguardShowing bool3 = qsExpansionEnabled + str1 = downTime.toString() }, { - "QsTrackingNotStarted: initTouchY=$int1,y=$int2,h=$long1,slop=$double1,qsExpanded" + - "=$bool1,keyguardShowing=$bool2,qsExpansion=$bool3" + "QsTrackingNotStarted: downTime=$str1,initTouchY=$int1,y=$int2,h=$long1," + + "slop=$double1,qsExpanded=$bool1,keyguardShowing=$bool2,qsExpansion=$bool3" } ) } @@ -306,91 +308,6 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) { ) } - fun logNotificationsTopPadding(message: String, padding: Int) { - buffer.log( - TAG, - LogLevel.VERBOSE, - { - str1 = message - int1 = padding - }, - { "QSC NotificationsTopPadding $str1: $int1"} - ) - } - - fun logClippingTopBound(message: String, top: Int) { - buffer.log( - TAG, - LogLevel.VERBOSE, - { - str1 = message - int1 = top - }, - { "QSC ClippingTopBound $str1: $int1" } - ) - } - - fun logNotificationsClippingTopBound(top: Int, nsslTop: Int) { - buffer.log( - TAG, - LogLevel.VERBOSE, - { - int1 = top - int2 = nsslTop - }, - { "QSC NotificationsClippingTopBound set to $int1 - $int2" } - ) - } - - fun logOnTouchEventLastReturn( - event: MotionEvent, - dozing: Boolean, - handled: Boolean, - ) { - buffer.log( - TAG, - LogLevel.VERBOSE, - { - bool1 = dozing - bool2 = handled - long1 = event.eventTime - long2 = event.downTime - int1 = event.action - int2 = event.classification - double1 = event.y.toDouble() - }, - { - "NPVC onTouchEvent last return: !mDozing: $bool1 || handled: $bool2 " + - "\neventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2" - } - ) - } - - fun logHandleTouchLastReturn( - event: MotionEvent, - gestureWaitForTouchSlop: Boolean, - tracking: Boolean, - ) { - buffer.log( - TAG, - LogLevel.VERBOSE, - { - bool1 = gestureWaitForTouchSlop - bool2 = tracking - long1 = event.eventTime - long2 = event.downTime - int1 = event.action - int2 = event.classification - double1 = event.y.toDouble() - }, - { - "NPVC handleTouch last return: !mGestureWaitForTouchSlop: $bool1 " + - "|| mTracking: $bool2 " + - "\neventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2" - } - ) - } - fun logUpdateNotificationPanelTouchState( disabled: Boolean, isGoingToSleep: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 4b2a65f36aa1..a2b93516695a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.annotation.SuppressLint import android.content.ContentResolver import android.os.Handler import android.view.LayoutInflater @@ -28,13 +29,19 @@ import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.biometrics.AuthRippleController import com.android.systemui.biometrics.AuthRippleView +import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.privacy.OngoingPrivacyChip +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneContainerNames +import com.android.systemui.scene.ui.view.SceneWindowRootView import com.android.systemui.scene.ui.view.WindowRootView +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.NotificationShelf @@ -67,14 +74,30 @@ abstract class ShadeModule { companion object { const val SHADE_HEADER = "large_screen_shade_header" + @SuppressLint("InflateParams") // Root views don't have parents. @Provides @SysUISingleton fun providesWindowRootView( layoutInflater: LayoutInflater, featureFlags: FeatureFlags, + @Named(SceneContainerNames.SYSTEM_UI_DEFAULT) + viewModelProvider: Provider<SceneContainerViewModel>, + @Named(SceneContainerNames.SYSTEM_UI_DEFAULT) + containerConfigProvider: Provider<SceneContainerConfig>, + @Named(SceneContainerNames.SYSTEM_UI_DEFAULT) + scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>, ): WindowRootView { - return if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { - layoutInflater.inflate(R.layout.scene_window_root, null) + return if ( + featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable() + ) { + val sceneWindowRootView = + layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView + sceneWindowRootView.init( + viewModel = viewModelProvider.get(), + containerConfig = containerConfigProvider.get(), + scenes = scenesProvider.get(), + ) + sceneWindowRootView } else { layoutInflater.inflate(R.layout.super_notification_shade, null) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java index fc1e87ad3e45..ad49b267c6ac 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java @@ -198,6 +198,11 @@ public class ShadeCarrierGroupController { return mSlotIndexResolver.getSlotIndex(subscriptionId); } + @VisibleForTesting + protected int getShadeCarrierVisibility(int index) { + return mCarrierGroups[index].getVisibility(); + } + /** * Sets a {@link OnSingleCarrierChangedListener}. * @@ -307,11 +312,13 @@ public class ShadeCarrierGroupController { + info.subscriptionIds[i]); continue; } - mInfos[slot] = mInfos[slot].changeVisibility(true); - slotSeen[slot] = true; - mCarrierGroups[slot].setCarrierText( - info.listOfCarriers[i].toString().trim()); - mCarrierGroups[slot].setVisibility(View.VISIBLE); + String carrierText = info.listOfCarriers[i].toString().trim(); + if (!TextUtils.isEmpty(carrierText)) { + mInfos[slot] = mInfos[slot].changeVisibility(true); + slotSeen[slot] = true; + mCarrierGroups[slot].setCarrierText(carrierText); + mCarrierGroups[slot].setVisibility(View.VISIBLE); + } } for (int i = 0; i < SIM_SLOTS; i++) { if (!slotSeen[i]) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt new file mode 100644 index 000000000000..eceda8453902 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt @@ -0,0 +1,55 @@ +/* + * 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.shade.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.user.domain.interactor.UserInteractor +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +/** Business logic for shade interactions. */ +@SysUISingleton +class ShadeInteractor +@Inject +constructor( + disableFlagsRepository: DisableFlagsRepository, + keyguardRepository: KeyguardRepository, + userSetupRepository: UserSetupRepository, + deviceProvisionedController: DeviceProvisionedController, + userInteractor: UserInteractor, +) { + /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */ + val isExpandToQsEnabled: Flow<Boolean> = + combine( + disableFlagsRepository.disableFlags, + keyguardRepository.isDozing, + userSetupRepository.isUserSetupFlow, + ) { disableFlags, isDozing, isUserSetup -> + deviceProvisionedController.isDeviceProvisioned && + // Disallow QS during setup if it's a simple user switcher. (The user intends to + // use the lock screen user switcher, QS is not needed.) + (isUserSetup || !userInteractor.isSimpleUserSwitcher) && + disableFlags.isShadeEnabled() && + disableFlags.isQuickSettingsEnabled() && + !isDozing + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a532195c5b9f..6c2c0cf12aad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar; import static android.app.StatusBarManager.DISABLE2_NONE; import static android.app.StatusBarManager.DISABLE_NONE; import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; -import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.view.Display.INVALID_DISPLAY; import android.annotation.Nullable; @@ -37,7 +36,7 @@ import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; -import android.inputmethodservice.InputMethodService.BackDispositionMode; +import android.inputmethodservice.InputMethodService; import android.media.INearbyMediaDevicesProvider; import android.media.MediaRoute2Info; import android.os.Binder; @@ -226,8 +225,10 @@ public class CommandQueue extends IStatusBar.Stub implements * @param backDisposition Disposition mode of back button. It should be one of below flags: * @param showImeSwitcher {@code true} to show IME switch button. */ - default void setImeWindowStatus(int displayId, IBinder token, int vis, - @BackDispositionMode int backDisposition, boolean showImeSwitcher) { } + default void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, + boolean showImeSwitcher) { } default void showRecentApps(boolean triggeredFromAltTab) { } default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { } default void toggleTaskbar() { } @@ -678,7 +679,9 @@ public class CommandQueue extends IStatusBar.Stub implements } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { synchronized (mLock) { mHandler.removeMessages(MSG_SHOW_IME_BUTTON); @@ -1092,7 +1095,9 @@ public class CommandQueue extends IStatusBar.Stub implements } } - private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition, + private void handleShowImeButton(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { if (displayId == INVALID_DISPLAY) return; @@ -1112,7 +1117,7 @@ public class CommandQueue extends IStatusBar.Stub implements private void sendImeInvisibleStatusForPrevNavBar() { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId, - null /* token */, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, + null /* token */, 0 /* vis */, BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 795bcadc8272..96924821cc1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -56,7 +56,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; -import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; @@ -86,6 +85,8 @@ import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -95,8 +96,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -227,7 +227,8 @@ public class KeyguardIndicationController { // triggered while the device is asleep private final AlarmTimeout mHideTransientMessageHandler; private final AlarmTimeout mHideBiometricMessageHandler; - private FeatureFlags mFeatureFlags; + private final FeatureFlags mFeatureFlags; + private final IndicationHelper mIndicationHelper; /** * Creates a new KeyguardIndicationController and registers callbacks. @@ -259,7 +260,8 @@ public class KeyguardIndicationController { AlarmManager alarmManager, UserTracker userTracker, BouncerMessageInteractor bouncerMessageInteractor, - FeatureFlags flags + FeatureFlags flags, + IndicationHelper indicationHelper ) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; @@ -286,6 +288,7 @@ public class KeyguardIndicationController { mUserTracker = userTracker; mBouncerMessageInteractor = bouncerMessageInteractor; mFeatureFlags = flags; + mIndicationHelper = indicationHelper; mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); @@ -1249,13 +1252,13 @@ public class KeyguardIndicationController { private void onFaceAuthError(int msgId, String errString) { CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage(); mFaceAcquiredMessageDeferral.reset(); - if (shouldSuppressFaceError(msgId)) { - mKeyguardLogger.logBiometricMessage("suppressingFaceError", msgId, errString); + if (mIndicationHelper.shouldSuppressErrorMsg(FACE, msgId)) { + mKeyguardLogger.logBiometricMessage("KIC suppressingFaceError", msgId, errString); return; } if (msgId == FaceManager.FACE_ERROR_TIMEOUT) { handleFaceAuthTimeoutError(deferredFaceMessage); - } else if (isLockoutError(msgId)) { + } else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) { handleFaceLockoutError(errString); } else { showErrorMessageNowOrLater(errString, null); @@ -1263,8 +1266,8 @@ public class KeyguardIndicationController { } private void onFingerprintAuthError(int msgId, String errString) { - if (shouldSuppressFingerprintError(msgId)) { - mKeyguardLogger.logBiometricMessage("suppressingFingerprintError", + if (mIndicationHelper.shouldSuppressErrorMsg(FINGERPRINT, msgId)) { + mKeyguardLogger.logBiometricMessage("KIC suppressingFingerprintError", msgId, errString); } else { @@ -1272,19 +1275,6 @@ public class KeyguardIndicationController { } } - private boolean shouldSuppressFingerprintError(int msgId) { - return ((isPrimaryAuthRequired() && !isLockoutError(msgId)) - || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED - || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED - || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED); - } - - private boolean shouldSuppressFaceError(int msgId) { - return ((isPrimaryAuthRequired() && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) - || msgId == FaceManager.FACE_ERROR_CANCELED - || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS); - } - @Override public void onTrustChanged(int userId) { if (!isCurrentUser(userId)) return; @@ -1408,11 +1398,6 @@ public class KeyguardIndicationController { return mContext.getString(followupMsgId); } - private static boolean isLockoutError(int msgId) { - return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT - || msgId == FaceManager.FACE_ERROR_LOCKOUT; - } - private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) { mKeyguardLogger.logBiometricMessage("deferred message after face auth timeout", null, String.valueOf(deferredFaceMessage)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index c5191154377a..da84afef42c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -27,7 +27,6 @@ import android.content.pm.UserInfo; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserManager; import android.service.notification.StatusBarNotification; @@ -52,6 +51,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.RemoteInputControllerLogger; @@ -60,19 +60,15 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry. import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.policy.RemoteInputView; import com.android.systemui.util.DumpUtilsKt; import com.android.systemui.util.ListenerSet; -import dagger.Lazy; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.function.Consumer; /** @@ -95,10 +91,8 @@ public class NotificationRemoteInputManager implements Dumpable { private final NotificationLockscreenUserManager mLockscreenUserManager; private final SmartReplyController mSmartReplyController; private final NotificationVisibilityProvider mVisibilityProvider; + private final PowerInteractor mPowerInteractor; private final ActionClickLogger mLogger; - - private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy; - protected final Context mContext; protected final NotifPipelineFlags mNotifPipelineFlags; private final UserManager mUserManager; @@ -122,10 +116,8 @@ public class NotificationRemoteInputManager implements Dumpable { @Override public boolean onInteraction( View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) { - mCentralSurfacesOptionalLazy.get().ifPresent( - centralSurfaces -> centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), "NOTIFICATION_CLICK", - PowerManager.WAKE_REASON_GESTURE)); + mPowerInteractor.wakeUpIfDozing( + "NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE); final NotificationEntry entry = getNotificationForParent(view.getParent()); mLogger.logInitialClick(entry, pendingIntent); @@ -259,7 +251,7 @@ public class NotificationRemoteInputManager implements Dumpable { NotificationLockscreenUserManager lockscreenUserManager, SmartReplyController smartReplyController, NotificationVisibilityProvider visibilityProvider, - Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, + PowerInteractor powerInteractor, StatusBarStateController statusBarStateController, RemoteInputUriController remoteInputUriController, RemoteInputControllerLogger remoteInputControllerLogger, @@ -271,7 +263,7 @@ public class NotificationRemoteInputManager implements Dumpable { mLockscreenUserManager = lockscreenUserManager; mSmartReplyController = smartReplyController; mVisibilityProvider = visibilityProvider; - mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy; + mPowerInteractor = powerInteractor; mLogger = logger; mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -402,7 +394,7 @@ public class NotificationRemoteInputManager implements Dumpable { while (p != null) { if (p instanceof View) { View pv = (View) p; - if (pv.isRootNamespace()) { + if (pv.getId() == com.android.internal.R.id.status_bar_latest_event_content) { riv = findRemoteInputView(pv); row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view); break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 73eba0ee9675..075b41b91d97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -28,6 +28,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.AnimationFeatureFlags; import com.android.systemui.animation.DialogLaunchAnimator; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -35,10 +36,10 @@ import com.android.systemui.dump.DumpHandler; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.carrier.ShadeCarrierGroupController; import com.android.systemui.statusbar.ActionClickLogger; @@ -79,14 +80,14 @@ import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.time.SystemClock; -import java.util.Optional; -import java.util.concurrent.Executor; - import dagger.Binds; import dagger.Lazy; import dagger.Module; import dagger.Provides; +import java.util.Optional; +import java.util.concurrent.Executor; + /** * This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to * this separate from {@link CentralSurfacesModule} module so that components that wish to build @@ -104,7 +105,7 @@ public interface CentralSurfacesDependenciesModule { NotificationLockscreenUserManager lockscreenUserManager, SmartReplyController smartReplyController, NotificationVisibilityProvider visibilityProvider, - Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, + PowerInteractor powerInteractor, StatusBarStateController statusBarStateController, RemoteInputUriController remoteInputUriController, RemoteInputControllerLogger remoteInputControllerLogger, @@ -117,7 +118,7 @@ public interface CentralSurfacesDependenciesModule { lockscreenUserManager, smartReplyController, visibilityProvider, - centralSurfacesOptionalLazy, + powerInteractor, statusBarStateController, remoteInputUriController, remoteInputControllerLogger, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/dagger/DisableFlagsModule.kt index 3dcaae28e181..3d5f8df631a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/dagger/DisableFlagsModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * 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. @@ -14,19 +14,16 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.stack; +package com.android.systemui.statusbar.disableflags.dagger -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; - -import dagger.Module; -import dagger.Provides; +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepositoryImpl +import dagger.Binds +import dagger.Module +/** Provides information related to disable flags. */ @Module -public abstract class NotificationStackScrollLayoutListContainerModule { - @Provides - @CentralSurfacesComponent.CentralSurfacesScope - static NotificationListContainer provideListContainer( - NotificationStackScrollLayoutController nsslController) { - return nsslController.getNotificationListContainer(); - } +abstract class DisableFlagsModule { + @Binds + abstract fun bindDisableFlagsRepo(impl: DisableFlagsRepositoryImpl): DisableFlagsRepository } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt new file mode 100644 index 000000000000..ac05248a2b87 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt @@ -0,0 +1,72 @@ +/* + * 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.statusbar.disableflags.data.model + +import android.app.StatusBarManager.DISABLE2_NONE +import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE +import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS +import android.app.StatusBarManager.DISABLE_NONE +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger + +/** + * Model for the disable flags that come from [IStatusBar]. + * + * For clients of the disable flags: do *not* refer to the disable integers directly. Instead, + * re-use or define a helper method that internally processes the flags. (We want to hide the + * bitwise logic here so no one else has to worry about it.) + */ +data class DisableFlagsModel( + private val disable1: Int = DISABLE_NONE, + private val disable2: Int = DISABLE2_NONE, +) { + /** Returns true if notification alerts are allowed based on the flags. */ + fun areNotificationAlertsEnabled(): Boolean { + return (disable1 and DISABLE_NOTIFICATION_ALERTS) == 0 + } + + /** Returns true if the shade is allowed based on the flags. */ + fun isShadeEnabled(): Boolean { + return (disable2 and DISABLE2_NOTIFICATION_SHADE) == 0 + } + + /** Returns true if full quick settings are allowed based on the flags. */ + fun isQuickSettingsEnabled(): Boolean { + return (disable2 and DISABLE2_QUICK_SETTINGS) == 0 + } + + /** Logs the change to the provided buffer. */ + fun logChange(buffer: LogBuffer, disableFlagsLogger: DisableFlagsLogger) { + buffer.log( + TAG, + LogLevel.INFO, + { + int1 = disable1 + int2 = disable2 + }, + { + disableFlagsLogger.getDisableFlagsString( + new = DisableFlagsLogger.DisableState(int1, int2), + ) + } + ) + } + + private companion object { + const val TAG = "DisableFlagsModel" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt new file mode 100644 index 000000000000..13b74b493905 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt @@ -0,0 +1,86 @@ +/* + * 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.statusbar.disableflags.data.repository + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.DisplayId +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.dagger.DisableFlagsRepositoryLog +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn + +/** Repository for the disable flags received from external systems. See [IStatusBar.disable]. */ +interface DisableFlagsRepository { + /** A model of the disable flags last received from [IStatusBar]. */ + val disableFlags: StateFlow<DisableFlagsModel> +} + +@SysUISingleton +class DisableFlagsRepositoryImpl +@Inject +constructor( + commandQueue: CommandQueue, + @DisplayId private val thisDisplayId: Int, + @Application scope: CoroutineScope, + remoteInputQuickSettingsDisabler: RemoteInputQuickSettingsDisabler, + @DisableFlagsRepositoryLog private val logBuffer: LogBuffer, + private val disableFlagsLogger: DisableFlagsLogger, +) : DisableFlagsRepository { + override val disableFlags: StateFlow<DisableFlagsModel> = + conflatedCallbackFlow { + val callback = + object : CommandQueue.Callbacks { + override fun disable( + displayId: Int, + state1: Int, + state2: Int, + animate: Boolean, + ) { + if (displayId != thisDisplayId) { + return + } + trySend( + DisableFlagsModel( + state1, + // Ideally, [RemoteInputQuickSettingsDisabler] should instead + // expose a flow that gets `combine`d with this [disableFlags] + // flow in a [DisableFlagsInteractor] or + // [QuickSettingsInteractor]-type class. However, that's out of + // scope for the CentralSurfaces removal project. + remoteInputQuickSettingsDisabler.adjustDisableFlags(state2), + ) + ) + } + } + commandQueue.addCallback(callback) + awaitClose { commandQueue.removeCallback(callback) } + } + .distinctUntilChanged() + .onEach { it.logChange(logBuffer, disableFlagsLogger) } + // Use Eagerly because we always need to know about disable flags + .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel()) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index cf3903860e94..877846336562 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -176,7 +176,16 @@ constructor( now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis)) } if (weatherTarget != null) { - val weatherData = WeatherData.fromBundle(weatherTarget.baseAction.extras) + val clickIntent = weatherTarget.headerAction?.intent + val weatherData = WeatherData.fromBundle(weatherTarget.baseAction.extras, { v -> + if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + activityStarter.startActivity( + clickIntent, + true, /* dismissShade */ + null, + false) + } + }) if (weatherData != null) { keyguardUpdateMonitor.sendWeatherData(weatherData) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index bab553e18fdc..d10fac6ea3fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -17,15 +17,14 @@ package com.android.systemui.statusbar.notification; import android.app.Notification; import android.os.PowerManager; -import android.os.SystemClock; import android.service.notification.StatusBarNotification; import android.util.Log; import android.view.View; import com.android.systemui.DejankUtils; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.wm.shell.bubbles.Bubbles; import java.util.Optional; @@ -40,7 +39,7 @@ public final class NotificationClicker implements View.OnClickListener { private static final String TAG = "NotificationClicker"; private final NotificationClickerLogger mLogger; - private final Optional<CentralSurfaces> mCentralSurfacesOptional; + private final PowerInteractor mPowerInteractor; private final Optional<Bubbles> mBubblesOptional; private final NotificationActivityStarter mNotificationActivityStarter; @@ -54,11 +53,11 @@ public final class NotificationClicker implements View.OnClickListener { private NotificationClicker( NotificationClickerLogger logger, - Optional<CentralSurfaces> centralSurfacesOptional, + PowerInteractor powerInteractor, Optional<Bubbles> bubblesOptional, NotificationActivityStarter notificationActivityStarter) { mLogger = logger; - mCentralSurfacesOptional = centralSurfacesOptional; + mPowerInteractor = powerInteractor; mBubblesOptional = bubblesOptional; mNotificationActivityStarter = notificationActivityStarter; } @@ -70,9 +69,7 @@ public final class NotificationClicker implements View.OnClickListener { return; } - mCentralSurfacesOptional.ifPresent(centralSurfaces -> centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), "NOTIFICATION_CLICK", - PowerManager.WAKE_REASON_GESTURE)); + mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE); final ExpandableNotificationRow row = (ExpandableNotificationRow) v; final NotificationEntry entry = row.getEntry(); @@ -131,21 +128,22 @@ public final class NotificationClicker implements View.OnClickListener { /** Daggerized builder for NotificationClicker. */ public static class Builder { private final NotificationClickerLogger mLogger; + private final PowerInteractor mPowerInteractor; @Inject - public Builder(NotificationClickerLogger logger) { + public Builder(NotificationClickerLogger logger, PowerInteractor powerInteractor) { mLogger = logger; + mPowerInteractor = powerInteractor; } /** Builds an instance. */ public NotificationClicker build( - Optional<CentralSurfaces> centralSurfacesOptional, Optional<Bubbles> bubblesOptional, NotificationActivityStarter notificationActivityStarter ) { return new NotificationClicker( mLogger, - centralSurfacesOptional, + mPowerInteractor, bubblesOptional, notificationActivityStarter); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 38c37239a7d3..189608072ec7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -140,7 +140,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { .expandableNotificationRow(row) .notificationEntry(entry) .onExpandClickListener(mPresenter) - .listContainer(mListContainer) .build(); ExpandableNotificationRowController rowController = component.getExpandableNotificationRowController(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java index 731ec80817ca..d47fe20911f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java @@ -63,10 +63,27 @@ public class HighPriorityProvider { * - has a media session associated with it * - has messaging style * - * A GroupEntry is considered high priority if its representativeEntry (summary) or children are - * high priority + * A GroupEntry is considered high priority if its representativeEntry (summary) or any of its + * children are high priority. */ public boolean isHighPriority(@Nullable ListEntry entry) { + return isHighPriority(entry, /* allowImplicit = */ true); + } + + /** + * @return true if the ListEntry is explicitly high priority, else false + * + * A NotificationEntry is considered explicitly high priority if has importance greater than or + * equal to IMPORTANCE_DEFAULT. + * + * A GroupEntry is considered explicitly high priority if its representativeEntry (summary) or + * any of its children are explicitly high priority. + */ + public boolean isExplicitlyHighPriority(@Nullable ListEntry entry) { + return isHighPriority(entry, /* allowImplicit= */ false); + } + + private boolean isHighPriority(@Nullable ListEntry entry, boolean allowImplicit) { if (entry == null) { return false; } @@ -77,8 +94,8 @@ public class HighPriorityProvider { } return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT - || hasHighPriorityCharacteristics(notifEntry) - || hasHighPriorityChild(entry); + || (allowImplicit && hasHighPriorityCharacteristics(notifEntry)) + || hasHighPriorityChild(entry, allowImplicit); } /** @@ -112,7 +129,7 @@ public class HighPriorityProvider { >= NotificationManager.IMPORTANCE_DEFAULT); } - private boolean hasHighPriorityChild(ListEntry entry) { + private boolean hasHighPriorityChild(ListEntry entry, boolean allowImplicit) { if (entry instanceof NotificationEntry && !mGroupMembershipManager.isGroupSummary((NotificationEntry) entry)) { return false; @@ -121,7 +138,7 @@ public class HighPriorityProvider { List<NotificationEntry> children = mGroupMembershipManager.getChildren(entry); if (children != null) { for (NotificationEntry child : children) { - if (child != entry && isHighPriority(child)) { + if (child != entry && isHighPriority(child, allowImplicit)) { return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index b100d44bbc5d..31d4ab99098e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -63,8 +63,12 @@ import com.android.systemui.statusbar.notification.logging.NotificationPanelLogg import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; +import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule; import com.android.systemui.statusbar.phone.KeyguardBypassController; import dagger.Binds; @@ -85,6 +89,8 @@ import javax.inject.Provider; ShadeEventsModule.class, NotifPipelineChoreographerModule.class, NotificationSectionHeadersModule.class, + NotificationListViewModelModule.class, + ActivatableNotificationViewModelModule.class, }) public interface NotificationsModule { @Binds @@ -159,6 +165,14 @@ public interface NotificationsModule { } } + /** Provides the container for the notification list. */ + @Provides + @SysUISingleton + static NotificationListContainer provideListContainer( + NotificationStackScrollLayoutController nsslController) { + return nsslController.getNotificationListContainer(); + } + /** * Provide the active notification collection managing the notifications to render. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt new file mode 100644 index 000000000000..8f7e269d34b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt @@ -0,0 +1,34 @@ +/* + * 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.statusbar.notification.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import javax.inject.Inject + +/** Interactor for notifications in general. */ +@SysUISingleton +class NotificationsInteractor +@Inject +constructor( + private val disableFlagsRepository: DisableFlagsRepository, +) { + /** Returns true if notification alerts are allowed. */ + fun areNotificationAlertsEnabled(): Boolean { + return disableFlagsRepository.disableFlags.value.areNotificationAlertsEnabled() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt index c9ebed18c623..76e228bb54d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt @@ -22,7 +22,6 @@ import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.render.NotifStackController import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.CentralSurfaces /** * The master controller for all notifications-related work @@ -32,7 +31,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces */ interface NotificationsController { fun initialize( - centralSurfaces: CentralSurfaces, presenter: NotificationPresenter, listContainer: NotificationListContainer, stackController: NotifStackController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 6409635af250..f7bd177594a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -42,7 +42,6 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger import com.android.systemui.statusbar.notification.logging.NotificationMemoryMonitor import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.wm.shell.bubbles.Bubbles import dagger.Lazy import java.util.Optional @@ -78,7 +77,6 @@ class NotificationsControllerImpl @Inject constructor( ) : NotificationsController { override fun initialize( - centralSurfaces: CentralSurfaces, presenter: NotificationPresenter, listContainer: NotificationListContainer, stackController: NotifStackController, @@ -93,9 +91,7 @@ class NotificationsControllerImpl @Inject constructor( }) notificationRowBinder.setNotificationClicker( - clickerBuilder.build( - Optional.ofNullable(centralSurfaces), bubblesOptional, - notificationActivityStarter)) + clickerBuilder.build(bubblesOptional, notificationActivityStarter)) notificationRowBinder.setUpWithPresenter(presenter, listContainer) headsUpViewBinder.setPresenter(presenter) notifBindPipelineInitializer.initialize() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt index 302a1f422240..65ba6de5b5cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt @@ -23,7 +23,6 @@ import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.render.NotifStackController import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.CentralSurfaces import javax.inject.Inject /** @@ -34,7 +33,6 @@ class NotificationsControllerStub @Inject constructor( ) : NotificationsController { override fun initialize( - centralSurfaces: CentralSurfaces, presenter: NotificationPresenter, listContainer: NotificationListContainer, stackController: NotifStackController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index 7513aa7fa2a2..90014c2518d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -180,8 +180,8 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor( } private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean = when { - // Show if high priority (not hidden) - highPriorityProvider.isHighPriority(entry) -> false + // Show if explicitly high priority (not hidden) + highPriorityProvider.isExplicitlyHighPriority(entry) -> false // Ambient notifications are hidden always from lock screen entry.representativeEntry?.isAmbient == true -> true // [Now notification is silent] 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 417d4acccf4e..8af488ea443d 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 @@ -321,8 +321,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView protected void setBackgroundTintColor(int color) { if (color != mCurrentBackgroundTint) { mCurrentBackgroundTint = color; - // TODO(282173943): re-enable this tinting optimization when Resources are thread-safe - if (false && color == mNormalColor) { + if (color == mNormalColor) { // We don't need to tint a normal notification color = 0; } 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 0bfd3c3c0b2b..30747db0fb64 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 @@ -53,6 +53,7 @@ import android.view.MotionEvent; import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.ViewStub; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -103,6 +104,8 @@ import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; +import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.SwipeableView; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -170,6 +173,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private PeopleNotificationIdentifier mPeopleNotificationIdentifier; private Optional<BubblesManager> mBubblesManagerOptional; private MetricsLogger mMetricsLogger; + private NotificationChildrenContainerLogger mChildrenContainerLogger; private NotificationDismissibilityProvider mDismissibilityProvider; private FeatureFlags mFeatureFlags; private int mIconTransformContentShift; @@ -1627,6 +1631,51 @@ public class ExpandableNotificationRow extends ActivatableNotificationView NotificationEntry child, NotificationEntry newParent ); + + /** + * Called when an ExpandableNotificationRow transient view is removed from the + * NotificationChildrenContainer + */ + void logRemoveTransientFromContainer( + NotificationEntry childEntry, + NotificationEntry containerEntry + ); + + /** + * Called when an ExpandableNotificationRow transient view is removed from the + * NotificationStackScrollLayout + */ + void logRemoveTransientFromNssl( + NotificationEntry childEntry + ); + + /** + * Called when an ExpandableNotificationRow transient view is removed from a ViewGroup that + * is not NotificationChildrenContainer or NotificationStackScrollLayout + */ + void logRemoveTransientFromViewGroup( + NotificationEntry childEntry, + ViewGroup containerView + ); + + /** + * Called when an ExpandableNotificationRow transient view is added to this + * ExpandableNotificationRow + */ + void logAddTransientRow( + NotificationEntry childEntry, + NotificationEntry containerEntry, + int index + ); + + /** + * Called when an ExpandableNotificationRow transient view is removed from this + * ExpandableNotificationRow + */ + void logRemoveTransientRow( + NotificationEntry childEntry, + NotificationEntry containerEntry + ); } /** @@ -1668,6 +1717,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView NotificationGutsManager gutsManager, NotificationDismissibilityProvider dismissibilityProvider, MetricsLogger metricsLogger, + NotificationChildrenContainerLogger childrenContainerLogger, SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController, FeatureFlags featureFlags, @@ -1706,6 +1756,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mBubblesManagerOptional = bubblesManagerOptional; mNotificationGutsManager = gutsManager; mMetricsLogger = metricsLogger; + mChildrenContainerLogger = childrenContainerLogger; mDismissibilityProvider = dismissibilityProvider; mFeatureFlags = featureFlags; } @@ -1874,6 +1925,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mChildrenContainer.setIsLowPriority(mIsLowPriority); mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); mChildrenContainer.onNotificationUpdated(); + mChildrenContainer.setLogger(mChildrenContainerLogger); mTranslateableViews.add(mChildrenContainer); }); @@ -3714,4 +3766,73 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public boolean getShowSnooze() { return mShowSnooze; } + + @Override + public void removeFromTransientContainer() { + final ViewGroup transientContainer = getTransientContainer(); + final ViewParent parent = getParent(); + // Only log when there is real removal of transient views + if (transientContainer == null || transientContainer != parent) { + super.removeFromTransientContainer(); + return; + } + logRemoveFromTransientContainer(transientContainer); + super.removeFromTransientContainer(); + } + + /** + * Log calls to removeFromTransientContainer when the container is NotificationChildrenContainer + * or NotificationStackScrollLayout. + */ + public void logRemoveFromTransientContainer(ViewGroup transientContainer) { + if (mLogger == null) { + return; + } + if (transientContainer instanceof NotificationChildrenContainer) { + mLogger.logRemoveTransientFromContainer( + /* childEntry = */ getEntry(), + /* containerEntry = */ ((NotificationChildrenContainer) transientContainer) + .getContainingNotification().getEntry() + ); + } else if (transientContainer instanceof NotificationStackScrollLayout) { + mLogger.logRemoveTransientFromNssl( + /* childEntry = */ getEntry() + ); + } else { + mLogger.logRemoveTransientFromViewGroup( + /* childEntry = */ getEntry(), + /* containerView = */ transientContainer + ); + } + } + + @Override + public void addTransientView(View view, int index) { + if (view instanceof ExpandableNotificationRow) { + logAddTransientRow((ExpandableNotificationRow) view, index); + } + super.addTransientView(view, index); + } + + private void logAddTransientRow(ExpandableNotificationRow row, int index) { + if (mLogger == null) { + return; + } + mLogger.logAddTransientRow(row.getEntry(), getEntry(), index); + } + + @Override + public void removeTransientView(View view) { + if (view instanceof ExpandableNotificationRow) { + logRemoveTransientRow((ExpandableNotificationRow) view); + } + super.removeTransientView(view); + } + + private void logRemoveTransientRow(ExpandableNotificationRow row) { + if (mLogger == null) { + return; + } + mLogger.logRemoveTransientRow(row.getEntry(), getEntry()); + } } 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 1acc9f90b58f..a4e8c2ece894 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 @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden import com.android.systemui.statusbar.notification.row.dagger.AppName; import com.android.systemui.statusbar.notification.row.dagger.NotificationKey; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; +import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -88,6 +89,7 @@ public class ExpandableNotificationRowController implements NotifViewController private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener; private final StatusBarStateController mStatusBarStateController; private final MetricsLogger mMetricsLogger; + private final NotificationChildrenContainerLogger mChildrenContainerLogger; private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; private final NotificationGutsManager mNotificationGutsManager; private final OnUserInteractionCallback mOnUserInteractionCallback; @@ -125,6 +127,46 @@ public class ExpandableNotificationRowController implements NotifViewController ) { mLogBufferLogger.logSkipAttachingKeepInParentChild(child, newParent); } + + @Override + public void logRemoveTransientFromContainer( + NotificationEntry childEntry, + NotificationEntry containerEntry + ) { + mLogBufferLogger.logRemoveTransientFromContainer(childEntry, containerEntry); + } + + @Override + public void logRemoveTransientFromNssl( + NotificationEntry childEntry + ) { + mLogBufferLogger.logRemoveTransientFromNssl(childEntry); + } + + @Override + public void logRemoveTransientFromViewGroup( + NotificationEntry childEntry, + ViewGroup containerView + ) { + mLogBufferLogger.logRemoveTransientFromViewGroup(childEntry, containerView); + } + + @Override + public void logAddTransientRow( + NotificationEntry childEntry, + NotificationEntry containerEntry, + int index + ) { + mLogBufferLogger.logAddTransientRow(childEntry, containerEntry, index); + } + + @Override + public void logRemoveTransientRow( + NotificationEntry childEntry, + NotificationEntry containerEntry + ) { + mLogBufferLogger.logRemoveTransientRow(childEntry, containerEntry); + } }; @@ -135,6 +177,7 @@ public class ExpandableNotificationRowController implements NotifViewController RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, MetricsLogger metricsLogger, NotificationRowLogger logBufferLogger, + NotificationChildrenContainerLogger childrenContainerLogger, NotificationListContainer listContainer, SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController, @@ -188,6 +231,7 @@ public class ExpandableNotificationRowController implements NotifViewController mBubblesManagerOptional = bubblesManagerOptional; mDragController = dragController; mMetricsLogger = metricsLogger; + mChildrenContainerLogger = childrenContainerLogger; mLogBufferLogger = logBufferLogger; mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; @@ -222,6 +266,7 @@ public class ExpandableNotificationRowController implements NotifViewController mNotificationGutsManager, mDismissibilityProvider, mMetricsLogger, + mChildrenContainerLogger, mSmartReplyConstants, mSmartReplyController, mFeatureFlags, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 0989df61a5e3..6bbeebfdb431 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -16,15 +16,11 @@ package com.android.systemui.statusbar.notification.row; -import static android.graphics.PorterDuff.Mode.SRC_ATOP; - import android.annotation.ColorInt; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.ColorFilter; -import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -161,20 +157,10 @@ public class FooterView extends StackScrollerDecorView { */ public void updateColors() { Resources.Theme theme = mContext.getTheme(); - final @ColorInt int textColor = getResources().getColor(R.color.notif_pill_text, theme); - final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background); - final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background); - // TODO(b/282173943): Remove redundant tinting once Resources are thread-safe - final @ColorInt int buttonBgColor = - Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface); - final ColorFilter bgColorFilter = new PorterDuffColorFilter(buttonBgColor, SRC_ATOP); - if (buttonBgColor != 0) { - clearAllBg.setColorFilter(bgColorFilter); - manageBg.setColorFilter(bgColorFilter); - } - mClearAllButton.setBackground(clearAllBg); + int textColor = getResources().getColor(R.color.notif_pill_text, theme); + mClearAllButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); mClearAllButton.setTextColor(textColor); - mManageButton.setBackground(manageBg); + mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); mManageButton.setTextColor(textColor); final @ColorInt int labelTextColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); 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 b4bfded58e4b..13d1978ec8ff 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 @@ -474,7 +474,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder parentLayout, remoteViewClickHandler); validateView(v, entry, row.getResources()); - v.setIsRootNamespace(true); applyCallback.setResultView(v); } else { newContentView.reapply( @@ -511,7 +510,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder return; } if (isNewView) { - v.setIsRootNamespace(true); applyCallback.setResultView(v); } else if (existingWrapper != null) { existingWrapper.onReinflated(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 124df8c3b815..20f4429f294b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -208,6 +208,23 @@ public class NotificationContentView extends FrameLayout implements Notification mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; mStatusBarService = statusBarService; + // We set root namespace so that we avoid searching children for id. Notification might + // contain custom view and their ids may clash with ids already existing in shade or + // notification panel + setIsRootNamespace(true); + } + + @Override + public View focusSearch(View focused, int direction) { + // This implementation is copied from ViewGroup but with removed special handling of + // setIsRootNamespace. This view is set as tree root using setIsRootNamespace and it + // causes focus to be stuck inside of it. We need to be root to avoid id conflicts + // but we don't want to behave like root when it comes to focusing. + if (mParent != null) { + return mParent.focusSearch(focused, direction); + } + Log.wtf(TAG, "NotificationContentView doesn't have parent"); + return null; } public void reinflate() { 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 c3dd92a51a91..89338f9eeed3 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 @@ -17,14 +17,21 @@ package com.android.systemui.statusbar.notification.row +import android.view.ViewGroup import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel import com.android.systemui.log.dagger.NotificationLog +import com.android.systemui.log.dagger.NotificationRenderLog import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject -class NotificationRowLogger @Inject constructor(@NotificationLog private val buffer: LogBuffer) { +class NotificationRowLogger +@Inject +constructor( + @NotificationLog private val buffer: LogBuffer, + @NotificationRenderLog private val notificationRenderBuffer: LogBuffer +) { fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) { buffer.log( TAG, @@ -48,6 +55,79 @@ class NotificationRowLogger @Inject constructor(@NotificationLog private val buf { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" } ) } + + fun logRemoveTransientFromContainer( + childEntry: NotificationEntry, + containerEntry: NotificationEntry + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.INFO, + { + str1 = childEntry.logKey + str2 = containerEntry.logKey + }, + { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" } + ) + } + + fun logRemoveTransientFromNssl( + childEntry: NotificationEntry, + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.INFO, + { str1 = childEntry.logKey }, + { "RemoveTransientRow from Nssl: childKey: $str1" } + ) + } + + fun logRemoveTransientFromViewGroup( + childEntry: NotificationEntry, + containerView: ViewGroup, + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.WARNING, + { + str1 = childEntry.logKey + str2 = containerView.toString() + }, + { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" } + ) + } + + fun logAddTransientRow( + childEntry: NotificationEntry, + containerEntry: NotificationEntry, + index: Int + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.ERROR, + { + str1 = childEntry.logKey + str2 = containerEntry.logKey + int1 = index + }, + { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" } + ) + } + + fun logRemoveTransientRow( + childEntry: NotificationEntry, + containerEntry: NotificationEntry, + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.ERROR, + { + str1 = childEntry.logKey + str2 = containerEntry.logKey + }, + { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" } + ) + } } private const val TAG = "NotifRow" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java index 1a7417a78186..3588621b12ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; -import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CentralSurfaces; import dagger.Binds; @@ -59,8 +58,6 @@ public interface ExpandableNotificationRowComponent { Builder notificationEntry(NotificationEntry entry); @BindsInstance Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter); - @BindsInstance - Builder listContainer(NotificationListContainer listContainer); ExpandableNotificationRowComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt index fb1944315e10..5ca8b53d0704 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt @@ -16,16 +16,16 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewmodel +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map /** ViewModel for [NotificationShelf]. */ -@CentralSurfacesScope +@SysUISingleton class NotificationShelfViewModel @Inject constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index f8e374de11e2..dad806418f2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -132,6 +132,8 @@ public class NotificationChildrenContainer extends ViewGroup private boolean mContainingNotificationIsFaded = false; private RoundableState mRoundableState; + private NotificationChildrenContainerLogger mLogger; + public NotificationChildrenContainer(Context context) { this(context, null); } @@ -1507,6 +1509,33 @@ public class NotificationChildrenContainer extends ViewGroup return mNotificationHeaderWrapper; } + public void setLogger(NotificationChildrenContainerLogger logger) { + mLogger = logger; + } + + @Override + public void addTransientView(View view, int index) { + if (mLogger != null && view instanceof ExpandableNotificationRow) { + mLogger.addTransientRow( + ((ExpandableNotificationRow) view).getEntry(), + getContainingNotification().getEntry(), + index + ); + } + super.addTransientView(view, index); + } + + @Override + public void removeTransientView(View view) { + if (mLogger != null && view instanceof ExpandableNotificationRow) { + mLogger.removeTransientRow( + ((ExpandableNotificationRow) view).getEntry(), + getContainingNotification().getEntry() + ); + } + super.removeTransientView(view); + } + public String debugString() { return TAG + " { " + "visibility: " + getVisibility() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt new file mode 100644 index 000000000000..6be1ef8711df --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt @@ -0,0 +1,64 @@ +/* + * 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.statusbar.notification.stack + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.dagger.NotificationRenderLog +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.logKey +import javax.inject.Inject + +class NotificationChildrenContainerLogger +@Inject +constructor(@NotificationRenderLog private val notificationRenderBuffer: LogBuffer) { + fun addTransientRow( + childEntry: NotificationEntry, + containerEntry: NotificationEntry, + index: Int + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.INFO, + { + str1 = childEntry.logKey + str2 = containerEntry.logKey + int1 = index + }, + { "addTransientRow: childKey: $str1 -- containerKey: $str2 -- index: $int1" } + ) + } + + fun removeTransientRow( + childEntry: NotificationEntry, + containerEntry: NotificationEntry, + ) { + notificationRenderBuffer.log( + TAG, + LogLevel.INFO, + { + str1 = childEntry.logKey + str2 = containerEntry.logKey + }, + { "removeTransientRow: childKey: $str1 -- containerKey: $str2" } + ) + } + + companion object { + private const val TAG = "NotifChildrenContainer" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index d1413a275ff2..479fbdb348d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -2759,6 +2759,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable boolean animationGenerated = container != null && generateRemoveAnimation(child); if (animationGenerated) { if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) { + logAddTransientChild(child, container); container.addTransientView(child, 0); child.setTransientContainer(container); } @@ -2774,6 +2775,46 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable focusNextViewIfFocused(child); } + private void logAddTransientChild(ExpandableView child, ViewGroup container) { + if (mLogger == null) { + return; + } + if (child instanceof ExpandableNotificationRow) { + if (container instanceof NotificationChildrenContainer) { + mLogger.addTransientChildNotificationToChildContainer( + ((ExpandableNotificationRow) child).getEntry(), + ((NotificationChildrenContainer) container) + .getContainingNotification().getEntry() + ); + } else if (container instanceof NotificationStackScrollLayout) { + mLogger.addTransientChildNotificationToNssl( + ((ExpandableNotificationRow) child).getEntry() + ); + } else { + mLogger.addTransientChildNotificationToViewGroup( + ((ExpandableNotificationRow) child).getEntry(), + container + ); + } + } + } + + @Override + public void addTransientView(View view, int index) { + if (mLogger != null && view instanceof ExpandableNotificationRow) { + mLogger.addTransientRow(((ExpandableNotificationRow) view).getEntry(), index); + } + super.addTransientView(view, index); + } + + @Override + public void removeTransientView(View view) { + if (mLogger != null && view instanceof ExpandableNotificationRow) { + mLogger.removeTransientRow(((ExpandableNotificationRow) view).getEntry()); + } + super.removeTransientView(view); + } + /** * Has this view been fully swiped out such that it's not visible anymore. */ @@ -3033,7 +3074,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (!animationsEnabled) { mSwipedOutViews.clear(); mChildrenToRemoveAnimated.clear(); - clearTemporaryViewsInGroup(this); + clearTemporaryViewsInGroup( + /* viewGroup = */ this, + /* reason = */ "setAnimationsEnabled"); } } @@ -4008,26 +4051,48 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void clearTemporaryViews() { // lets make sure nothing is transient anymore - clearTemporaryViewsInGroup(this); + clearTemporaryViewsInGroup( + /* viewGroup = */ this, + /* reason = */ "clearTemporaryViews" + ); for (int i = 0; i < getChildCount(); i++) { ExpandableView child = getChildAtIndex(i); if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; - clearTemporaryViewsInGroup(row.getChildrenContainer()); + clearTemporaryViewsInGroup( + /* viewGroup = */ row.getChildrenContainer(), + /* reason = */ "clearTemporaryViewsInGroup(row.getChildrenContainer())" + ); } } } - private void clearTemporaryViewsInGroup(ViewGroup viewGroup) { + private void clearTemporaryViewsInGroup(ViewGroup viewGroup, String reason) { while (viewGroup != null && viewGroup.getTransientViewCount() != 0) { final View transientView = viewGroup.getTransientView(0); viewGroup.removeTransientView(transientView); if (transientView instanceof ExpandableView) { ((ExpandableView) transientView).setTransientContainer(null); + if (transientView instanceof ExpandableNotificationRow) { + logTransientNotificationRowTraversalCleaned( + (ExpandableNotificationRow) transientView, + reason + ); + } } } } + private void logTransientNotificationRowTraversalCleaned( + ExpandableNotificationRow transientView, + String reason + ) { + if (mLogger == null) { + return; + } + mLogger.transientNotificationRowTraversalCleaned(transientView.getEntry(), reason); + } + void onPanelTrackingStarted() { mPanelTracking = true; mAmbientState.setPanelTracking(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index ad7cdc4eb50f..86b88ccf3ca0 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 @@ -59,6 +59,7 @@ import com.android.systemui.Gefingerpoken; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; @@ -118,7 +119,6 @@ import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -141,7 +141,7 @@ import javax.inject.Named; /** * Controller for {@link NotificationStackScrollLayout}. */ -@CentralSurfacesComponent.CentralSurfacesScope +@SysUISingleton public class NotificationStackScrollLayoutController { private static final String TAG = "StackScrollerController"; private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); @@ -1462,7 +1462,7 @@ public class NotificationStackScrollLayoutController { return mNotificationRoundnessManager; } - NotificationListContainer getNotificationListContainer() { + public NotificationListContainer getNotificationListContainer() { return mNotificationListContainer; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt index 5b0ec1d14edc..9c1bd174107e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt @@ -1,9 +1,12 @@ package com.android.systemui.statusbar.notification.stack +import android.view.ViewGroup import com.android.systemui.log.dagger.NotificationHeadsUpLog import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.dagger.NotificationRenderLog import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD @@ -15,7 +18,8 @@ import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject class NotificationStackScrollLogger @Inject constructor( - @NotificationHeadsUpLog private val buffer: LogBuffer + @NotificationHeadsUpLog private val buffer: LogBuffer, + @NotificationRenderLog private val notificationRenderBuffer: LogBuffer ) { fun hunAnimationSkipped(entry: NotificationEntry, reason: String) { buffer.log(TAG, INFO, { @@ -77,6 +81,79 @@ class NotificationStackScrollLogger @Inject constructor( "isTouchBelowNotification: $bool2 motionEvent: $str1" }) } + + fun transientNotificationRowTraversalCleaned(entry: NotificationEntry, reason: String) { + notificationRenderBuffer.log(TAG, INFO, { + str1 = entry.logKey + str2 = reason + }, { + "transientNotificationRowTraversalCleaned: key: $str1 reason: $str2" + }) + } + + fun addTransientChildNotificationToChildContainer( + childEntry: NotificationEntry, + containerEntry: NotificationEntry, + ) { + notificationRenderBuffer.log(TAG, INFO, { + str1 = childEntry.logKey + str2 = containerEntry.logKey + }, { + "addTransientChildToContainer from onViewRemovedInternal: childKey: $str1 " + + "-- containerKey: $str2" + }) + } + + fun addTransientChildNotificationToNssl( + childEntry: NotificationEntry, + ) { + notificationRenderBuffer.log(TAG, INFO, { + str1 = childEntry.logKey + }, { + "addTransientRowToNssl from onViewRemovedInternal: childKey: $str1" + }) + } + + fun addTransientChildNotificationToViewGroup( + childEntry: NotificationEntry, + container: ViewGroup + ) { + notificationRenderBuffer.log(TAG, ERROR, { + str1 = childEntry.logKey + str2 = container.toString() + }, { + "addTransientRowTo unhandled ViewGroup from onViewRemovedInternal: childKey: $str1 " + + "-- ViewGroup: $str2" + }) + } + + fun addTransientRow( + childEntry: NotificationEntry, + index: Int + ) { + notificationRenderBuffer.log( + TAG, + INFO, + { + str1 = childEntry.logKey + int1 = index + }, + { "addTransientRow to NSSL: childKey: $str1 -- index: $int1" } + ) + } + + fun removeTransientRow( + childEntry: NotificationEntry, + ) { + notificationRenderBuffer.log( + TAG, + INFO, + { + str1 = childEntry.logKey + }, + { "removeTransientRow from NSSL: childKey: $str1" } + ) + } } private const val TAG = "NotificationStackScroll"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index f07dd00827b0..d73919b82c42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -347,10 +347,12 @@ public class StackStateAnimator { final ExpandableView changingView = (ExpandableView) event.mChangingView; boolean loggable = false; boolean isHeadsUp = false; + boolean isGroupChild = false; String key = null; if (changingView instanceof ExpandableNotificationRow && mLogger != null) { loggable = true; isHeadsUp = ((ExpandableNotificationRow) changingView).isHeadsUp(); + isGroupChild = changingView.isChildInGroup(); key = ((ExpandableNotificationRow) changingView).getEntry().getKey(); } if (event.animationType == @@ -407,13 +409,21 @@ public class StackStateAnimator { } Runnable postAnimation = changingView::removeFromTransientContainer; - if (loggable && isHeadsUp) { - mLogger.logHUNViewDisappearingWithRemoveEvent(key); + if (loggable) { String finalKey = key; - postAnimation = () -> { - mLogger.disappearAnimationEnded(finalKey); - changingView.removeFromTransientContainer(); - }; + if (isHeadsUp) { + mLogger.logHUNViewDisappearingWithRemoveEvent(key); + postAnimation = () -> { + mLogger.disappearAnimationEnded(finalKey); + changingView.removeFromTransientContainer(); + }; + } else if (isGroupChild) { + mLogger.groupChildRemovalEventProcessed(key); + postAnimation = () -> { + mLogger.groupChildRemovalAnimationEnded(finalKey); + changingView.removeFromTransientContainer(); + }; + } } changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, 0 /* delay */, translationDirection, false /* isHeadsUpAppear */, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt index cca84b3330a9..c7f80f311650 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt @@ -3,11 +3,13 @@ package com.android.systemui.statusbar.notification.stack import com.android.systemui.log.dagger.NotificationHeadsUpLog import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel +import com.android.systemui.log.dagger.NotificationRenderLog import com.android.systemui.statusbar.notification.logKey import javax.inject.Inject class StackStateLogger @Inject constructor( - @NotificationHeadsUpLog private val buffer: LogBuffer + @NotificationHeadsUpLog private val buffer: LogBuffer, + @NotificationRenderLog private val notificationRenderBuffer: LogBuffer ) { fun logHUNViewDisappearing(key: String) { buffer.log(TAG, LogLevel.INFO, { @@ -56,6 +58,21 @@ class StackStateLogger @Inject constructor( "Heads up notification appear animation ended $str1 " }) } + + fun groupChildRemovalEventProcessed(key: String) { + notificationRenderBuffer.log(TAG, LogLevel.DEBUG, { + str1 = logKey(key) + }, { + "Group Child Notification removal event processed $str1 for ANIMATION_TYPE_REMOVE" + }) + } + fun groupChildRemovalAnimationEnded(key: String) { + notificationRenderBuffer.log(TAG, LogLevel.INFO, { + str1 = logKey(key) + }, { + "Group child notification removal animation ended $str1 " + }) + } } private const val TAG = "StackScroll"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt index aab1c2b877e8..11f68e0f663b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel @@ -33,6 +34,7 @@ class NotificationListViewModel( object NotificationListViewModelModule { @JvmStatic @Provides + @SysUISingleton fun maybeProvideViewModel( featureFlags: FeatureFlags, shelfViewModel: Provider<NotificationShelfViewModel>, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 01c00b6308cb..0242e913e573 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; -import android.os.PowerManager; import android.os.UserHandle; import android.view.KeyEvent; import android.view.MotionEvent; @@ -43,7 +42,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; -import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSPanelController; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; @@ -196,14 +194,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { @Override Lifecycle getLifecycle(); - /** - * Wakes up the device if the device was dozing. - * - * @deprecated Use {@link PowerInteractor#wakeUpIfDozing(String, int)} instead. - */ - @Deprecated - void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason); - /** */ ShadeViewController getShadeViewController(); @@ -212,8 +202,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { int getStatusBarHeight(); - void updateQsExpansionEnabled(); - boolean isShadeDisabled(); boolean isLaunchingActivityOverLockscreen(); @@ -406,9 +394,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void setLaunchEmergencyActionOnFinishedWaking(boolean launch); QSPanelController getQSPanelController(); - - boolean areNotificationAlertsDisabled(); - float getDisplayDensity(); void extendDozePulse(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index 555a3c2c439f..332be2aa957d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -244,8 +244,13 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba } /** * State is one or more of the DISABLE constants from StatusBarManager. + * + * @deprecated If you need to react to changes in disable flags, listen to + * {@link com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository} + * instead. */ @Override + @Deprecated public void disable(int displayId, int state1, int state2, boolean animate) { if (displayId != mDisplayId) { return; @@ -275,17 +280,12 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba } if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { - if (mCentralSurfaces.areNotificationAlertsDisabled()) { + if ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { mHeadsUpManager.releaseAllImmediately(); } } - if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) { - mCentralSurfaces.updateQsExpansionEnabled(); - } - if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) { - mCentralSurfaces.updateQsExpansionEnabled(); if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) { mShadeController.animateCollapseShade(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index bba581b7f858..4ae4c52d19ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -312,7 +312,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final DeviceStateManager mDeviceStateManager; private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks; private float mTransitionToFullShadeProgress = 0f; - private NotificationListContainer mNotifListContainer; + private final NotificationListContainer mNotifListContainer; private boolean mIsShortcutListSearchEnabled; private final KeyguardStateController.Callback mKeyguardStateControllerCallback = @@ -660,8 +660,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final ActivityIntentHelper mActivityIntentHelper; - @VisibleForTesting - protected NotificationStackScrollLayoutController mStackScrollerController; + private final NotificationStackScrollLayoutController mStackScrollerController; private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener = (extractor, which) -> updateTheme(); @@ -749,6 +748,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { ConfigurationController configurationController, NotificationShadeWindowController notificationShadeWindowController, NotificationShelfController notificationShelfController, + NotificationStackScrollLayoutController notificationStackScrollLayoutController, DozeParameters dozeParameters, ScrimController scrimController, Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, @@ -849,6 +849,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mConfigurationController = configurationController; mNotificationShadeWindowController = notificationShadeWindowController; mNotificationShelfController = notificationShelfController; + mStackScrollerController = notificationStackScrollLayoutController; + mStackScroller = mStackScrollerController.getView(); + mNotifListContainer = mStackScrollerController.getNotificationListContainer(); mDozeServiceHost = dozeServiceHost; mPowerManager = powerManager; mDozeParameters = dozeParameters; @@ -1582,7 +1585,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); mShadeController.setNotificationPresenter(mPresenter); mNotificationsController.initialize( - this, mPresenter, mNotifListContainer, mStackScrollerController.getNotifStackController(), @@ -1598,24 +1600,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */); } - /** - * Ask the display to wake up if currently dozing, else do nothing - * - * @deprecated Use {@link PowerInteractor#wakeUpIfDozing(String, int)} instead. - * - * @param time when to wake up - * @param why the reason for the wake up - */ - @Override - @Deprecated - public void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason) { - if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) { - mPowerManager.wakeUp( - time, wakeReason, "com.android.systemui:" + why); - mFalsingCollector.onScreenOnFromTouch(); - } - } - // TODO(b/117478341): This was left such that CarStatusBar can override this method. // Try to remove this. protected void createNavigationBar(@Nullable RegisterStatusBarResult result) { @@ -1658,12 +1642,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mShadeController.setNotificationPanelViewController(npvc); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); - mStackScrollerController = - mCentralSurfacesComponent.getNotificationStackScrollLayoutController(); mQsController = mCentralSurfacesComponent.getQuickSettingsController(); mBackActionInteractor.setup(mQsController, mShadeSurface); - mStackScroller = mStackScrollerController.getView(); - mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer(); mPresenter = mCentralSurfacesComponent.getNotificationPresenter(); mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter(); @@ -1744,23 +1724,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { return mStatusBarWindowController.getStatusBarHeight(); } - /** - * Disable QS if device not provisioned. - * If the user switcher is simple then disable QS during setup because - * the user intends to use the lock screen user switcher, QS in not needed. - */ - @Override - public void updateQsExpansionEnabled() { - final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned() - && (mUserSetup || mUserSwitcherController == null - || !mUserSwitcherController.isSimpleUserSwitcher()) - && !isShadeDisabled() - && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) - && !mDozing; - mQsController.setExpansionEnabledPolicy(expandEnabled); - Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled); - } - @Override public boolean isShadeDisabled() { return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0; @@ -1774,11 +1737,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); } - @Override - public boolean areNotificationAlertsDisabled() { - return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; - } - /** * Whether we are currently animating an activity launch above the lockscreen (occluding * activity). @@ -2728,7 +2686,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { || (mDozing && mDozeParameters.shouldControlScreenOff() && keyguardVisibleOrWillBe); mShadeSurface.setDozing(mDozing, animate); - updateQsExpansionEnabled(); Trace.endSection(); } @@ -2913,8 +2870,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); if (mBouncerShowing) { - wakeUpIfDozing(SystemClock.uptimeMillis(), "BOUNCER_VISIBLE", - PowerManager.WAKE_REASON_GESTURE); + mPowerInteractor.wakeUpIfDozing("BOUNCER_VISIBLE", PowerManager.WAKE_REASON_GESTURE); } updateScrimController(); if (!mBouncerShowing) { @@ -3369,7 +3325,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected IStatusBarService mBarService; // all notifications - protected NotificationStackScrollLayout mStackScroller; + private final NotificationStackScrollLayout mStackScroller; protected AccessibilityManager mAccessibilityManager; @@ -3636,7 +3592,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mShadeSurface.collapse(true /* animate */, false /* delayed */, 1.0f /* speedUpFactor */); } - updateQsExpansionEnabled(); } } }; @@ -3771,7 +3726,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // reset them again when we're waking up mShadeSurface.resetViews(dozingAnimated && isDozing); - updateQsExpansionEnabled(); mKeyguardViewMediator.setDozing(mDozing); updateDozingState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index 7bc4fc3c5e47..ae70384aa71a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -115,7 +115,6 @@ public class LightBarTransitionsController implements Dumpable { }; private final Context mContext; - private Boolean mOverrideIconTintForNavMode; @AssistedInject public LightBarTransitionsController( @@ -276,19 +275,11 @@ public class LightBarTransitionsController implements Dumpable { } /** - * Specify an override value to return for {@link #overrideIconTintForNavMode(boolean)}. - */ - public void overrideIconTintForNavMode(boolean overrideValue) { - mOverrideIconTintForNavMode = overrideValue; - } - /** * Return whether to use the tint calculated in this class for nav icons. */ public boolean supportsIconTintForNavMode(int navigationMode) { // In gesture mode, we already do region sampling to update tint based on content beneath. - return mOverrideIconTintForNavMode != null - ? mOverrideIconTintForNavMode - : !QuickStepContract.isGesturalMode(navigationMode); + return !QuickStepContract.isGesturalMode(navigationMode); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 3b56d2769d7f..35285b222f63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -22,7 +22,6 @@ import android.content.Context; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.service.notification.StatusBarNotification; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; @@ -36,6 +35,7 @@ import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.ShadeViewController; @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; +import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -83,7 +84,9 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu private final AboveShelfObserver mAboveShelfObserver; private final DozeScrimController mDozeScrimController; private final CentralSurfaces mCentralSurfaces; + private final NotificationsInteractor mNotificationsInteractor; private final LockscreenShadeTransitionController mShadeTransitionController; + private final PowerInteractor mPowerInteractor; private final CommandQueue mCommandQueue; private final AccessibilityManager mAccessibilityManager; @@ -111,7 +114,9 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu DynamicPrivacyController dynamicPrivacyController, KeyguardStateController keyguardStateController, CentralSurfaces centralSurfaces, + NotificationsInteractor notificationsInteractor, LockscreenShadeTransitionController shadeTransitionController, + PowerInteractor powerInteractor, CommandQueue commandQueue, NotificationLockscreenUserManager lockscreenUserManager, SysuiStatusBarStateController sysuiStatusBarStateController, @@ -133,7 +138,9 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu mDynamicPrivacyController = dynamicPrivacyController; // TODO: use KeyguardStateController#isOccluded to remove this dependency mCentralSurfaces = centralSurfaces; + mNotificationsInteractor = notificationsInteractor; mShadeTransitionController = shadeTransitionController; + mPowerInteractor = powerInteractor; mCommandQueue = commandQueue; mLockscreenUserManager = lockscreenUserManager; mStatusBarStateController = sysuiStatusBarStateController; @@ -234,9 +241,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu public void onExpandClicked(NotificationEntry clickedEntry, View clickedView, boolean nowExpanded) { mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); - mCentralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), "NOTIFICATION_CLICK", - PowerManager.WAKE_REASON_GESTURE); + mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE); if (nowExpanded) { if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { mShadeTransitionController.goToLockedShade(clickedEntry.getRow()); @@ -333,7 +338,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu @Override public boolean suppressInterruptions(NotificationEntry entry) { - return mCentralSurfaces.areNotificationAlertsDisabled(); + return !mNotificationsInteractor.areNotificationAlertsEnabled(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java index 64c798b99a18..c618be843832 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java @@ -28,9 +28,6 @@ import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.ShadeHeaderController; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule; import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks; import com.android.systemui.statusbar.phone.CentralSurfacesImpl; import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener; @@ -56,7 +53,6 @@ import javax.inject.Scope; * outside the component. Should more items be moved *into* this component to avoid so many getters? */ @Subcomponent(modules = { - NotificationStackScrollLayoutListContainerModule.class, StatusBarViewModule.class, StatusBarNotificationActivityStarterModule.class, StatusBarNotificationPresenterModule.class, @@ -87,9 +83,6 @@ public interface CentralSurfacesComponent { */ NotificationShadeWindowView getNotificationShadeWindowView(); - /** */ - NotificationStackScrollLayoutController getNotificationStackScrollLayoutController(); - /** * Creates a NotificationShadeWindowViewController. */ @@ -128,6 +121,4 @@ public interface CentralSurfacesComponent { NotificationActivityStarter getNotificationActivityStarter(); NotificationPresenter getNotificationPresenter(); - - NotificationListContainer getNotificationListContainer(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 260d986db6c0..6b0746f08df0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -31,8 +31,6 @@ import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; -import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule; -import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; @@ -60,11 +58,14 @@ import java.util.concurrent.Executor; import javax.inject.Named; -@Module(subcomponents = StatusBarFragmentComponent.class, - includes = { - ActivatableNotificationViewModelModule.class, - NotificationListViewModelModule.class, - }) +/** + * A module for {@link CentralSurfacesComponent.CentralSurfacesScope} components. + * + * @deprecated CentralSurfacesScope will be removed shortly (b/277762009). Classes should be + * annotated with @SysUISingleton instead. + */ +@Module(subcomponents = StatusBarFragmentComponent.class) +@Deprecated public abstract class StatusBarViewModule { public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt index a47f95d69c65..74352d29cd9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt @@ -57,7 +57,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -205,9 +204,6 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), null) - private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> = - MutableSharedFlow(extraBufferCapacity = 1) - override val defaultDataSubId: StateFlow<Int> = broadcastDispatcher .broadcastFlow( @@ -223,7 +219,6 @@ constructor( initialValue = INVALID_SUBSCRIPTION_ID, ) .onStart { emit(subscriptionManagerProxy.getDefaultDataSubscriptionId()) } - .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) } .stateIn(scope, SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID) private val carrierConfigChangedEvent = @@ -232,7 +227,7 @@ constructor( .onEach { logger.logActionCarrierConfigChanged() } override val defaultDataSubRatConfig: StateFlow<Config> = - merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent) + merge(defaultDataSubId, carrierConfigChangedEvent) .onStart { emit(Unit) } .mapLatest { Config.readConfig(context) } .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt index 2b4f51c63043..4c6374b75f82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt @@ -35,7 +35,10 @@ protected constructor( ) : BaseAdapter() { protected open val users: List<UserRecord> - get() = controller.users.filter { !controller.isKeyguardShowing || !it.isRestricted } + get() = controller.users.filter { + (!controller.isKeyguardShowing || !it.isRestricted) && + (controller.isUserSwitcherEnabled || it.isCurrent) + } init { controller.addAdapter(WeakReference(this)) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt index 1e223b1920ed..f88339a4d077 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt @@ -67,6 +67,9 @@ constructor( val isSimpleUserSwitcher: Boolean get() = userInteractor.isSimpleUserSwitcher + val isUserSwitcherEnabled: Boolean + get() = userInteractor.isUserSwitcherEnabled + /** The [UserRecord] of the current user or `null` when none. */ val currentUserRecord: UserRecord? get() = userInteractor.selectedUserRecord.value diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index a0b56aa6f5c3..3de75ca2ed87 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -105,6 +105,8 @@ interface UserRepository { fun getSelectedUserInfo(): UserInfo fun isSimpleUserSwitcher(): Boolean + + fun isUserSwitcherEnabled(): Boolean } @SysUISingleton @@ -206,6 +208,10 @@ constructor( return _userSwitcherSettings.value.isSimpleUserSwitcher } + override fun isUserSwitcherEnabled(): Boolean { + return _userSwitcherSettings.value.isUserSwitcherEnabled + } + private fun observeUserSwitching() { conflatedCallbackFlow { val callback = diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt index 27c348ba329a..a487f538e59c 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt @@ -294,6 +294,10 @@ constructor( val isSimpleUserSwitcher: Boolean get() = repository.isSimpleUserSwitcher() + + val isUserSwitcherEnabled: Boolean + get() = repository.isUserSwitcherEnabled() + val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { override fun onKeyguardGoingAway() { @@ -370,6 +374,7 @@ constructor( } pw.println("isSimpleUserSwitcher=$isSimpleUserSwitcher") + pw.println("isUserSwitcherEnabled=$isUserSwitcherEnabled") pw.println("isGuestUserAutoCreated=$isGuestUserAutoCreated") } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt index d6b3b22bfd02..12d7b4d372e8 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt @@ -19,12 +19,44 @@ package com.android.systemui.util.kotlin import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.lifecycle.repeatWhenAttached import java.util.function.Consumer +import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch + +/** A class allowing Java classes to collect on Kotlin flows. */ +@SysUISingleton +class JavaAdapter +@Inject +constructor( + @Application private val scope: CoroutineScope, +) { + /** + * Collect information for the given [flow], calling [consumer] for each emitted event. + * + * Important: This will immediately start collection and *never* stop it. This should only be + * used by classes that *need* to always be collecting a value and processing it. Whenever + * possible, please use [collectFlow] instead; that method will stop the collection when a view + * has disappeared, which will ensure that we don't perform unnecessary work. + * + * Do *not* call this method in a class's constructor. Instead, call it in + * [com.android.systemui.CoreStartable.start] or similar method. + */ + fun <T> alwaysCollectFlow( + flow: Flow<T>, + consumer: Consumer<T>, + ): Job { + return scope.launch { flow.collect { consumer.accept(it) } } + } +} /** * Collect information for the given [flow], calling [consumer] for each emitted event. Defaults to diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 5144d1966222..4c7e6b007f38 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -334,8 +334,10 @@ public final class WMShell implements } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, - int backDisposition, boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, + boolean showImeSwitcher) { if (displayId == mDisplayTracker.getDefaultDisplayId() && (vis & InputMethodService.IME_VISIBLE) != 0) { oneHanded.stopOneHanded( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java index 48a8d1bc90d7..e7d420bcb32b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java @@ -561,6 +561,66 @@ public class CarrierTextManagerTest extends SysuiTestCase { captor.getValue().carrierText); } + @Test + public void testGetStatusForIccState() { + when(mKeyguardUpdateMonitor.isDeviceProvisioned()).thenReturn(false); + assertEquals(CarrierTextManager.StatusMode.SimMissingLocked, + mCarrierTextManager.getStatusForIccState(TelephonyManager.SIM_STATE_ABSENT)); + assertEquals(CarrierTextManager.StatusMode.NetworkLocked, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_NETWORK_LOCKED)); + assertEquals(CarrierTextManager.StatusMode.SimNotReady, + mCarrierTextManager.getStatusForIccState(TelephonyManager.SIM_STATE_NOT_READY)); + assertEquals(CarrierTextManager.StatusMode.SimLocked, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_PIN_REQUIRED)); + assertEquals(CarrierTextManager.StatusMode.SimPukLocked, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_PUK_REQUIRED)); + assertEquals(CarrierTextManager.StatusMode.Normal, + mCarrierTextManager.getStatusForIccState(TelephonyManager.SIM_STATE_READY)); + assertEquals(CarrierTextManager.StatusMode.SimMissingLocked, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_PERM_DISABLED)); + assertEquals(CarrierTextManager.StatusMode.SimUnknown, + mCarrierTextManager.getStatusForIccState(TelephonyManager.SIM_STATE_UNKNOWN)); + assertEquals(CarrierTextManager.StatusMode.SimIoError, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_CARD_IO_ERROR)); + assertEquals(CarrierTextManager.StatusMode.SimRestricted, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_CARD_RESTRICTED)); + + when(mKeyguardUpdateMonitor.isDeviceProvisioned()).thenReturn(true); + assertEquals(CarrierTextManager.StatusMode.SimMissing, + mCarrierTextManager.getStatusForIccState(TelephonyManager.SIM_STATE_ABSENT)); + assertEquals(CarrierTextManager.StatusMode.NetworkLocked, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_NETWORK_LOCKED)); + assertEquals(CarrierTextManager.StatusMode.SimNotReady, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_NOT_READY)); + assertEquals(CarrierTextManager.StatusMode.SimLocked, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_PIN_REQUIRED)); + assertEquals(CarrierTextManager.StatusMode.SimPukLocked, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_PUK_REQUIRED)); + assertEquals(CarrierTextManager.StatusMode.Normal, + mCarrierTextManager.getStatusForIccState(TelephonyManager.SIM_STATE_READY)); + assertEquals(CarrierTextManager.StatusMode.SimPermDisabled, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_PERM_DISABLED)); + assertEquals(CarrierTextManager.StatusMode.SimUnknown, + mCarrierTextManager.getStatusForIccState(TelephonyManager.SIM_STATE_UNKNOWN)); + assertEquals(CarrierTextManager.StatusMode.SimIoError, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_CARD_IO_ERROR)); + assertEquals(CarrierTextManager.StatusMode.SimRestricted, + mCarrierTextManager.getStatusForIccState( + TelephonyManager.SIM_STATE_CARD_RESTRICTED)); + } + private Context getContextSpyForStickyBroadcast(Intent returnVal) { Context contextSpy = spy(mContext); doReturn(returnVal).when(contextSpy).registerReceiver(eq(null), any(IntentFilter.class)); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index ae3a320cde54..fa32835c2695 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.KeyEvent; @@ -183,4 +184,15 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { mKeyguardAbsKeyInputViewController.onResume(KeyguardSecurityView.VIEW_REVEALED); verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt()); } + + @Test + public void testLockedOut_verifyPasswordAndUnlock_doesNotEnableViewInput() { + mKeyguardAbsKeyInputViewController.handleAttemptLockout( + SystemClock.elapsedRealtime() + 1000); + mKeyguardAbsKeyInputViewController.verifyPasswordAndUnlock(); + verify(mAbsKeyInputView).setPasswordEntryInputEnabled(false); + verify(mAbsKeyInputView).setPasswordEntryEnabled(false); + verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true); + verify(mAbsKeyInputView, never()).setPasswordEntryEnabled(true); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt index 71a57c78e226..2eea9eb76398 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt @@ -18,10 +18,12 @@ package com.android.keyguard import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils -import com.android.internal.widget.LockPatternView import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector @@ -29,6 +31,10 @@ import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.statusbar.policy.DevicePostureController +import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -45,7 +51,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class KeyguardPatternViewControllerTest : SysuiTestCase() { - @Mock private lateinit var mKeyguardPatternView: KeyguardPatternView + private lateinit var mKeyguardPatternView: KeyguardPatternView @Mock private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor @@ -63,54 +69,70 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() { @Mock private lateinit var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory - @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea - @Mock private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController<BouncerKeyguardMessageArea> - @Mock private lateinit var mLockPatternView: LockPatternView - - @Mock private lateinit var mPostureController: DevicePostureController + @Mock private lateinit var mPostureController: DevicePostureController private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController private lateinit var fakeFeatureFlags: FakeFeatureFlags - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true) - `when`( - mKeyguardPatternView.requireViewById<BouncerKeyguardMessageArea>( - R.id.bouncer_message_area)) - .thenReturn(mKeyguardMessageArea) - `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView)) - .thenReturn(mLockPatternView) - `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea)) - .thenReturn(mKeyguardMessageAreaController) - `when`(mKeyguardPatternView.resources).thenReturn(context.resources) - fakeFeatureFlags = FakeFeatureFlags() - fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false) - mKeyguardPatternViewController = - KeyguardPatternViewController( - mKeyguardPatternView, - mKeyguardUpdateMonitor, - mSecurityMode, - mLockPatternUtils, - mKeyguardSecurityCallback, - mLatencyTracker, - mFalsingCollector, - mEmergencyButtonController, - mKeyguardMessageAreaControllerFactory, - mPostureController, - fakeFeatureFlags) - } + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + whenever(mKeyguardMessageAreaControllerFactory.create(any())) + .thenReturn(mKeyguardMessageAreaController) + fakeFeatureFlags = FakeFeatureFlags() + fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false) + mKeyguardPatternView = View.inflate(mContext, R.layout.keyguard_pattern_view, null) + as KeyguardPatternView + + + mKeyguardPatternViewController = + KeyguardPatternViewController( + mKeyguardPatternView, + mKeyguardUpdateMonitor, + mSecurityMode, + mLockPatternUtils, + mKeyguardSecurityCallback, + mLatencyTracker, + mFalsingCollector, + mEmergencyButtonController, + mKeyguardMessageAreaControllerFactory, + mPostureController, + fakeFeatureFlags + ) + mKeyguardPatternView.onAttachedToWindow() + } + + @Test + fun tabletopPostureIsDetectedFromStart() { + overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f) + whenever(mPostureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED) + + mKeyguardPatternViewController.onViewAttached() + + assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline()) + } + + private fun getPatternTopGuideline(): Float { + val cs = ConstraintSet() + val container = + mKeyguardPatternView.findViewById(R.id.pattern_container) as ConstraintLayout + cs.clone(container) + return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent + } + + private fun getExpectedTopGuideline(): Float { + return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio) + } @Test fun withFeatureFlagOn_oldMessage_isHidden() { fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) - mKeyguardPatternViewController.init() + mKeyguardPatternViewController.onViewAttached() verify<KeyguardMessageAreaController<*>>(mKeyguardMessageAreaController).disable() } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 0edfd77f2703..b0576e02f998 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -169,6 +169,7 @@ import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -677,6 +678,130 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + public void testHandleSimStateChange_Unknown() { + Intent intent = new Intent(Intent.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_UNKNOWN); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, false)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_UNKNOWN, + mKeyguardUpdateMonitor.getCachedSimState()); + } + + @Test + public void testHandleSimStateChange_Absent() { + Intent intent = new Intent(Intent.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_ABSENT); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, false)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_ABSENT, + mKeyguardUpdateMonitor.getCachedSimState()); + } + + @Test + public void testHandleSimStateChange_CardIOError() { + Intent intent = new Intent(Intent.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_CARD_IO_ERROR); + intent.putExtra(Intent.EXTRA_SIM_LOCKED_REASON, Intent.SIM_STATE_CARD_IO_ERROR); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, false)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_CARD_IO_ERROR, + mKeyguardUpdateMonitor.getCachedSimState()); + } + + @Test + public void testHandleSimStateChange_CardRestricted() { + Intent intent = new Intent(Intent.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_CARD_RESTRICTED); + intent.putExtra(Intent.EXTRA_SIM_LOCKED_REASON, Intent.SIM_STATE_CARD_RESTRICTED); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, false)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_CARD_RESTRICTED, + mKeyguardUpdateMonitor.getCachedSimState()); + } + + @Test + public void testHandleSimStateChange_Locked() { + Intent intent = new Intent(Intent.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_LOCKED); + + // locked on PIN1 + intent.putExtra(Intent.EXTRA_SIM_LOCKED_REASON, Intent.SIM_LOCKED_ON_PIN); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, true)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_PIN_REQUIRED, + mKeyguardUpdateMonitor.getCachedSimState()); + + // locked on PUK1 + intent.putExtra(Intent.EXTRA_SIM_LOCKED_REASON, Intent.SIM_LOCKED_ON_PUK); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, true)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_PUK_REQUIRED, + mKeyguardUpdateMonitor.getCachedSimState()); + + // locked on network personalization + intent.putExtra(Intent.EXTRA_SIM_LOCKED_REASON, Intent.SIM_LOCKED_NETWORK); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, true)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_NETWORK_LOCKED, + mKeyguardUpdateMonitor.getCachedSimState()); + + // permanently disabled due to puk fails + intent.putExtra(Intent.EXTRA_SIM_LOCKED_REASON, Intent.SIM_ABSENT_ON_PERM_DISABLED); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, true)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_PERM_DISABLED, + mKeyguardUpdateMonitor.getCachedSimState()); + } + + @Test + public void testHandleSimStateChange_NotReady() { + Intent intent = new Intent(Intent.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_NOT_READY); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, false)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_NOT_READY, + mKeyguardUpdateMonitor.getCachedSimState()); + } + + @Test + public void testHandleSimStateChange_Ready() { + Intent intent = new Intent(Intent.ACTION_SIM_STATE_CHANGED); + + // ICC IMSI is ready in property + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_IMSI); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, false)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_READY, + mKeyguardUpdateMonitor.getCachedSimState()); + + // ICC is ready to access + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_READY); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, false)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_READY, + mKeyguardUpdateMonitor.getCachedSimState()); + + // all ICC records, including IMSI, are loaded + intent.putExtra(Intent.EXTRA_SIM_STATE, Intent.SIM_STATE_LOADED); + mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), + putPhoneInfo(intent, null, true)); + mTestableLooper.processAllMessages(); + Assert.assertEquals(TelephonyManager.SIM_STATE_READY, + mKeyguardUpdateMonitor.getCachedSimState()); + } + + @Test public void testTriesToAuthenticateFingerprint_whenKeyguard() { mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); mTestableLooper.processAllMessages(); @@ -3144,6 +3269,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private class TestableKeyguardUpdateMonitor extends KeyguardUpdateMonitor { AtomicBoolean mSimStateChanged = new AtomicBoolean(false); + AtomicInteger mCachedSimState = new AtomicInteger(-1); protected TestableKeyguardUpdateMonitor(Context context) { super(context, mUserTracker, @@ -3166,9 +3292,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { return mSimStateChanged.getAndSet(false); } + public int getCachedSimState() { + return mCachedSimState.getAndSet(-1); + } + @Override protected void handleSimStateChange(int subId, int slotId, int state) { mSimStateChanged.set(true); + mCachedSimState.set(state); super.handleSimStateChange(subId, slotId, state); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java deleted file mode 100644 index ef3af8a3c9bc..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.res.Resources; -import android.graphics.Color; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class AnalogClockControllerTest extends SysuiTestCase { - - private AnalogClockController mClockController; - @Mock SysuiColorExtractor mMockColorExtractor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - Resources res = getContext().getResources(); - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - mClockController = new AnalogClockController(res, layoutInflater, - mMockColorExtractor); - } - - @Test - public void setDarkAmount_AOD() { - ViewGroup smallClockFrame = (ViewGroup) mClockController.getView(); - View smallClock = smallClockFrame.getChildAt(0); - // WHEN dark amount is set to AOD - mClockController.setDarkAmount(1f); - // THEN small clock should be shown. - assertThat(smallClock.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setColorPalette_setDigitalClock() { - ViewGroup smallClock = (ViewGroup) mClockController.getView(); - // WHEN color palette is set - mClockController.setColorPalette(true, new int[]{Color.RED}); - // THEN child of small clock should have text color set. - TextView digitalClock = (TextView) smallClock.getChildAt(0); - assertThat(digitalClock.getCurrentTextColor()).isEqualTo(Color.RED); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java deleted file mode 100644 index b56986eb80d0..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.res.Resources; -import android.graphics.Color; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class BubbleClockControllerTest extends SysuiTestCase { - - private BubbleClockController mClockController; - @Mock SysuiColorExtractor mMockColorExtractor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - Resources res = getContext().getResources(); - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - mClockController = new BubbleClockController(res, layoutInflater, mMockColorExtractor); - } - - @Test - public void setDarkAmount_AOD() { - ViewGroup smallClockFrame = (ViewGroup) mClockController.getView(); - View smallClock = smallClockFrame.getChildAt(0); - // WHEN dark amount is set to AOD - mClockController.setDarkAmount(1f); - // THEN small clock should not be shown. - assertThat(smallClock.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setColorPalette_setDigitalClock() { - ViewGroup smallClock = (ViewGroup) mClockController.getView(); - // WHEN text color is set - mClockController.setColorPalette(true, new int[]{Color.RED}); - // THEN child of small clock should have text color set. - TextView digitalClock = (TextView) smallClock.getChildAt(0); - assertThat(digitalClock.getCurrentTextColor()).isEqualTo(Color.RED); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java deleted file mode 100644 index 4c0890a73853..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.verify; - -import android.graphics.Bitmap; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.function.Supplier; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class ClockInfoTest extends SysuiTestCase { - - @Mock - private Supplier<Bitmap> mMockSupplier; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testGetName() { - final String name = "name"; - ClockInfo info = ClockInfo.builder().setName(name).build(); - assertThat(info.getName()).isEqualTo(name); - } - - @Test - public void testGetTitle() { - final String title = "title"; - ClockInfo info = ClockInfo.builder().setTitle(() -> title).build(); - assertThat(info.getTitle()).isEqualTo(title); - } - - @Test - public void testGetId() { - final String id = "id"; - ClockInfo info = ClockInfo.builder().setId(id).build(); - assertThat(info.getId()).isEqualTo(id); - } - - @Test - public void testGetThumbnail() { - ClockInfo info = ClockInfo.builder().setThumbnail(mMockSupplier).build(); - info.getThumbnail(); - verify(mMockSupplier).get(); - } - - @Test - public void testGetPreview() { - ClockInfo info = ClockInfo.builder().setPreview(mMockSupplier).build(); - info.getPreview(); - verify(mMockSupplier).get(); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java deleted file mode 100644 index 7a5b772e2f1b..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ContentResolver; -import android.database.ContentObserver; -import android.net.Uri; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManagerFake; -import com.android.systemui.plugins.ClockPlugin; -import com.android.systemui.plugins.PluginManager; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Arrays; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -// Need to run tests on main looper to allow for onClockChanged operation to happen synchronously. -@RunWithLooper(setAsMainLooper = true) -public final class ClockManagerTest extends SysuiTestCase { - - private static final String BUBBLE_CLOCK = BubbleClockController.class.getName(); - private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class; - private static final int MAIN_USER_ID = 0; - private static final int SECONDARY_USER_ID = 11; - private static final Uri SETTINGS_URI = null; - - private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); - private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); - private ClockManager mClockManager; - private ContentObserver mContentObserver; - private DockManagerFake mFakeDockManager; - private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallbackCaptor; - @Mock PluginManager mMockPluginManager; - @Mock SysuiColorExtractor mMockColorExtractor; - @Mock ContentResolver mMockContentResolver; - @Mock UserTracker mUserTracker; - @Mock SettingsWrapper mMockSettingsWrapper; - @Mock ClockManager.ClockChangedListener mMockListener1; - @Mock ClockManager.ClockChangedListener mMockListener2; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - LayoutInflater inflater = LayoutInflater.from(getContext()); - - mFakeDockManager = new DockManagerFake(); - - when(mUserTracker.getUserId()).thenReturn(MAIN_USER_ID); - mUserTrackerCallbackCaptor = ArgumentCaptor.forClass(UserTracker.Callback.class); - - mClockManager = new ClockManager(getContext(), inflater, - mMockPluginManager, mMockColorExtractor, mMockContentResolver, - mUserTracker, mMainExecutor, mMockSettingsWrapper, mFakeDockManager); - - mClockManager.addBuiltinClock(() -> new BubbleClockController( - getContext().getResources(), inflater, mMockColorExtractor)); - mClockManager.addOnClockChangedListener(mMockListener1); - mClockManager.addOnClockChangedListener(mMockListener2); - verify(mUserTracker).addCallback(mUserTrackerCallbackCaptor.capture(), any()); - reset(mMockListener1, mMockListener2); - - mContentObserver = mClockManager.getContentObserver(); - } - - @After - public void tearDown() { - mClockManager.removeOnClockChangedListener(mMockListener1); - mClockManager.removeOnClockChangedListener(mMockListener2); - } - - @Test - public void dockEvent() { - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - assertThat(mClockManager.isDocked()).isTrue(); - } - - @Test - public void undockEvent() { - mFakeDockManager.setDockEvent(DockManager.STATE_NONE); - assertThat(mClockManager.isDocked()).isFalse(); - } - - @Test - public void getCurrentClock_default() { - // GIVEN that settings doesn't contain any values - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null); - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the result is null, indicated the default clock face should be used. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_customClock() { - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onClockChanged_customClock() { - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the plugin is the bubble clock face. - ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); - verify(mMockListener1).onClockChanged(captor.capture()); - assertThat(captor.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onClockChanged_uniqueInstances() { - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the listeners receive separate instances of the Bubble clock plugin. - ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); - ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); - verify(mMockListener1).onClockChanged(captor1.capture()); - verify(mMockListener2).onClockChanged(captor2.capture()); - assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); - assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); - assertThat(captor1.getValue()).isNotSameInstanceAs(captor2.getValue()); - } - - @Test - public void getCurrentClock_badSettingsValue() { - // GIVEN that settings contains a value that doesn't correspond to a - // custom clock face. - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value"); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the result is null. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_dockedDefault() { - // WHEN dock event is fired - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the result is null, indicating the default clock face. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_dockedCustomClock() { - // GIVEN settings is set to the bubble clock face - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN dock event fires - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void getCurrentClock_badDockedSettingsValue() { - // GIVEN settings contains a value that doesn't correspond to an available clock face. - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); - // WHEN dock event fires - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the result is null. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_badDockedSettingsFallback() { - // GIVEN settings contains a value that doesn't correspond to an available clock face, but - // locked screen settings is set to bubble clock. - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN dock event is fired - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onUserChanged_defaultClock() { - // WHEN the user changes - switchUser(SECONDARY_USER_ID); - // THEN the plugin is null for the default clock face - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void onUserChanged_customClock() { - // GIVEN that a second user has selected the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(SECONDARY_USER_ID)).thenReturn( - BUBBLE_CLOCK); - // WHEN the user changes - switchUser(SECONDARY_USER_ID); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onUserChanged_docked() { - // GIVEN device is docked - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // AND the second user as selected the bubble clock for the dock - when(mMockSettingsWrapper.getDockedClockFace(SECONDARY_USER_ID)).thenReturn(BUBBLE_CLOCK); - // WHEN the user changes - switchUser(SECONDARY_USER_ID); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - private void switchUser(int newUser) { - when(mUserTracker.getUserId()).thenReturn(newUser); - mUserTrackerCallbackCaptor.getValue().onUserChanged(newUser, mContext); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java deleted file mode 100644 index d2832fb98460..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.verify; - -import android.database.Cursor; -import android.graphics.Bitmap; -import android.net.Uri; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class ClockOptionsProviderTest extends SysuiTestCase { - - private static final String CONTENT_SCHEME = "content"; - private static final String AUTHORITY = "com.android.keyguard.clock"; - private static final String LIST_OPTIONS = "list_options"; - private static final String PREVIEW = "preview"; - private static final String THUMBNAIL = "thumbnail"; - private static final String MIME_TYPE_LIST_OPTIONS = "vnd.android.cursor.dir/clock_faces"; - private static final String MIME_TYPE_PNG = "image/png"; - private static final String NAME_COLUMN = "name"; - private static final String TITLE_COLUMN = "title"; - private static final String ID_COLUMN = "id"; - private static final String PREVIEW_COLUMN = "preview"; - private static final String THUMBNAIL_COLUMN = "thumbnail"; - - private ClockOptionsProvider mProvider; - private Supplier<List<ClockInfo>> mMockSupplier; - private List<ClockInfo> mClocks; - private Uri mListOptionsUri; - @Mock - private Supplier<Bitmap> mMockBitmapSupplier; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mClocks = new ArrayList<>(); - mProvider = new ClockOptionsProvider(() -> mClocks); - mListOptionsUri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(LIST_OPTIONS) - .build(); - } - - @Test - public void testGetType_listOptions() { - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(LIST_OPTIONS) - .build(); - assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_LIST_OPTIONS); - } - - @Test - public void testGetType_preview() { - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(PREVIEW) - .appendPath("id") - .build(); - assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG); - } - - @Test - public void testGetType_thumbnail() { - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(THUMBNAIL) - .appendPath("id") - .build(); - assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG); - } - - @Test - public void testQuery_noClocks() { - Cursor cursor = mProvider.query(mListOptionsUri, null, null, null); - assertThat(cursor.getCount()).isEqualTo(0); - } - - @Test - public void testQuery_listOptions() { - mClocks.add(ClockInfo.builder() - .setName("name_a") - .setTitle(() -> "title_a") - .setId("id_a") - .build()); - mClocks.add(ClockInfo.builder() - .setName("name_b") - .setTitle(() -> "title_b") - .setId("id_b") - .build()); - Cursor cursor = mProvider.query(mListOptionsUri, null, null, null); - assertThat(cursor.getCount()).isEqualTo(2); - cursor.moveToFirst(); - assertThat(cursor.getString( - cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(PREVIEW_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/preview/id_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(THUMBNAIL_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_a"); - cursor.moveToNext(); - assertThat(cursor.getString( - cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(PREVIEW_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/preview/id_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(THUMBNAIL_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_b"); - } - - @Test - public void testOpenFile_preview() throws Exception { - mClocks.add(ClockInfo.builder() - .setId("id") - .setPreview(mMockBitmapSupplier) - .build()); - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(PREVIEW) - .appendPath("id") - .build(); - mProvider.openFile(uri, "r").close(); - verify(mMockBitmapSupplier).get(); - } - - @Test - public void testOpenFile_thumbnail() throws Exception { - mClocks.add(ClockInfo.builder() - .setId("id") - .setThumbnail(mMockBitmapSupplier) - .build()); - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(THUMBNAIL) - .appendPath("id") - .build(); - mProvider.openFile(uri, "r").close(); - verify(mMockBitmapSupplier).get(); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt deleted file mode 100644 index 347b26deacd4..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.graphics.Color -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class ClockPaletteTest : SysuiTestCase() { - - private lateinit var clockPalette: ClockPalette - private lateinit var colors: IntArray - - @Before - fun setUp() { - clockPalette = ClockPalette() - // colors used are reds from light to dark. - val hsv: FloatArray = FloatArray(3) - Color.colorToHSV(Color.RED, hsv) - colors = IntArray(10) - val step: Float = (0f - hsv[2]) / colors.size - for (i in 0 until colors.size) { - hsv[2] += step - colors[i] = Color.HSVToColor(hsv) - } - } - - @Test - fun testDark() { - // GIVEN on AOD - clockPalette.setDarkAmount(1f) - // AND GIVEN that wallpaper doesn't support dark text - clockPalette.setColorPalette(false, colors) - // THEN the secondary color should be lighter than the primary color - assertThat(value(clockPalette.getPrimaryColor())) - .isGreaterThan(value(clockPalette.getSecondaryColor())) - } - - @Test - fun testDarkText() { - // GIVEN on lock screen - clockPalette.setDarkAmount(0f) - // AND GIVEN that wallpaper supports dark text - clockPalette.setColorPalette(true, colors) - // THEN the secondary color should be darker the primary color - assertThat(value(clockPalette.getPrimaryColor())) - .isLessThan(value(clockPalette.getSecondaryColor())) - } - - @Test - fun testLightText() { - // GIVEN on lock screen - clockPalette.setDarkAmount(0f) - // AND GIVEN that wallpaper doesn't support dark text - clockPalette.setColorPalette(false, colors) - // THEN the secondary color should be darker than the primary color - assertThat(value(clockPalette.getPrimaryColor())) - .isGreaterThan(value(clockPalette.getSecondaryColor())) - } - - @Test - fun testNullColors() { - // GIVEN on AOD - clockPalette.setDarkAmount(1f) - // AND GIVEN that wallpaper colors are null - clockPalette.setColorPalette(false, null) - // THEN the primary color should be whilte - assertThat(clockPalette.getPrimaryColor()).isEqualTo(Color.WHITE) - } - - private fun value(color: Int): Float { - val hsv: FloatArray = FloatArray(3) - Color.colorToHSV(color, hsv) - return hsv[2] - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java deleted file mode 100644 index fd7657ff18cc..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.View; -import android.widget.TextView; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class CrossFadeDarkControllerTest extends SysuiTestCase { - - private View mViewFadeIn; - private View mViewFadeOut; - private CrossFadeDarkController mDarkController; - - @Before - public void setUp() { - mViewFadeIn = new TextView(getContext()); - mViewFadeIn.setVisibility(View.VISIBLE); - mViewFadeIn.setAlpha(1f); - mViewFadeOut = new TextView(getContext()); - mViewFadeOut.setVisibility(View.VISIBLE); - mViewFadeOut.setAlpha(1f); - - mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut); - } - - @Test - public void setDarkAmount_fadeIn() { - // WHEN dark amount corresponds to AOD - mDarkController.setDarkAmount(1f); - // THEN fade in view should be faded in and fade out view faded out. - assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f); - assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f); - assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE); - } - - @Test - public void setDarkAmount_fadeOut() { - // WHEN dark amount corresponds to lock screen - mDarkController.setDarkAmount(0f); - // THEN fade out view should bed faded out and fade in view faded in. - assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f); - assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE); - assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f); - assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setDarkAmount_partialFadeIn() { - // WHEN dark amount corresponds to a partial transition - mDarkController.setDarkAmount(0.9f); - // THEN views should have intermediate alpha value. - assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f); - assertThat(mViewFadeIn.getAlpha()).isLessThan(1f); - assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setDarkAmount_partialFadeOut() { - // WHEN dark amount corresponds to a partial transition - mDarkController.setDarkAmount(0.1f); - // THEN views should have intermediate alpha value. - assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f); - assertThat(mViewFadeOut.getAlpha()).isLessThan(1f); - assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/SettingsWrapperTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/SettingsWrapperTest.kt deleted file mode 100644 index 573581dae3b1..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/SettingsWrapperTest.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.json.JSONObject -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify - -private const val PACKAGE = "com.android.keyguard.clock.Clock" -private const val CLOCK_FIELD = "clock" -private const val TIMESTAMP_FIELD = "_applied_timestamp" -private const val USER_ID = 0 - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class SettingsWrapperTest : SysuiTestCase() { - - private lateinit var wrapper: SettingsWrapper - private lateinit var migration: SettingsWrapper.Migration - - @Before - fun setUp() { - migration = mock(SettingsWrapper.Migration::class.java) - wrapper = SettingsWrapper(getContext().contentResolver, migration) - } - - @Test - fun testDecodeUnnecessary() { - // GIVEN a settings value that doesn't need to be decoded - val value = PACKAGE - // WHEN the value is decoded - val decoded = wrapper.decode(value, USER_ID) - // THEN the same value is returned, because decoding isn't necessary. - // TODO(b/135674383): Null should be returned when the migration code in removed. - assertThat(decoded).isEqualTo(value) - // AND the value is migrated to JSON format - verify(migration).migrate(value, USER_ID) - } - - @Test - fun testDecodeJSON() { - // GIVEN a settings value that is encoded in JSON - val json: JSONObject = JSONObject() - json.put(CLOCK_FIELD, PACKAGE) - json.put(TIMESTAMP_FIELD, System.currentTimeMillis()) - val value = json.toString() - // WHEN the value is decoded - val decoded = wrapper.decode(value, USER_ID) - // THEN the clock field should have been extracted - assertThat(decoded).isEqualTo(PACKAGE) - } - - @Test - fun testDecodeJSONWithoutClockField() { - // GIVEN a settings value that doesn't contain the CLOCK_FIELD - val json: JSONObject = JSONObject() - json.put(TIMESTAMP_FIELD, System.currentTimeMillis()) - val value = json.toString() - // WHEN the value is decoded - val decoded = wrapper.decode(value, USER_ID) - // THEN null is returned - assertThat(decoded).isNull() - // AND the value is not migrated to JSON format - verify(migration, never()).migrate(value, USER_ID) - } - - @Test - fun testDecodeNullJSON() { - assertThat(wrapper.decode(null, USER_ID)).isNull() - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt deleted file mode 100644 index 3a27e356ed84..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class SmallClockPositionTest : SysuiTestCase() { - - private val statusBarHeight = 100 - private val lockPadding = 15 - private val lockHeight = 35 - private val burnInY = 20 - - private lateinit var position: SmallClockPosition - - @Before - fun setUp() { - position = SmallClockPosition(statusBarHeight, lockPadding, lockHeight, burnInY) - } - - @Test - fun loadResources() { - // Cover constructor taking Resources object. - position = SmallClockPosition(context) - position.setDarkAmount(1f) - assertThat(position.preferredY).isGreaterThan(0) - } - - @Test - fun darkPosition() { - // GIVEN on AOD - position.setDarkAmount(1f) - // THEN Y is sum of statusBarHeight, lockPadding, lockHeight, lockPadding, burnInY - assertThat(position.preferredY).isEqualTo(185) - } - - @Test - fun lockPosition() { - // GIVEN on lock screen - position.setDarkAmount(0f) - // THEN Y position is statusBarHeight + lockPadding + lockHeight + lockPadding - // (100 + 15 + 35 + 15 = 165) - assertThat(position.preferredY).isEqualTo(165) - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt deleted file mode 100644 index 5ece6ef1a794..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard.clock - -import android.content.Context -import com.google.common.truth.Truth.assertThat - -import android.graphics.Canvas -import android.graphics.Color -import android.testing.AndroidTestingRunner -import android.view.View -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class ViewPreviewerTest : SysuiTestCase() { - - private lateinit var previewer: ViewPreviewer - private lateinit var view: View - - @Before - fun setUp() { - previewer = ViewPreviewer() - view = TestView(context) - } - - @Test - fun testCreatePreview() { - val width = 100 - val height = 100 - // WHEN a preview image is created - val bitmap = previewer.createPreview(view, width, height)!! - // THEN the bitmap has the expected width and height - assertThat(bitmap.height).isEqualTo(height) - assertThat(bitmap.width).isEqualTo(width) - assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.RED) - } - - class TestView(context: Context) : View(context) { - override fun onDraw(canvas: Canvas?) { - super.onDraw(canvas) - canvas?.drawColor(Color.RED) - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt index 8207fa6958f3..d500b5aebcbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt @@ -74,7 +74,8 @@ class ScreenDecorHwcLayerTest : SysuiTestCase() { val decorationSupport = DisplayDecorationSupport() decorationSupport.format = PixelFormat.R_8 - decorHwcLayer = Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport)) + decorHwcLayer = + Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport, /* debug */ false)) whenever(decorHwcLayer.width).thenReturn(displayWidth) whenever(decorHwcLayer.height).thenReturn(displayHeight) whenever(decorHwcLayer.context).thenReturn(mockContext) diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 4cf5a4be0b60..79c87cfd1f3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -43,7 +43,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; 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; @@ -63,6 +62,7 @@ import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.util.PathParser; import android.util.Size; import android.view.Display; import android.view.DisplayCutout; @@ -83,6 +83,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.decor.CornerDecorProvider; import com.android.systemui.decor.CutoutDecorProviderFactory; import com.android.systemui.decor.CutoutDecorProviderImpl; +import com.android.systemui.decor.DebugRoundedCornerModel; import com.android.systemui.decor.DecorProvider; import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.FaceScanningOverlayProviderImpl; @@ -96,7 +97,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.events.PrivacyDotViewController; -import com.android.systemui.tuner.TunerService; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeThreadFactory; import com.android.systemui.util.settings.FakeSettings; @@ -139,8 +139,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { @Mock private Display mDisplay; @Mock - private TunerService mTunerService; - @Mock private UserTracker mUserTracker; @Mock private PrivacyDotViewController mDotViewController; @@ -234,7 +232,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")))); mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings, - mTunerService, mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory, + mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory, new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController) { @@ -251,12 +249,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { } @Override - public void onTuningChanged(String key, String newValue) { - super.onTuningChanged(key, newValue); - mExecutor.runAllReady(); - } - - @Override protected void updateOverlayWindowVisibilityIfViewExists(@Nullable View view) { super.updateOverlayWindowVisibilityIfViewExists(view); mExecutor.runAllReady(); @@ -268,9 +260,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { } }); mScreenDecorations.mDisplayInfo = mDisplayInfo; + // Make sure tests are never run starting in debug mode + mScreenDecorations.setDebug(false); doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio(); doNothing().when(mScreenDecorations).updateOverlayProviderViews(any()); - reset(mTunerService); try { mPrivacyDotShowingListener = mScreenDecorations.mPrivacyDotShowingListener.getClass() @@ -464,8 +457,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // No views added. verifyOverlaysExistAndAdded(false, false, false, false, null); - // No Tuners tuned. - verify(mTunerService, never()).addTunable(any(), any()); // No dot controller init verify(mDotViewController, never()).initialize(any(), any(), any(), any()); } @@ -497,8 +488,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Face scanning doesn't exist verifyFaceScanningViewExists(false); - // One tunable. - verify(mTunerService, times(1)).addTunable(any(), any()); // Dot controller init verify(mDotViewController, times(1)).initialize( isA(View.class), isA(View.class), isA(View.class), isA(View.class)); @@ -528,8 +517,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Face scanning doesn't exist verifyFaceScanningViewExists(false); - // One tunable. - verify(mTunerService, times(1)).addTunable(any(), any()); // No dot controller init verify(mDotViewController, never()).initialize(any(), any(), any(), any()); } @@ -560,8 +547,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Face scanning doesn't exist verifyFaceScanningViewExists(false); - // One tunable. - verify(mTunerService, times(1)).addTunable(any(), any()); // Dot controller init verify(mDotViewController, times(1)).initialize( isA(View.class), isA(View.class), isA(View.class), isA(View.class)); @@ -1073,18 +1058,89 @@ public class ScreenDecorationsTest extends SysuiTestCase { } @Test + public void testDebugRoundedCorners_noDeviceCornersSet() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + null /* roundedTopDrawable */, null /* roundedBottomDrawable */, + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); + + mScreenDecorations.start(); + // No rounded corners exist at this point + verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE); + + // Path from rounded.xml, scaled by 10x to produce 80x80 corners + Path debugPath = PathParser.createPathFromPathData("M8,0H0v8C0,3.6,3.6,0,8,0z"); + // WHEN debug corners are added to the delegate + DebugRoundedCornerModel debugCorner = new DebugRoundedCornerModel( + debugPath, + 80, + 80, + 10f, + 10f + ); + mScreenDecorations.mDebugRoundedCornerDelegate + .applyNewDebugCorners(debugCorner, debugCorner); + + // AND debug mode is entered + mScreenDecorations.setDebug(true); + mExecutor.runAllReady(); + + // THEN the debug corners provide decor + List<DecorProvider> providers = mScreenDecorations.getProviders(false); + assertEquals(4, providers.size()); + + // Top and bottom overlays contain the debug rounded corners + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + } + + @Test + public void testDebugRoundedCornersRemoved_noDeviceCornersSet() { + // GIVEN a device with no rounded corners defined + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + null /* roundedTopDrawable */, null /* roundedBottomDrawable */, + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); + + mScreenDecorations.start(); + // No rounded corners exist at this point + verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE); + + // Path from rounded.xml, scaled by 10x to produce 80x80 corners + Path debugPath = PathParser.createPathFromPathData("M8,0H0v8C0,3.6,3.6,0,8,0z"); + // WHEN debug corners are added to the delegate + DebugRoundedCornerModel debugCorner = new DebugRoundedCornerModel( + debugPath, + 80, + 80, + 10f, + 10f + ); + mScreenDecorations.mDebugRoundedCornerDelegate + .applyNewDebugCorners(debugCorner, debugCorner); + + // AND debug mode is entered + mScreenDecorations.setDebug(true); + mExecutor.runAllReady(); + + // Top and bottom overlays contain the debug rounded corners + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + + // WHEN debug is exited + mScreenDecorations.setDebug(false); + mExecutor.runAllReady(); + + // THEN the decor is removed + verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE); + assertThat(mScreenDecorations.mDebugRoundedCornerDelegate.getHasBottom()).isFalse(); + assertThat(mScreenDecorations.mDebugRoundedCornerDelegate.getHasTop()).isFalse(); + } + + @Test public void testRegistration_From_NoOverlay_To_HasOverlays() { doReturn(false).when(mScreenDecorations).hasOverlays(); mScreenDecorations.start(); - verify(mTunerService, times(0)).addTunable(any(), any()); - verify(mTunerService, times(1)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(false)); - reset(mTunerService); doReturn(true).when(mScreenDecorations).hasOverlays(); mScreenDecorations.onConfigurationChanged(new Configuration()); - verify(mTunerService, times(1)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); } @@ -1093,14 +1149,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { doReturn(true).when(mScreenDecorations).hasOverlays(); mScreenDecorations.start(); - verify(mTunerService, times(1)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); - reset(mTunerService); mScreenDecorations.onConfigurationChanged(new Configuration()); - verify(mTunerService, times(0)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); } @@ -1109,15 +1160,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { doReturn(true).when(mScreenDecorations).hasOverlays(); mScreenDecorations.start(); - verify(mTunerService, times(1)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); - reset(mTunerService); doReturn(false).when(mScreenDecorations).hasOverlays(); mScreenDecorations.onConfigurationChanged(new Configuration()); - verify(mTunerService, times(0)).addTunable(any(), any()); - verify(mTunerService, times(1)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(false)); } @@ -1181,7 +1227,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders); when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(true); ScreenDecorations screenDecorations = new ScreenDecorations(mContext, mExecutor, - mSecureSettings, mTunerService, mUserTracker, mDisplayTracker, mDotViewController, + mSecureSettings, mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory, new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController); screenDecorations.start(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index 3a93e7744d00..ac2d492f3320 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -337,6 +337,110 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test + fun tryAutoConfirm_withAutoConfirmPinAndEmptyInput_returnsNullAndHasNoEffect() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test + fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 3), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test + fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test + fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 3, 4, 5), tryAutoConfirm = true)) + .isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test + fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test + fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test + fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password")) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test fun unlocksDevice_whenAuthMethodBecomesNone() = testScope.runTest { val isUnlocked by collectLastValue(underTest.isUnlocked) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index b4a4a11a81a1..5cae23ce783a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -492,6 +492,50 @@ public class AuthControllerTest extends SysuiTestCase { } @Test + public void testOnAuthenticationFailedInvoked_whenFaceAuthRejected() throws RemoteException { + final int modality = BiometricAuthenticator.TYPE_FACE; + final int userId = 0; + + enrollFingerprintAndFace(userId); + + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + + mAuthController.onBiometricError(modality, + BiometricConstants.BIOMETRIC_PAUSED_REJECTED, + 0 /* vendorCode */); + + ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class); + verify(mDialog1).onAuthenticationFailed(modalityCaptor.capture(), messageCaptor.capture()); + + assertEquals(modalityCaptor.getValue().intValue(), modality); + assertEquals(messageCaptor.getValue(), + mContext.getString(R.string.biometric_face_not_recognized)); + } + + @Test + public void testOnAuthenticationFailedInvoked_whenFingerprintAuthRejected() { + final int modality = BiometricAuthenticator.TYPE_FINGERPRINT; + final int userId = 0; + + enrollFingerprintAndFace(userId); + + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + + mAuthController.onBiometricError(modality, + BiometricConstants.BIOMETRIC_PAUSED_REJECTED, + 0 /* vendorCode */); + + ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class); + verify(mDialog1).onAuthenticationFailed(modalityCaptor.capture(), messageCaptor.capture()); + + assertEquals(modalityCaptor.getValue().intValue(), modality); + assertEquals(messageCaptor.getValue(), + mContext.getString(R.string.fingerprint_error_not_match)); + } + + @Test public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); final int modality = BiometricAuthenticator.TYPE_FACE; @@ -1017,6 +1061,31 @@ public class AuthControllerTest extends SysuiTestCase { return HAT; } + private void enrollFingerprintAndFace(final int userId) { + + // Enroll fingerprint + verify(mFingerprintManager).registerBiometricStateListener( + mBiometricStateCaptor.capture()); + assertFalse(mAuthController.isFingerprintEnrolled(userId)); + + mBiometricStateCaptor.getValue().onEnrollmentsChanged(userId, + 1 /* sensorId */, true /* hasEnrollments */); + waitForIdleSync(); + + assertTrue(mAuthController.isFingerprintEnrolled(userId)); + + // Enroll face + verify(mFaceManager).registerBiometricStateListener( + mBiometricStateCaptor.capture()); + assertFalse(mAuthController.isFaceAuthEnrolled(userId)); + + mBiometricStateCaptor.getValue().onEnrollmentsChanged(userId, + 2 /* sensorId */, true /* hasEnrollments */); + waitForIdleSync(); + + assertTrue(mAuthController.isFaceAuthEnrolled(userId)); + } + private final class TestableAuthController extends AuthController { private int mBuildCount = 0; private PromptInfo mLastBiometricPromptInfo; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt index ef750be90b4b..9cabd35cb1e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt @@ -20,12 +20,12 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shade.ShadeExpansionStateManager +import org.junit.Assert 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.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions import org.mockito.junit.MockitoJUnit @@ -63,10 +63,10 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { } @Test - fun testEnableDetector_expandOnly_shouldPostRunnable() { + fun testEnableDetector_expandOnly_shouldNotPostRunnable() { detector.enable(action) shadeExpansionStateManager.onPanelExpansionChanged(1.0f, true, false, 0f) - verify(action).run() + verifyZeroInteractions(action) } @Test @@ -84,4 +84,14 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() { shadeExpansionStateManager.onPanelExpansionChanged(1.0f, true, true, 0f) verifyZeroInteractions(action) } + + @Test + fun testFromOpenState_becomeStateClose_enableDetector_shouldNotPostRunnable() { + // STATE_OPEN is 2 + shadeExpansionStateManager.updateState(2) + detector.enable(action) + shadeExpansionStateManager.onPanelExpansionChanged(0.5f, false, false, 0f) + verifyZeroInteractions(action) + Assert.assertEquals(true, shadeExpansionStateManager.isClosed()) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java index 38c9caf085e2..9cb3b1aa9a55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java @@ -30,7 +30,11 @@ import android.app.Notification; import android.app.NotificationManager; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricSourceType; +import android.hardware.biometrics.BiometricStateListener; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Handler; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -69,6 +73,10 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { Optional<FingerprintReEnrollNotification> mFingerprintReEnrollNotificationOptional; @Mock FingerprintReEnrollNotification mFingerprintReEnrollNotification; + @Mock + FingerprintManager mFingerprintManager; + @Mock + FaceManager mFaceManager; private static final String TAG = "BiometricNotificationService"; private static final int FACE_NOTIFICATION_ID = 1; @@ -81,6 +89,8 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { private TestableLooper mLooper; private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; private KeyguardStateController.Callback mKeyguardStateControllerCallback; + private BiometricStateListener mFaceStateListener; + private BiometricStateListener mFingerprintStateListener; @Before public void setUp() { @@ -99,25 +109,37 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { mKeyguardUpdateMonitor, mKeyguardStateController, handler, mNotificationManager, broadcastReceiver, - mFingerprintReEnrollNotificationOptional); + mFingerprintReEnrollNotificationOptional, + mFingerprintManager, + mFaceManager); biometricNotificationService.start(); ArgumentCaptor<KeyguardUpdateMonitorCallback> updateMonitorCallbackArgumentCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); ArgumentCaptor<KeyguardStateController.Callback> stateControllerCallbackArgumentCaptor = ArgumentCaptor.forClass(KeyguardStateController.Callback.class); + ArgumentCaptor<BiometricStateListener> faceStateListenerArgumentCaptor = + ArgumentCaptor.forClass(BiometricStateListener.class); + ArgumentCaptor<BiometricStateListener> fingerprintStateListenerArgumentCaptor = + ArgumentCaptor.forClass(BiometricStateListener.class); verify(mKeyguardUpdateMonitor).registerCallback( updateMonitorCallbackArgumentCaptor.capture()); verify(mKeyguardStateController).addCallback( stateControllerCallbackArgumentCaptor.capture()); + verify(mFaceManager).registerBiometricStateListener( + faceStateListenerArgumentCaptor.capture()); + verify(mFingerprintManager).registerBiometricStateListener( + fingerprintStateListenerArgumentCaptor.capture()); + mFaceStateListener = faceStateListenerArgumentCaptor.getValue(); + mFingerprintStateListener = fingerprintStateListenerArgumentCaptor.getValue(); mKeyguardUpdateMonitorCallback = updateMonitorCallbackArgumentCaptor.getValue(); mKeyguardStateControllerCallback = stateControllerCallbackArgumentCaptor.getValue(); } @Test - public void testShowFingerprintReEnrollNotification() { + public void testShowFingerprintReEnrollNotification_onAcquiredReEnroll() { when(mKeyguardStateController.isShowing()).thenReturn(false); mKeyguardUpdateMonitorCallback.onBiometricHelp( @@ -139,7 +161,7 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { .isEqualTo(ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG); } @Test - public void testShowFaceReEnrollNotification() { + public void testShowFaceReEnrollNotification_onErrorReEnroll() { when(mKeyguardStateController.isShowing()).thenReturn(false); mKeyguardUpdateMonitorCallback.onBiometricError( @@ -161,4 +183,52 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { .isEqualTo(ACTION_SHOW_FACE_REENROLL_DIALOG); } + @Test + public void testCancelReEnrollmentNotification_onFaceEnrollmentStateChange() { + when(mKeyguardStateController.isShowing()).thenReturn(false); + + mKeyguardUpdateMonitorCallback.onBiometricError( + BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL, + "Testing Face Re-enrollment" /* errString */, + BiometricSourceType.FACE + ); + mKeyguardStateControllerCallback.onKeyguardShowingChanged(); + + mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); + mLooper.processAllMessages(); + + verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), + mNotificationArgumentCaptor.capture(), any()); + + mFaceStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */, + false /* hasEnrollments */); + + verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), + eq(UserHandle.CURRENT)); + } + + @Test + public void testCancelReEnrollmentNotification_onFingerprintEnrollmentStateChange() { + when(mKeyguardStateController.isShowing()).thenReturn(false); + + mKeyguardUpdateMonitorCallback.onBiometricHelp( + FINGERPRINT_ACQUIRED_RE_ENROLL, + "Testing Fingerprint Re-enrollment" /* errString */, + BiometricSourceType.FINGERPRINT + ); + mKeyguardStateControllerCallback.onKeyguardShowingChanged(); + + mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); + mLooper.processAllMessages(); + + verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID), + mNotificationArgumentCaptor.capture(), any()); + + mFingerprintStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */, + false /* hasEnrollments */); + + verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID), + eq(UserHandle.CURRENT)); + } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt new file mode 100644 index 000000000000..0df4fbf86d98 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt @@ -0,0 +1,124 @@ +/* + * 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.data.repository + +import android.database.ContentObserver +import android.os.Handler +import android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.mockito.captureMany +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.settings.SecureSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +private const val USER_ID = 8 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class FaceSettingsRepositoryImplTest : SysuiTestCase() { + + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + private val testScope = TestScope() + + @Mock private lateinit var mainHandler: Handler + @Mock private lateinit var secureSettings: SecureSettings + + private lateinit var repository: FaceSettingsRepositoryImpl + + @Before + fun setup() { + repository = FaceSettingsRepositoryImpl(mainHandler, secureSettings) + } + + @Test + fun createsOneRepositoryPerUser() = + testScope.runTest { + val userRepo = repository.forUser(USER_ID) + + assertThat(userRepo.userId).isEqualTo(USER_ID) + + assertThat(repository.forUser(USER_ID)).isSameInstanceAs(userRepo) + assertThat(repository.forUser(USER_ID + 1)).isNotSameInstanceAs(userRepo) + } + + @Test + fun startsRepoImmediatelyWithAllSettingKeys() = + testScope.runTest { + val userRepo = repository.forUser(USER_ID) + + val keys = + captureMany<String> { + verify(secureSettings) + .registerContentObserverForUser(capture(), anyBoolean(), any(), eq(USER_ID)) + } + + assertThat(keys).containsExactly(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION) + } + + @Test + fun forwardsSettingsValues() = runTest { + val userRepo = repository.forUser(USER_ID) + + val intAsBooleanSettings = + listOf( + FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION to + collectLastValue(userRepo.alwaysRequireConfirmationInApps) + ) + + for ((setting, accessor) in intAsBooleanSettings) { + val observer = + withArgCaptor<ContentObserver> { + verify(secureSettings) + .registerContentObserverForUser( + eq(setting), + anyBoolean(), + capture(), + eq(USER_ID) + ) + } + + for (value in listOf(true, false)) { + secureSettings.mockIntSetting(setting, if (value) 1 else 0) + observer.onChange(false) + assertThat(accessor()).isEqualTo(value) + } + } + } + + private fun SecureSettings.mockIntSetting(key: String, value: Int) { + whenever(getIntForUser(eq(key), anyInt(), eq(USER_ID))).thenReturn(value) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt index 4836af635aed..ec7ce634fd78 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt @@ -1,3 +1,19 @@ +/* + * 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.data.repository import android.hardware.biometrics.PromptInfo @@ -5,12 +21,16 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.biometrics.shared.model.PromptKind +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -21,61 +41,109 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +private const val USER_ID = 9 +private const val CHALLENGE = 90L + +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class PromptRepositoryImplTest : SysuiTestCase() { @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + private val testScope = TestScope() + private val faceSettings = FakeFaceSettingsRepository() + @Mock private lateinit var authController: AuthController private lateinit var repository: PromptRepositoryImpl @Before fun setup() { - repository = PromptRepositoryImpl(authController) + repository = PromptRepositoryImpl(faceSettings, authController) } @Test - fun isShowing() = runBlockingTest { - whenever(authController.isShowing).thenReturn(true) + fun isShowing() = + testScope.runTest { + whenever(authController.isShowing).thenReturn(true) + + val values = mutableListOf<Boolean>() + val job = launch { repository.isShowing.toList(values) } + runCurrent() - val values = mutableListOf<Boolean>() - val job = launch { repository.isShowing.toList(values) } - assertThat(values).containsExactly(true) + assertThat(values).containsExactly(true) - withArgCaptor<AuthController.Callback> { - verify(authController).addCallback(capture()) + withArgCaptor<AuthController.Callback> { + verify(authController).addCallback(capture()) - value.onBiometricPromptShown() - assertThat(values).containsExactly(true, true) + value.onBiometricPromptShown() + runCurrent() + assertThat(values).containsExactly(true, true) - value.onBiometricPromptDismissed() - assertThat(values).containsExactly(true, true, false).inOrder() + value.onBiometricPromptDismissed() + runCurrent() + assertThat(values).containsExactly(true, true, false).inOrder() - job.cancel() - verify(authController).removeCallback(eq(value)) + job.cancel() + runCurrent() + verify(authController).removeCallback(eq(value)) + } } - } @Test - fun setsAndUnsetsPrompt() = runBlockingTest { - val kind = PromptKind.Pin - val uid = 8 - val challenge = 90L - val promptInfo = PromptInfo() + fun isConfirmationRequired_whenNotForced() = + testScope.runTest { + faceSettings.setUserSettings(USER_ID, alwaysRequireConfirmationInApps = false) + val isConfirmationRequired by collectLastValue(repository.isConfirmationRequired) + + for (case in listOf(true, false)) { + repository.setPrompt( + PromptInfo().apply { isConfirmationRequested = case }, + USER_ID, + CHALLENGE, + PromptKind.Biometric() + ) + + assertThat(isConfirmationRequired).isEqualTo(case) + } + } - repository.setPrompt(promptInfo, uid, challenge, kind) + @Test + fun isConfirmationRequired_whenForced() = + testScope.runTest { + faceSettings.setUserSettings(USER_ID, alwaysRequireConfirmationInApps = true) + val isConfirmationRequired by collectLastValue(repository.isConfirmationRequired) + + for (case in listOf(true, false)) { + repository.setPrompt( + PromptInfo().apply { isConfirmationRequested = case }, + USER_ID, + CHALLENGE, + PromptKind.Biometric() + ) + + assertThat(isConfirmationRequired).isTrue() + } + } - assertThat(repository.kind.value).isEqualTo(kind) - assertThat(repository.userId.value).isEqualTo(uid) - assertThat(repository.challenge.value).isEqualTo(challenge) - assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo) + @Test + fun setsAndUnsetsPrompt() = + testScope.runTest { + val kind = PromptKind.Pin + val promptInfo = PromptInfo() - repository.unsetPrompt() + repository.setPrompt(promptInfo, USER_ID, CHALLENGE, kind) - assertThat(repository.promptInfo.value).isNull() - assertThat(repository.userId.value).isNull() - assertThat(repository.challenge.value).isNull() - } + assertThat(repository.kind.value).isEqualTo(kind) + assertThat(repository.userId.value).isEqualTo(USER_ID) + assertThat(repository.challenge.value).isEqualTo(CHALLENGE) + assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo) + + repository.unsetPrompt() + + assertThat(repository.promptInfo.value).isNull() + assertThat(repository.userId.value).isNull() + assertThat(repository.challenge.value).isNull() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt index a62ea3b77fc2..81cbaeab2a32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt @@ -106,17 +106,11 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { val currentPrompt by collectLastValue(interactor.prompt) val credentialKind by collectLastValue(interactor.credentialKind) val isCredentialAllowed by collectLastValue(interactor.isCredentialAllowed) - val isExplicitConfirmationRequired by collectLastValue(interactor.isConfirmationRequested) + val isExplicitConfirmationRequired by collectLastValue(interactor.isConfirmationRequired) assertThat(currentPrompt).isNull() - interactor.useBiometricsForAuthentication( - info, - confirmationRequired, - USER_ID, - CHALLENGE, - modalities - ) + interactor.useBiometricsForAuthentication(info, USER_ID, CHALLENGE, modalities) assertThat(currentPrompt).isNotNull() assertThat(currentPrompt?.title).isEqualTo(TITLE) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 3ba6004e4532..5b3edaba8bc0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -631,7 +631,6 @@ private fun PromptSelectorInteractor.initializePrompt( } useBiometricsForAuthentication( info, - requireConfirmation, USER_ID, CHALLENGE, BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face), diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 6a63c32f2c40..9483667909d5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -95,6 +95,61 @@ class BouncerInteractorTest : SysuiTestCase() { } @Test + fun pinAuthMethod_tryAutoConfirm_withAutoConfirmPin() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val message by collectLastValue(underTest.message) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + authenticationInteractor.lockDevice() + underTest.showOrUnlockDevice("container1") + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) + underTest.clearMessage() + + // Incomplete input. + assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull() + assertThat(message).isEmpty() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + + // Wrong 4-digit pin + assertThat(underTest.authenticate(listOf(1, 2, 3, 5), tryAutoConfirm = true)).isFalse() + assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + + // Correct input. + assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isTrue() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test + fun pinAuthMethod_tryAutoConfirm_withoutAutoConfirmPin() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val message by collectLastValue(underTest.message) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + authenticationInteractor.lockDevice() + underTest.showOrUnlockDevice("container1") + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.clearMessage() + + // Incomplete input. + assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull() + assertThat(message).isEmpty() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + + // Correct input. + assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() + assertThat(message).isEmpty() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + } + + @Test fun passwordAuthMethod() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 7b6bb37459a7..7e358d27046b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -25,7 +25,6 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel -import com.google.common.truth.Correspondence import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -286,15 +285,160 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } + @Test + fun onAutoConfirm_whenCorrect() = + testScope.runTest { + val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) + val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + authenticationInteractor.lockDevice() + sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + underTest.onPinButtonClicked(1) + underTest.onPinButtonClicked(2) + underTest.onPinButtonClicked(3) + underTest.onPinButtonClicked(4) + + assertThat(isUnlocked).isTrue() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test + fun onAutoConfirm_whenWrong() = + testScope.runTest { + val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) + val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val message by collectLastValue(bouncerViewModel.message) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + authenticationInteractor.lockDevice() + sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + underTest.onPinButtonClicked(1) + underTest.onPinButtonClicked(2) + underTest.onPinButtonClicked(3) + underTest.onPinButtonClicked(5) // PIN is now wrong! + + assertThat(entries).hasSize(0) + assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + } + + @Test + fun backspaceButtonAppearance_withoutAutoConfirm_alwaysShown() = + testScope.runTest { + val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + + assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Shown) + } + + @Test + fun backspaceButtonAppearance_withAutoConfirmButNoInput_isHidden() = + testScope.runTest { + val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + + assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) + } + + @Test + fun backspaceButtonAppearance_withAutoConfirmAndInput_isShownQuiet() = + testScope.runTest { + val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + + underTest.onPinButtonClicked(1) + + assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Subtle) + } + + @Test + fun confirmButtonAppearance_withoutAutoConfirm_alwaysShown() = + testScope.runTest { + val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + + assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Shown) + } + + @Test + fun confirmButtonAppearance_withAutoConfirm_isHidden() = + testScope.runTest { + val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + + assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) + } + + @Test + fun hintedPinLength_withoutAutoConfirm_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + + assertThat(hintedPinLength).isNull() + } + + @Test + fun hintedPinLength_withAutoConfirmPinLessThanSixDigits_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(12345, autoConfirm = true) + ) + + assertThat(hintedPinLength).isNull() + } + + @Test + fun hintedPinLength_withAutoConfirmPinExactlySixDigits_isSix() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(123456, autoConfirm = true) + ) + + assertThat(hintedPinLength).isEqualTo(6) + } + + @Test + fun hintedPinLength_withAutoConfirmPinMoreThanSixDigits_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234567, autoConfirm = true) + ) + + assertThat(hintedPinLength).isNull() + } + companion object { private const val CONTAINER_NAME = "container1" private const val ENTER_YOUR_PIN = "Enter your pin" private const val WRONG_PIN = "Wrong pin" - - val KEY_CODE = - Correspondence.transforming<EnteredKey, Int>( - { it?.input }, - "has a eventId of", - ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java index 39fb7b4cda2c..967196689650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java @@ -26,6 +26,7 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED; import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT; +import static com.android.systemui.flags.Flags.CLIPBOARD_SHARED_TRANSITIONS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -93,12 +94,16 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Mock private ClipboardImageLoader mClipboardImageLoader; @Mock + private ClipboardTransitionExecutor mClipboardTransitionExecutor; + @Mock private UiEventLogger mUiEventLogger; private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Mock private Animator mAnimator; + private ArgumentCaptor<Animator.AnimatorListener> mAnimatorListenerCaptor = + ArgumentCaptor.forClass(Animator.AnimatorListener.class); private ClipData mSampleClipData; @@ -117,6 +122,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { when(mClipboardOverlayView.getEnterAnimation()).thenReturn(mAnimator); when(mClipboardOverlayView.getExitAnimation()).thenReturn(mAnimator); + when(mClipboardOverlayView.getFadeOutAnimation()).thenReturn(mAnimator); when(mClipboardOverlayWindow.getWindowInsets()).thenReturn( getImeInsets(new Rect(0, 0, 0, 0))); @@ -124,7 +130,16 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { new ClipData.Item("Test Item")); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, true); // turned off for old tests + } + /** + * Needs to be done after setting flags for legacy tests, since the value of + * CLIPBOARD_SHARED_TRANSITIONS is checked during construction. This can be moved back into + * the setup method once CLIPBOARD_SHARED_TRANSITIONS is fully released and the tests where it + * is false are removed.[ + */ + private void initController() { mOverlayController = new ClipboardOverlayController( mContext, mClipboardOverlayView, @@ -136,6 +151,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { mClipboardUtils, mExecutor, mClipboardImageLoader, + mClipboardTransitionExecutor, mUiEventLogger); verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture()); mCallbacks = mOverlayCallbacksCaptor.getValue(); @@ -148,6 +164,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_invalidImageData_legacy() { + initController(); + ClipData clipData = new ClipData("", new String[]{"image/png"}, new ClipData.Item(Uri.parse(""))); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); @@ -161,6 +179,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_nonImageUri_legacy() { + initController(); + ClipData clipData = new ClipData("", new String[]{"resource/png"}, new ClipData.Item(Uri.parse(""))); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); @@ -175,6 +195,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_textData_legacy() { mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + initController(); + mOverlayController.setClipData(mSampleClipData, "abc"); verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false); @@ -186,6 +208,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_sensitiveTextData_legacy() { mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + initController(); + ClipDescription description = mSampleClipData.getDescription(); PersistableBundle b = new PersistableBundle(); b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true); @@ -202,6 +226,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { public void test_setClipData_repeatedCalls_legacy() { when(mAnimator.isRunning()).thenReturn(true); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + initController(); mOverlayController.setClipData(mSampleClipData, ""); mOverlayController.setClipData(mSampleClipData, ""); @@ -211,6 +236,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_invalidImageData() { + initController(); + ClipData clipData = new ClipData("", new String[]{"image/png"}, new ClipData.Item(Uri.parse(""))); @@ -223,6 +250,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_nonImageUri() { + initController(); ClipData clipData = new ClipData("", new String[]{"resource/png"}, new ClipData.Item(Uri.parse(""))); @@ -235,6 +263,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_textData() { + initController(); mOverlayController.setClipData(mSampleClipData, "abc"); verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false); @@ -245,6 +274,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_sensitiveTextData() { + initController(); ClipDescription description = mSampleClipData.getDescription(); PersistableBundle b = new PersistableBundle(); b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true); @@ -259,6 +289,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_repeatedCalls() { + initController(); when(mAnimator.isRunning()).thenReturn(true); mOverlayController.setClipData(mSampleClipData, ""); @@ -268,7 +299,9 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @Test - public void test_viewCallbacks_onShareTapped() { + public void test_viewCallbacks_onShareTapped_sharedTransitionsOff() { + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false); + initController(); mOverlayController.setClipData(mSampleClipData, ""); mCallbacks.onShareButtonTapped(); @@ -278,7 +311,22 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @Test - public void test_viewCallbacks_onDismissTapped() { + public void test_viewCallbacks_onShareTapped() { + initController(); + mOverlayController.setClipData(mSampleClipData, ""); + + mCallbacks.onShareButtonTapped(); + verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()); + mAnimatorListenerCaptor.getValue().onAnimationEnd(mAnimator); + + verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, ""); + verify(mClipboardOverlayView, times(1)).getFadeOutAnimation(); + } + + @Test + public void test_viewCallbacks_onDismissTapped_sharedTransitionsOff() { + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false); + initController(); mOverlayController.setClipData(mSampleClipData, ""); mCallbacks.onDismissButtonTapped(); @@ -288,7 +336,35 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @Test + public void test_viewCallbacks_onDismissTapped() { + initController(); + + mCallbacks.onDismissButtonTapped(); + verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()); + mAnimatorListenerCaptor.getValue().onAnimationEnd(mAnimator); + + // package name is null since we haven't actually set a source for this test + verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, null); + verify(mClipboardOverlayView, times(1)).getExitAnimation(); + } + + @Test + public void test_multipleDismissals_dismissesOnce_sharedTransitionsOff() { + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false); + initController(); + mCallbacks.onSwipeDismissInitiated(mAnimator); + mCallbacks.onDismissButtonTapped(); + mCallbacks.onSwipeDismissInitiated(mAnimator); + mCallbacks.onDismissButtonTapped(); + + verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED, 0, null); + verify(mUiEventLogger, never()).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED); + } + + @Test public void test_multipleDismissals_dismissesOnce() { + initController(); + mCallbacks.onSwipeDismissInitiated(mAnimator); mCallbacks.onDismissButtonTapped(); mCallbacks.onSwipeDismissInitiated(mAnimator); @@ -300,6 +376,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_remoteCopy_withFlagOn() { + initController(); when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true); mOverlayController.setClipData(mSampleClipData, ""); @@ -309,6 +386,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_nonRemoteCopy() { + initController(); when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false); mOverlayController.setClipData(mSampleClipData, ""); @@ -318,13 +396,16 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_logsUseLastClipSource() { + initController(); + mOverlayController.setClipData(mSampleClipData, "first.package"); - mCallbacks.onDismissButtonTapped(); + mCallbacks.onShareButtonTapped(); + mOverlayController.setClipData(mSampleClipData, "second.package"); - mCallbacks.onDismissButtonTapped(); + mCallbacks.onShareButtonTapped(); - verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "first.package"); - verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "second.package"); + verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, "first.package"); + verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, "second.package"); verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHOWN_EXPANDED, 0, "first.package"); verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHOWN_EXPANDED, 0, "second.package"); verifyNoMoreInteractions(mUiEventLogger); @@ -332,6 +413,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_logOnClipboardActionsShown() { + initController(); ClipData.Item item = mSampleClipData.getItemAt(0); item.setTextLinks(Mockito.mock(TextLinks.class)); when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString())) @@ -357,6 +439,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_noInsets_showsExpanded() { + initController(); mOverlayController.setClipData(mSampleClipData, ""); verify(mClipboardOverlayView, never()).setMinimized(true); @@ -366,6 +449,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_insets_showsMinimized() { + initController(); when(mClipboardOverlayWindow.getWindowInsets()).thenReturn( getImeInsets(new Rect(0, 0, 0, 1))); mOverlayController.setClipData(mSampleClipData, "abc"); @@ -389,6 +473,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_insetsChanged_minimizes() { + initController(); mOverlayController.setClipData(mSampleClipData, ""); verify(mClipboardOverlayView, never()).setMinimized(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt index fcc358982e6c..8f0b19307000 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -39,7 +39,7 @@ class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { @Before fun setUp() { - roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null)) + roundedCornerResDelegate = spy(RoundedCornerResDelegateImpl(mContext.resources, null)) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index 93a1868b72f5..4feba7bfd359 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -48,7 +48,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { @Test fun testTopAndBottomRoundedCornerExist() { setupResources(radius = 5) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(true, roundedCornerResDelegate.hasTop) assertEquals(true, roundedCornerResDelegate.hasBottom) } @@ -56,7 +56,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { @Test fun testTopRoundedCornerExist() { setupResources(radiusTop = 10) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(true, roundedCornerResDelegate.hasTop) assertEquals(false, roundedCornerResDelegate.hasBottom) } @@ -64,7 +64,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { @Test fun testBottomRoundedCornerExist() { setupResources(radiusBottom = 15) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(false, roundedCornerResDelegate.hasTop) assertEquals(true, roundedCornerResDelegate.hasBottom) } @@ -75,7 +75,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedTopDrawable = getTestsDrawable(R.drawable.rounded3px), roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) @@ -96,7 +96,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedTopDrawable = getTestsDrawable(R.drawable.rounded3px), roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) @@ -109,33 +109,12 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { } @Test - fun testUpdateTuningSizeFactor() { - setupResources(radius = 100, - roundedTopDrawable = getTestsDrawable(R.drawable.rounded3px), - roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) - - val factor = 5 - roundedCornerResDelegate.tuningSizeFactor = factor - val length = (factor * mContext.resources.displayMetrics.density).toInt() - - assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize) - assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize) - - roundedCornerResDelegate.tuningSizeFactor = null - - assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) - assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) - } - - @Test fun testPhysicalPixelDisplaySizeChanged() { setupResources( roundedTopDrawable = getTestsDrawable(R.drawable.rounded4px), roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(Size(4, 4), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index b1b77acd3113..60b1567c8044 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -27,6 +27,8 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION; import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT; +import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE; +import static com.android.systemui.keyguard.KeyguardViewMediator.SYS_BOOT_REASON_PROP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -80,6 +82,7 @@ import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.flags.SystemPropertiesHelper; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.log.SessionTracker; import com.android.systemui.navigationbar.NavigationModeController; @@ -164,6 +167,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ShadeWindowLogger mShadeWindowLogger; private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor; + private @Captor ArgumentCaptor<KeyguardStateController.Callback> + mKeyguardStateControllerCallback; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -179,6 +184,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock CoroutineDispatcher mDispatcher; private @Mock DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; + private @Mock SystemPropertiesHelper mSystemPropertiesHelper; private FakeFeatureFlags mFeatureFlags; private int mInitialUserId; @@ -381,6 +387,23 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { } @Test + public void testBouncerPrompt_deviceRestartedDueToMainlineUpdate() { + // GIVEN biometrics enrolled + when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(true); + + // WHEN reboot caused by ota update + KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker = + mock(KeyguardUpdateMonitor.StrongAuthTracker.class); + when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(strongAuthTracker); + when(strongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(false); + when(mSystemPropertiesHelper.get(SYS_BOOT_REASON_PROP)).thenReturn(REBOOT_MAINLINE_UPDATE); + + // THEN the bouncer prompt reason should return PROMPT_REASON_RESTART_FOR_OTA + assertEquals(KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE, + mViewMediator.mViewMediatorCallback.getBouncerPromptReason()); + } + + @Test public void testBouncerPrompt_afterUserLockDown() { // GIVEN biometrics enrolled when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(true); @@ -708,6 +731,33 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { ); } + @Test + @TestableLooper.RunWithLooper(setAsMainLooper = true) + public void pendingPinLockOnKeyguardGoingAway_doKeyguardLockedOnKeyguardVisibilityChanged() { + // GIVEN SIM_STATE_PIN_REQUIRED + mViewMediator.onSystemReady(); + final KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = + mViewMediator.mUpdateCallback; + keyguardUpdateMonitorCallback.onSimStateChanged(0, 0, + TelephonyManager.SIM_STATE_PIN_REQUIRED); + TestableLooper.get(this).processAllMessages(); + + // ...and then the primary bouncer shows while the keyguard is going away + captureKeyguardStateControllerCallback(); + when(mKeyguardStateController.isPrimaryBouncerShowing()).thenReturn(true); + when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true); + mKeyguardStateControllerCallback.getValue().onPrimaryBouncerShowingChanged(); + TestableLooper.get(this).processAllMessages(); + + // WHEN keyguard visibility becomes FALSE + mViewMediator.setShowingLocked(false); + keyguardUpdateMonitorCallback.onKeyguardVisibilityChanged(false); + TestableLooper.get(this).processAllMessages(); + + // THEN keyguard shows due to the pending SIM PIN lock + assertTrue(mViewMediator.isShowingAndNotOccluded()); + } + private void createAndStartViewMediator() { mViewMediator = new KeyguardViewMediator( mContext, @@ -747,7 +797,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mSystemSettings, mSystemClock, mDispatcher, - () -> mDreamingToLockscreenTransitionViewModel); + () -> mDreamingToLockscreenTransitionViewModel, + mSystemPropertiesHelper); mViewMediator.start(); mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null); @@ -756,4 +807,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private void captureKeyguardUpdateMonitorCallback() { verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture()); } + + private void captureKeyguardStateControllerCallback() { + verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 92ec9a1cfabc..5922cbf6268e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -507,6 +507,18 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test + fun authenticateDoesNotRunIfKeyguardIsNotShowing() = + testScope.runTest { + testGatingCheckForFaceAuth { keyguardRepository.setKeyguardShowing(false) } + } + + @Test + fun detectDoesNotRunIfKeyguardIsNotShowing() = + testScope.runTest { + testGatingCheckForDetect { keyguardRepository.setKeyguardShowing(false) } + } + + @Test fun authenticateDoesNotRunWhenFpIsLockedOut() = testScope.runTest { testGatingCheckForFaceAuth { deviceEntryFingerprintAuthRepository.setLockedOut(true) } @@ -565,6 +577,8 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { testScope.runTest { testGatingCheckForFaceAuth { bouncerRepository.setAlternateVisible(false) + // Keyguard is occluded when secure camera is active. + keyguardRepository.setKeyguardOccluded(true) fakeCommandQueue.doForEachCallback { it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) } @@ -774,6 +788,8 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { testScope.runTest { testGatingCheckForDetect { bouncerRepository.setAlternateVisible(false) + // Keyguard is occluded when secure camera is active. + keyguardRepository.setKeyguardOccluded(true) fakeCommandQueue.doForEachCallback { it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) } @@ -1011,6 +1027,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { fakeUserRepository.setSelectedUserInfo(primaryUser) biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true) bouncerRepository.setAlternateVisible(true) + keyguardRepository.setKeyguardShowing(true) runCurrent() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt new file mode 100644 index 000000000000..fd0ff9b38eec --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt @@ -0,0 +1,173 @@ +/* + * 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.keyguard.util + +import android.hardware.biometrics.BiometricFaceConstants.BIOMETRIC_ERROR_POWER_PRESSED +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR +import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED +import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT +import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT +import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_TIMEOUT +import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED +import android.hardware.biometrics.BiometricSourceType +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.whenever +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit + +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class IndicationHelperTest : SysuiTestCase() { + + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + private lateinit var underTest: IndicationHelper + + @Before + fun setup() { + underTest = + IndicationHelper( + keyguardUpdateMonitor, + ) + } + + @Test + fun suppressErrorMsg_faceErrorCancelled() { + givenPrimaryAuthNotRequired() + assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_CANCELED)) + } + + @Test + fun suppressErrorMsg_faceErrorUnableToProcess() { + givenPrimaryAuthNotRequired() + assertTrue( + underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_UNABLE_TO_PROCESS) + ) + } + + @Test + fun suppressErrorMsg_facePrimaryAuthRequired() { + givenPrimaryAuthRequired() + assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_TIMEOUT)) + } + + @Test + fun doNotSuppressErrorMsg_facePrimaryAuthRequired_faceLockout() { + givenPrimaryAuthRequired() + assertFalse(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT)) + assertFalse( + underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT_PERMANENT) + ) + } + + @Test + fun suppressErrorMsg_fingerprintErrorCancelled() { + givenPrimaryAuthNotRequired() + assertTrue( + underTest.shouldSuppressErrorMsg( + BiometricSourceType.FINGERPRINT, + FINGERPRINT_ERROR_CANCELED + ) + ) + } + + @Test + fun suppressErrorMsg_fingerprintErrorUserCancelled() { + givenPrimaryAuthNotRequired() + assertTrue( + underTest.shouldSuppressErrorMsg( + BiometricSourceType.FINGERPRINT, + FINGERPRINT_ERROR_USER_CANCELED + ) + ) + } + + @Test + fun suppressErrorMsg_fingerprintErrorPowerPressed() { + givenPrimaryAuthNotRequired() + assertTrue( + underTest.shouldSuppressErrorMsg( + BiometricSourceType.FINGERPRINT, + BIOMETRIC_ERROR_POWER_PRESSED + ) + ) + } + + @Test + fun suppressErrorMsg_fingerprintPrimaryAuthRequired() { + givenPrimaryAuthRequired() + assertTrue( + underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FINGERPRINT_ERROR_TIMEOUT) + ) + } + + @Test + fun doNotSuppressErrorMsg_fingerprintPrimaryAuthRequired_fingerprintLockout() { + givenPrimaryAuthRequired() + assertFalse( + underTest.shouldSuppressErrorMsg( + BiometricSourceType.FINGERPRINT, + FINGERPRINT_ERROR_LOCKOUT + ) + ) + assertFalse( + underTest.shouldSuppressErrorMsg( + BiometricSourceType.FACE, + FINGERPRINT_ERROR_LOCKOUT_PERMANENT + ) + ) + } + + @Test + fun isFaceLockoutErrorMsgId() { + givenPrimaryAuthRequired() + assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT)) + assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT_PERMANENT)) + assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_TIMEOUT)) + assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_CANCELED)) + assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_UNABLE_TO_PROCESS)) + assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_VENDOR)) + } + + private fun givenPrimaryAuthNotRequired() { + whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) + .thenReturn(true) + } + + private fun givenPrimaryAuthRequired() { + whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())) + .thenReturn(false) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt index 2aff90c47189..5b8272b04bfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt @@ -19,7 +19,9 @@ package com.android.systemui.media.controls.ui import android.app.PendingIntent import android.content.res.ColorStateList import android.content.res.Configuration +import android.database.ContentObserver import android.os.LocaleList +import android.provider.Settings import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.util.MathUtils.abs @@ -56,6 +58,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq +import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.time.FakeSystemClock import java.util.Locale import javax.inject.Provider @@ -113,6 +116,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { @Mock lateinit var mediaFlags: MediaFlags @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + @Mock lateinit var globalSettings: GlobalSettings private lateinit var transitionRepository: FakeKeyguardTransitionRepository @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener> @Captor @@ -120,6 +124,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { @Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener> @Captor lateinit var keyguardCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback> @Captor lateinit var hostStateCallback: ArgumentCaptor<MediaHostStatesManager.Callback> + @Captor lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver> private val clock = FakeSystemClock() private lateinit var mediaCarouselController: MediaCarouselController @@ -148,6 +153,7 @@ class MediaCarouselControllerTest : SysuiTestCase() { mediaFlags, keyguardUpdateMonitor, KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope), + globalSettings ) verify(configurationController).addCallback(capture(configListener)) verify(mediaDataManager).addListener(capture(listener)) @@ -160,6 +166,11 @@ class MediaCarouselControllerTest : SysuiTestCase() { whenever(mediaDataManager.smartspaceMediaData).thenReturn(smartspaceMediaData) whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false) MediaPlayerData.clear() + verify(globalSettings) + .registerContentObserver( + eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)), + settingsObserverCaptor.capture() + ) } @Test @@ -873,6 +884,15 @@ class MediaCarouselControllerTest : SysuiTestCase() { assertTrue(stateUpdated) } + @Test + fun testAnimationScaleChanged_mediaControlPanelsNotified() { + MediaPlayerData.addMediaPlayer("key", DATA, panel, clock, isSsReactivated = false) + + globalSettings.putFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 0f) + settingsObserverCaptor.value!!.onChange(false) + verify(panel).updateAnimatorDurationScale() + } + /** * Helper method when a configuration change occurs. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index f6075add6afe..f902be3c1a29 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -25,7 +25,6 @@ import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.res.Configuration -import android.database.ContentObserver import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color @@ -113,7 +112,6 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong -import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.anyString import org.mockito.Mockito.mock @@ -239,7 +237,6 @@ public class MediaControlPanelTest : SysuiTestCase() { this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false) } @Mock private lateinit var globalSettings: GlobalSettings - @Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver> @JvmField @Rule val mockito = MockitoJUnit.rule() @@ -281,7 +278,7 @@ public class MediaControlPanelTest : SysuiTestCase() { lockscreenUserManager, broadcastDialogController, fakeFeatureFlag, - globalSettings, + globalSettings ) { override fun loadAnimator( animId: Int, @@ -292,12 +289,6 @@ public class MediaControlPanelTest : SysuiTestCase() { } } - verify(globalSettings) - .registerContentObserver( - eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)), - settingsObserverCaptor.capture() - ) - initGutsViewHolderMocks() initMediaViewHolderMocks() @@ -986,7 +977,7 @@ public class MediaControlPanelTest : SysuiTestCase() { // When the setting changes, globalSettings.putFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 0f) - settingsObserverCaptor.value!!.onChange(false) + player.updateAnimatorDurationScale() // Then the seekbar is set to not animate assertThat(seekBarObserver.animationEnabled).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index f3aee48cfb56..a14ff2f72f1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -356,6 +356,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { }); verify(mockMediaOutputController).releaseSession(); + verify(mDialogLaunchAnimator).disableAllCurrentDialogsExitAnimations(); } @Test @@ -371,7 +372,7 @@ public class MediaOutputDialogTest extends SysuiTestCase { @NonNull private MediaOutputDialog makeTestDialog(MediaOutputController controller) { return new MediaOutputDialog(mContext, false, mBroadcastSender, - controller, mUiEventLogger); + controller, mDialogLaunchAnimator, mUiEventLogger); } private void withTestDialog(MediaOutputController controller, Consumer<MediaOutputDialog> c) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 25d494cee5e8..697d1a3b775c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -20,7 +20,6 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; -import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.inputmethodservice.InputMethodService.IME_VISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; @@ -364,7 +363,7 @@ public class NavigationBarTest extends SysuiTestCase { externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true); defaultNavBar.setImeWindowStatus( - DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false); + DEFAULT_DISPLAY, null, 0 /* vis */, BACK_DISPOSITION_DEFAULT, false); // Verify IME window state will be updated in external NavBar & default NavBar state reset. assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN | NAVIGATION_HINT_IME_SWITCHER_SHOWN, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt index 3706859a5b74..0a1eca69f1ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt @@ -62,9 +62,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(R.id.begin_guide) assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) .isEqualTo(R.id.end_guide) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) .isEqualTo(1f) assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) @@ -95,9 +95,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID) assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) .isEqualTo(PARENT_ID) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) .isEqualTo(0.5f) assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) @@ -133,18 +133,15 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { changes() with(qqsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(qsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(largeScreenConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } } @@ -155,18 +152,15 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { changes() with(qqsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(0f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(0f) + assertThat(systemIconsAlphaConstraint).isEqualTo(0f) } with(qsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(largeScreenConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } } @@ -181,12 +175,13 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { with(qqsConstraint) { // In this case, the date is constrained on the end by a Barrier determined by either - // privacy or statusIcons + // privacy or clickableIcons assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.barrier) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd).isEqualTo(R.id.date) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) + .isEqualTo(R.id.date) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd).isEqualTo(R.id.date) assertThat(getConstraint(R.id.barrier).layout.mReferenceIds).asList().containsExactly( - R.id.statusIcons, + R.id.shade_header_system_icons, R.id.privacy_container ) assertThat(getConstraint(R.id.barrier).layout.mBarrierDirection).isEqualTo(START) @@ -272,7 +267,7 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_left) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_right) @@ -285,9 +280,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_left) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) @@ -311,7 +306,7 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_right) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isEqualTo(R.id.center_left) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_left) @@ -324,9 +319,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_left) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) @@ -382,7 +377,8 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints()() assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() - assertThat(qqsConstraint.getConstraint(R.id.statusIcons).layout.constrainedWidth).isTrue() + val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) + assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() } @Test @@ -390,9 +386,13 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { CombinedShadeHeadersConstraintManagerImpl.centerCutoutConstraints(false, 10)() assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() - assertThat(qqsConstraint.getConstraint(R.id.statusIcons).layout.constrainedWidth).isTrue() + val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) + assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() } + private val ConstraintSet.systemIconsAlphaConstraint + get() = getConstraint(R.id.shade_header_system_icons).propertySet.alpha + private operator fun ConstraintsChanges.invoke() { qqsConstraintsChanges?.invoke(qqsConstraint) qsConstraintsChanges?.invoke(qsConstraint) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index b6a007f04a70..ef7c7bc0844d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -117,6 +117,7 @@ import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.QSFragment; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.ShadeRepository; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -168,6 +169,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; @@ -311,6 +313,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected ActivityStarter mActivityStarter; @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; @Mock protected ShadeRepository mShadeRepository; + @Mock private ShadeInteractor mShadeInteractor; + @Mock private JavaAdapter mJavaAdapter; @Mock private CastController mCastController; protected final int mMaxUdfpsBurnInOffsetY = 5; @@ -687,6 +691,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mDumpManager, mKeyguardFaceAuthInteractor, mShadeRepository, + mShadeInteractor, + mJavaAdapter, mCastController ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index c737a18ea8a2..2a9b403cb2e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -49,6 +49,7 @@ import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy import com.android.systemui.multishade.data.repository.MultiShadeRepository import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationInsetsController @@ -93,6 +94,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController @Mock private lateinit var centralSurfaces: CentralSurfaces @Mock private lateinit var backActionInteractor: BackActionInteractor + @Mock private lateinit var powerInteractor: PowerInteractor @Mock private lateinit var dockManager: DockManager @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController @@ -174,6 +176,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { lockIconViewController, centralSurfaces, backActionInteractor, + powerInteractor, notificationShadeWindowController, unfoldTransitionProgressProvider, keyguardUnlockAnimationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 1740284a26dd..252a03bb07d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -48,6 +48,7 @@ import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy import com.android.systemui.multishade.data.repository.MultiShadeRepository import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.DragDownHelper import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -95,6 +96,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var shadeController: ShadeController @Mock private lateinit var centralSurfaces: CentralSurfaces @Mock private lateinit var backActionInteractor: BackActionInteractor + @Mock private lateinit var powerInteractor: PowerInteractor @Mock private lateinit var dockManager: DockManager @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout @@ -187,6 +189,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { lockIconViewController, centralSurfaces, backActionInteractor, + powerInteractor, notificationShadeWindowController, unfoldTransitionProgressProvider, keyguardUnlockAnimationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java new file mode 100644 index 000000000000..c72f4e77d4eb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -0,0 +1,265 @@ +/* + * 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.shade; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.os.Handler; +import android.os.Looper; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityManager; + +import com.android.internal.jank.InteractionJankMonitor; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardStatusView; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.TestScopeProvider; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; +import com.android.systemui.media.controls.pipeline.MediaDataManager; +import com.android.systemui.media.controls.ui.MediaHierarchyManager; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QS; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.data.repository.ShadeRepository; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; +import com.android.systemui.shade.transition.ShadeTransitionController; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.QsFrameTranslateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository; +import com.android.systemui.statusbar.notification.stack.AmbientState; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.LightBarController; +import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.user.domain.interactor.UserInteractor; +import com.android.systemui.util.kotlin.JavaAdapter; + +import dagger.Lazy; + +import org.junit.After; +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import kotlinx.coroutines.test.TestScope; + +public class QuickSettingsControllerBaseTest extends SysuiTestCase { + protected static final float QS_FRAME_START_X = 0f; + protected static final int QS_FRAME_WIDTH = 1000; + protected static final int QS_FRAME_TOP = 0; + protected static final int QS_FRAME_BOTTOM = 1000; + protected static final int DEFAULT_HEIGHT = 1000; + // In split shade min = max + protected static final int DEFAULT_MIN_HEIGHT_SPLIT_SHADE = DEFAULT_HEIGHT; + protected static final int DEFAULT_MIN_HEIGHT = 300; + + protected QuickSettingsController mQsController; + + protected TestScope mTestScope = TestScopeProvider.getTestScope(); + + @Mock + protected Resources mResources; + @Mock protected KeyguardBottomAreaView mQsFrame; + @Mock protected KeyguardStatusBarView mKeyguardStatusBar; + @Mock protected QS mQs; + @Mock protected QSFragment mQSFragment; + @Mock protected Lazy<NotificationPanelViewController> mPanelViewControllerLazy; + @Mock protected NotificationPanelViewController mNotificationPanelViewController; + @Mock protected NotificationPanelView mPanelView; + @Mock protected ViewGroup mQsHeader; + @Mock protected ViewParent mPanelViewParent; + @Mock protected QsFrameTranslateController mQsFrameTranslateController; + @Mock protected ShadeTransitionController mShadeTransitionController; + @Mock protected PulseExpansionHandler mPulseExpansionHandler; + @Mock protected NotificationRemoteInputManager mNotificationRemoteInputManager; + @Mock protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock protected LightBarController mLightBarController; + @Mock protected NotificationStackScrollLayoutController + mNotificationStackScrollLayoutController; + @Mock protected LockscreenShadeTransitionController mLockscreenShadeTransitionController; + @Mock protected NotificationShadeDepthController mNotificationShadeDepthController; + @Mock protected ShadeHeaderController mShadeHeaderController; + @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock protected KeyguardStateController mKeyguardStateController; + @Mock protected KeyguardBypassController mKeyguardBypassController; + @Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock protected ScrimController mScrimController; + @Mock protected MediaDataManager mMediaDataManager; + @Mock protected MediaHierarchyManager mMediaHierarchyManager; + @Mock protected AmbientState mAmbientState; + @Mock protected RecordingController mRecordingController; + @Mock protected FalsingManager mFalsingManager; + @Mock protected FalsingCollector mFalsingCollector; + @Mock protected AccessibilityManager mAccessibilityManager; + @Mock protected LockscreenGestureLogger mLockscreenGestureLogger; + @Mock protected MetricsLogger mMetricsLogger; + @Mock protected FeatureFlags mFeatureFlags; + @Mock protected InteractionJankMonitor mInteractionJankMonitor; + @Mock protected ShadeLogger mShadeLogger; + @Mock protected DumpManager mDumpManager; + @Mock protected UiEventLogger mUiEventLogger; + @Mock protected CastController mCastController; + @Mock protected DeviceProvisionedController mDeviceProvisionedController; + @Mock protected UserInteractor mUserInteractor; + protected FakeDisableFlagsRepository mDisableFlagsRepository = + new FakeDisableFlagsRepository(); + protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository(); + + protected SysuiStatusBarStateController mStatusBarStateController; + protected ShadeInteractor mShadeInteractor; + + protected Handler mMainHandler; + protected LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback; + + protected final ShadeExpansionStateManager mShadeExpansionStateManager = + new ShadeExpansionStateManager(); + + protected FragmentHostManager.FragmentListener mFragmentListener; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController); + mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, + mInteractionJankMonitor, mShadeExpansionStateManager); + + when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); + mShadeInteractor = + new ShadeInteractor( + mDisableFlagsRepository, + mKeyguardRepository, + new FakeUserSetupRepository(), + mDeviceProvisionedController, + mUserInteractor + ); + + KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext); + keyguardStatusView.setId(R.id.keyguard_status_view); + + when(mResources.getDimensionPixelSize( + R.dimen.lockscreen_shade_qs_transition_distance)).thenReturn(DEFAULT_HEIGHT); + when(mPanelView.getResources()).thenReturn(mResources); + when(mPanelView.getContext()).thenReturn(getContext()); + when(mPanelView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); + when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); + when(mPanelView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); + when(mQsFrame.getX()).thenReturn(QS_FRAME_START_X); + when(mQsFrame.getWidth()).thenReturn(QS_FRAME_WIDTH); + when(mQsHeader.getTop()).thenReturn(QS_FRAME_TOP); + when(mQsHeader.getBottom()).thenReturn(QS_FRAME_BOTTOM); + when(mPanelView.getY()).thenReturn((float) QS_FRAME_TOP); + when(mPanelView.getHeight()).thenReturn(QS_FRAME_BOTTOM); + when(mPanelView.findViewById(R.id.keyguard_status_view)) + .thenReturn(mock(KeyguardStatusView.class)); + when(mQs.getView()).thenReturn(mPanelView); + when(mQSFragment.getView()).thenReturn(mPanelView); + + when(mNotificationRemoteInputManager.isRemoteInputActive()) + .thenReturn(false); + when(mInteractionJankMonitor.begin(any(), anyInt())) + .thenReturn(true); + when(mInteractionJankMonitor.end(anyInt())) + .thenReturn(true); + + when(mPanelView.getParent()).thenReturn(mPanelViewParent); + when(mQs.getHeader()).thenReturn(mQsHeader); + + doAnswer(invocation -> { + mLockscreenShadeTransitionCallback = invocation.getArgument(0); + return null; + }).when(mLockscreenShadeTransitionController).addCallback(any()); + + + mMainHandler = new Handler(Looper.getMainLooper()); + + mQsController = new QuickSettingsController( + mPanelViewControllerLazy, + mPanelView, + mQsFrameTranslateController, + mShadeTransitionController, + mPulseExpansionHandler, + mNotificationRemoteInputManager, + mShadeExpansionStateManager, + mStatusBarKeyguardViewManager, + mLightBarController, + mNotificationStackScrollLayoutController, + mLockscreenShadeTransitionController, + mNotificationShadeDepthController, + mShadeHeaderController, + mStatusBarTouchableRegionManager, + mKeyguardStateController, + mKeyguardBypassController, + mKeyguardUpdateMonitor, + mScrimController, + mMediaDataManager, + mMediaHierarchyManager, + mAmbientState, + mRecordingController, + mFalsingManager, + mFalsingCollector, + mAccessibilityManager, + mLockscreenGestureLogger, + mMetricsLogger, + mFeatureFlags, + mInteractionJankMonitor, + mShadeLogger, + mDumpManager, + mock(KeyguardFaceAuthInteractor.class), + mock(ShadeRepository.class), + mShadeInteractor, + new JavaAdapter(mTestScope.getBackgroundScope()), + mCastController + ); + mQsController.init(); + + mFragmentListener = mQsController.getQsFragmentListener(); + } + + @After + public void tearDown() { + mMainHandler.removeCallbacksAndMessages(null); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java index ff047aa2faf2..6d288e1db1cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java @@ -28,238 +28,32 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.res.Resources; -import android.os.Handler; -import android.os.Looper; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; -import com.android.internal.jank.InteractionJankMonitor; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.keyguard.KeyguardStatusView; -import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.classifier.FalsingCollector; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; -import com.android.systemui.media.controls.pipeline.MediaDataManager; -import com.android.systemui.media.controls.ui.MediaHierarchyManager; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; -import com.android.systemui.qs.QSFragment; -import com.android.systemui.screenrecord.RecordingController; -import com.android.systemui.shade.data.repository.ShadeRepository; -import com.android.systemui.shade.transition.ShadeTransitionController; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.NotificationShadeDepthController; -import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.QsFrameTranslateController; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; -import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.notification.stack.AmbientState; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.KeyguardStatusBarView; -import com.android.systemui.statusbar.phone.LightBarController; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; -import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.KeyguardStateController; - -import dagger.Lazy; - -import org.junit.After; -import org.junit.Before; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class QuickSettingsControllerTest extends SysuiTestCase { - - private static final float QS_FRAME_START_X = 0f; - private static final int QS_FRAME_WIDTH = 1000; - private static final int QS_FRAME_TOP = 0; - private static final int QS_FRAME_BOTTOM = 1000; - private static final int DEFAULT_HEIGHT = 1000; - // In split shade min = max - private static final int DEFAULT_MIN_HEIGHT_SPLIT_SHADE = DEFAULT_HEIGHT; - private static final int DEFAULT_MIN_HEIGHT = 300; - - private QuickSettingsController mQsController; - - @Mock private Resources mResources; - @Mock private KeyguardBottomAreaView mQsFrame; - @Mock private KeyguardStatusBarView mKeyguardStatusBar; - @Mock private QS mQs; - @Mock private QSFragment mQSFragment; - @Mock private Lazy<NotificationPanelViewController> mPanelViewControllerLazy; - @Mock private NotificationPanelViewController mNotificationPanelViewController; - @Mock private NotificationPanelView mPanelView; - @Mock private ViewGroup mQsHeader; - @Mock private ViewParent mPanelViewParent; - @Mock private QsFrameTranslateController mQsFrameTranslateController; - @Mock private ShadeTransitionController mShadeTransitionController; - @Mock private PulseExpansionHandler mPulseExpansionHandler; - @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager; - @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - @Mock private LightBarController mLightBarController; - @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; - @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; - @Mock private NotificationShadeDepthController mNotificationShadeDepthController; - @Mock private ShadeHeaderController mShadeHeaderController; - @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; - @Mock private KeyguardStateController mKeyguardStateController; - @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock private ScrimController mScrimController; - @Mock private MediaDataManager mMediaDataManager; - @Mock private MediaHierarchyManager mMediaHierarchyManager; - @Mock private AmbientState mAmbientState; - @Mock private RecordingController mRecordingController; - @Mock private FalsingManager mFalsingManager; - @Mock private FalsingCollector mFalsingCollector; - @Mock private AccessibilityManager mAccessibilityManager; - @Mock private LockscreenGestureLogger mLockscreenGestureLogger; - @Mock private MetricsLogger mMetricsLogger; - @Mock private FeatureFlags mFeatureFlags; - @Mock private InteractionJankMonitor mInteractionJankMonitor; - @Mock private ShadeLogger mShadeLogger; - @Mock private DumpManager mDumpManager; - @Mock private UiEventLogger mUiEventLogger; - @Mock private CastController mCastController; - - private SysuiStatusBarStateController mStatusBarStateController; - - private Handler mMainHandler; - private LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback; - - private final ShadeExpansionStateManager mShadeExpansionStateManager = - new ShadeExpansionStateManager(); - - private FragmentHostManager.FragmentListener mFragmentListener; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController); - mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, - mInteractionJankMonitor, mShadeExpansionStateManager); - - KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext); - keyguardStatusView.setId(R.id.keyguard_status_view); - - when(mResources.getDimensionPixelSize( - R.dimen.lockscreen_shade_qs_transition_distance)).thenReturn(DEFAULT_HEIGHT); - when(mPanelView.getResources()).thenReturn(mResources); - when(mPanelView.getContext()).thenReturn(getContext()); - when(mPanelView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); - when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); - when(mPanelView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); - when(mQsFrame.getX()).thenReturn(QS_FRAME_START_X); - when(mQsFrame.getWidth()).thenReturn(QS_FRAME_WIDTH); - when(mQsHeader.getTop()).thenReturn(QS_FRAME_TOP); - when(mQsHeader.getBottom()).thenReturn(QS_FRAME_BOTTOM); - when(mPanelView.getY()).thenReturn((float) QS_FRAME_TOP); - when(mPanelView.getHeight()).thenReturn(QS_FRAME_BOTTOM); - when(mPanelView.findViewById(R.id.keyguard_status_view)) - .thenReturn(mock(KeyguardStatusView.class)); - when(mQs.getView()).thenReturn(mPanelView); - when(mQSFragment.getView()).thenReturn(mPanelView); - - when(mNotificationRemoteInputManager.isRemoteInputActive()) - .thenReturn(false); - when(mInteractionJankMonitor.begin(any(), anyInt())) - .thenReturn(true); - when(mInteractionJankMonitor.end(anyInt())) - .thenReturn(true); - - when(mPanelView.getParent()).thenReturn(mPanelViewParent); - when(mQs.getHeader()).thenReturn(mQsHeader); - - doAnswer(invocation -> { - mLockscreenShadeTransitionCallback = invocation.getArgument(0); - return null; - }).when(mLockscreenShadeTransitionController).addCallback(any()); - - - mMainHandler = new Handler(Looper.getMainLooper()); - - mQsController = new QuickSettingsController( - mPanelViewControllerLazy, - mPanelView, - mQsFrameTranslateController, - mShadeTransitionController, - mPulseExpansionHandler, - mNotificationRemoteInputManager, - mShadeExpansionStateManager, - mStatusBarKeyguardViewManager, - mLightBarController, - mNotificationStackScrollLayoutController, - mLockscreenShadeTransitionController, - mNotificationShadeDepthController, - mShadeHeaderController, - mStatusBarTouchableRegionManager, - mKeyguardStateController, - mKeyguardBypassController, - mKeyguardUpdateMonitor, - mScrimController, - mMediaDataManager, - mMediaHierarchyManager, - mAmbientState, - mRecordingController, - mFalsingManager, - mFalsingCollector, - mAccessibilityManager, - mLockscreenGestureLogger, - mMetricsLogger, - mFeatureFlags, - mInteractionJankMonitor, - mShadeLogger, - mDumpManager, - mock(KeyguardFaceAuthInteractor.class), - mock(ShadeRepository.class), - mCastController - ); - - mFragmentListener = mQsController.getQsFragmentListener(); - } - - @After - public void tearDown() { - mMainHandler.removeCallbacksAndMessages(null); - } +public class QuickSettingsControllerTest extends QuickSettingsControllerBaseTest { @Test public void testCloseQsSideEffects() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt new file mode 100644 index 000000000000..cc4a06341cc6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt @@ -0,0 +1,85 @@ +/* + * 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.shade + +import android.app.StatusBarManager +import androidx.test.filters.SmallTest +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class QuickSettingsControllerWithCoroutinesTest : QuickSettingsControllerBaseTest() { + + @Test + fun isExpansionEnabled_dozing_false() = + mTestScope.runTest { + mKeyguardRepository.setIsDozing(true) + runCurrent() + + assertThat(mQsController.isExpansionEnabled).isFalse() + } + + @Test + fun isExpansionEnabled_notDozing_true() = + mTestScope.runTest { + mKeyguardRepository.setIsDozing(false) + runCurrent() + + assertThat(mQsController.isExpansionEnabled).isTrue() + } + + @Test + fun isExpansionEnabled_qsDisabled_false() = + mTestScope.runTest { + mDisableFlagsRepository.disableFlags.value = + DisableFlagsModel( + StatusBarManager.DISABLE_NONE, + StatusBarManager.DISABLE2_QUICK_SETTINGS + ) + runCurrent() + + assertThat(mQsController.isExpansionEnabled).isFalse() + } + + @Test + fun isExpansionEnabled_shadeDisabled_false() = + mTestScope.runTest { + mDisableFlagsRepository.disableFlags.value = + DisableFlagsModel( + StatusBarManager.DISABLE_NONE, + StatusBarManager.DISABLE2_NOTIFICATION_SHADE + ) + runCurrent() + + assertThat(mQsController.isExpansionEnabled).isFalse() + } + + @Test + fun isExpansionEnabled_qsAndShadeEnabled_true() = + mTestScope.runTest { + mDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE) + runCurrent() + + assertThat(mQsController.isExpansionEnabled).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java index 57ae621f01ef..31bfa3fdf8cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java @@ -66,6 +66,11 @@ import org.mockito.MockitoAnnotations; @SmallTest public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { + private static final String SINGLE_CARRIER_TEXT = "singleCarrierText"; + private static final String MULTI_CARRIER_TEXT = "multiCarrierText"; + private static final String FIRST_CARRIER_NAME = "carrier1"; + private static final String SECOND_CARRIER_NAME = "carrier2"; + private ShadeCarrierGroupController mShadeCarrierGroupController; private SignalCallback mSignalCallback; private CarrierTextManager.CarrierTextCallback mCallback; @@ -148,8 +153,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 1, subscriptionIds length 1, anySims false CarrierTextManager.CarrierTextCallbackInfo c1 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{""}, + SINGLE_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME}, false, new int[]{0}); mCallback.updateCarrierInfo(c1); @@ -157,8 +162,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 1, subscriptionIds length 1, anySims true CarrierTextManager.CarrierTextCallbackInfo c2 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{""}, + SINGLE_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME}, true, new int[]{0}); mCallback.updateCarrierInfo(c2); @@ -166,8 +171,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 2, subscriptionIds length 2, anySims false CarrierTextManager.CarrierTextCallbackInfo c3 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, false, new int[]{0, 1}); mCallback.updateCarrierInfo(c3); @@ -175,8 +180,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 2, subscriptionIds length 2, anySims true CarrierTextManager.CarrierTextCallbackInfo c4 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, true, new int[]{0, 1}); mCallback.updateCarrierInfo(c4); @@ -189,8 +194,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 2, subscriptionIds length 1, anySims false CarrierTextManager.CarrierTextCallbackInfo c1 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, false, new int[]{0}); mCallback.updateCarrierInfo(c1); @@ -198,8 +203,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 2, subscriptionIds length 1, anySims true CarrierTextManager.CarrierTextCallbackInfo c2 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, true, new int[]{0}); mCallback.updateCarrierInfo(c2); @@ -207,8 +212,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 1, subscriptionIds length 2, anySims false CarrierTextManager.CarrierTextCallbackInfo c3 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{""}, + SINGLE_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME}, false, new int[]{0, 1}); mCallback.updateCarrierInfo(c3); @@ -216,8 +221,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // listOfCarriers length 1, subscriptionIds length 2, anySims true CarrierTextManager.CarrierTextCallbackInfo c4 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{""}, + SINGLE_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME}, true, new int[]{0, 1}); mCallback.updateCarrierInfo(c4); @@ -230,8 +235,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { CarrierTextManager.CarrierTextCallbackInfo c4 = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, true, new int[]{0, 1}); mCallback.updateCarrierInfo(c4); @@ -264,6 +269,20 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { } @Test + public void testVisibleView_airplaneMode_WFCOn() { + CarrierTextManager.CarrierTextCallbackInfo + info = new CarrierTextManager.CarrierTextCallbackInfo( + "", + new CharSequence[]{FIRST_CARRIER_NAME, ""}, + true, + new int[]{0, 1}, + false /* airplaneMode */); + mCallback.updateCarrierInfo(info); + mTestableLooper.processAllMessages(); + assertEquals(View.VISIBLE, mShadeCarrierGroupController.getShadeCarrierVisibility(0)); + } + + @Test public void testListenerNotCalledOnRegistreation() { mShadeCarrierGroupController .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener); @@ -276,8 +295,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // Only one element in the info CarrierTextManager.CarrierTextCallbackInfo info = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{""}, + SINGLE_CARRIER_TEXT, + new CharSequence[]{SINGLE_CARRIER_TEXT}, true, new int[]{0}, false /* airplaneMode */); @@ -295,8 +314,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { // More than one element in the info CarrierTextManager.CarrierTextCallbackInfo info = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, true, new int[]{0, 1}, false /* airplaneMode */); @@ -313,16 +332,16 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { public void testSingleMultiCarrierSwitch() { CarrierTextManager.CarrierTextCallbackInfo singleCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{""}, + SINGLE_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME}, true, new int[]{0}, false /* airplaneMode */); CarrierTextManager.CarrierTextCallbackInfo multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, true, new int[]{0, 1}, false /* airplaneMode */); @@ -347,8 +366,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { public void testNoCallbackIfSingleCarrierDoesntChange() { CarrierTextManager.CarrierTextCallbackInfo singleCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{""}, + SINGLE_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME}, true, new int[]{0}, false /* airplaneMode */); @@ -369,8 +388,8 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { public void testNoCallbackIfMultiCarrierDoesntChange() { CarrierTextManager.CarrierTextCallbackInfo multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo( - "", - new CharSequence[]{"", ""}, + MULTI_CARRIER_TEXT, + new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME}, true, new int[]{0, 1}, false /* airplaneMode */); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt new file mode 100644 index 000000000000..7392a94a04c2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt @@ -0,0 +1,321 @@ +/* + * 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.shade.data.repository + +import android.app.ActivityManager +import android.app.StatusBarManager.DISABLE2_NONE +import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE +import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS +import android.os.UserManager +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.telephony.data.repository.FakeTelephonyRepository +import com.android.systemui.telephony.domain.interactor.TelephonyInteractor +import com.android.systemui.user.data.model.UserSwitcherSettingsModel +import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.user.domain.interactor.GuestUserInteractor +import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode +import com.android.systemui.user.domain.interactor.RefreshUsersScheduler +import com.android.systemui.user.domain.interactor.UserInteractor +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class ShadeInteractorTest : SysuiTestCase() { + private lateinit var underTest: ShadeInteractor + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + private val featureFlags = FakeFeatureFlags() + private val userSetupRepository = FakeUserSetupRepository() + private val userRepository = FakeUserRepository() + private val disableFlagsRepository = FakeDisableFlagsRepository() + private val keyguardRepository = FakeKeyguardRepository() + + @Mock private lateinit var manager: UserManager + @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode + @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController + @Mock private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var activityManager: ActivityManager + @Mock private lateinit var uiEventLogger: UiEventLogger + @Mock private lateinit var guestInteractor: GuestUserInteractor + + private lateinit var userInteractor: UserInteractor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) + + val refreshUsersScheduler = + RefreshUsersScheduler( + applicationScope = testScope.backgroundScope, + mainDispatcher = testDispatcher, + repository = userRepository, + ) + userInteractor = + UserInteractor( + applicationContext = context, + repository = userRepository, + activityStarter = activityStarter, + keyguardInteractor = + KeyguardInteractorFactory.create(featureFlags = featureFlags) + .keyguardInteractor, + featureFlags = featureFlags, + manager = manager, + headlessSystemUserMode = headlessSystemUserMode, + applicationScope = testScope.backgroundScope, + telephonyInteractor = + TelephonyInteractor( + repository = FakeTelephonyRepository(), + ), + broadcastDispatcher = fakeBroadcastDispatcher, + keyguardUpdateMonitor = keyguardUpdateMonitor, + backgroundDispatcher = testDispatcher, + activityManager = activityManager, + refreshUsersScheduler = refreshUsersScheduler, + guestUserInteractor = guestInteractor, + uiEventLogger = uiEventLogger, + ) + underTest = + ShadeInteractor( + disableFlagsRepository, + keyguardRepository, + userSetupRepository, + deviceProvisionedController, + userInteractor, + ) + } + + @Test + fun isExpandToQsEnabled_deviceNotProvisioned_false() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isFalse() + } + + @Test + fun isExpandToQsEnabled_userNotSetupAndSimpleUserSwitcher_false() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + + userSetupRepository.setUserSetup(false) + userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true)) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isFalse() + } + + @Test + fun isExpandToQsEnabled_shadeNotEnabled_false() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + userSetupRepository.setUserSetup(true) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NOTIFICATION_SHADE, + ) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isFalse() + } + + @Test + fun isExpandToQsEnabled_quickSettingsNotEnabled_false() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + userSetupRepository.setUserSetup(true) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_QUICK_SETTINGS, + ) + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isFalse() + } + + @Test + fun isExpandToQsEnabled_dozing_false() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + userSetupRepository.setUserSetup(true) + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NONE, + ) + + keyguardRepository.setIsDozing(true) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isFalse() + } + + @Test + fun isExpandToQsEnabled_userSetup_true() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + keyguardRepository.setIsDozing(false) + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NONE, + ) + + userSetupRepository.setUserSetup(true) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isTrue() + } + + @Test + fun isExpandToQsEnabled_notSimpleUserSwitcher_true() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + keyguardRepository.setIsDozing(false) + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NONE, + ) + + userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = false)) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isTrue() + } + + @Test + fun isExpandToQsEnabled_respondsToDozingUpdates() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + keyguardRepository.setIsDozing(false) + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NONE, + ) + userSetupRepository.setUserSetup(true) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isTrue() + + // WHEN dozing starts + keyguardRepository.setIsDozing(true) + + // THEN expand is disabled + assertThat(actual).isFalse() + + // WHEN dozing stops + keyguardRepository.setIsDozing(false) + + // THEN expand is enabled + assertThat(actual).isTrue() + } + + @Test + fun isExpandToQsEnabled_respondsToDisableUpdates() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + keyguardRepository.setIsDozing(false) + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NONE, + ) + userSetupRepository.setUserSetup(true) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isTrue() + + // WHEN QS is disabled + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_QUICK_SETTINGS, + ) + // THEN expand is disabled + assertThat(actual).isFalse() + + // WHEN QS is enabled + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NONE, + ) + // THEN expand is enabled + assertThat(actual).isTrue() + } + + @Test + fun isExpandToQsEnabled_respondsToUserUpdates() = + testScope.runTest { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + keyguardRepository.setIsDozing(false) + disableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = DISABLE2_NONE, + ) + userSetupRepository.setUserSetup(true) + + val actual by collectLastValue(underTest.isExpandToQsEnabled) + + assertThat(actual).isTrue() + + // WHEN the user is no longer setup + userSetupRepository.setUserSetup(false) + userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true)) + + // THEN expand is disabled + assertThat(actual).isFalse() + + // WHEN the user is setup again + userSetupRepository.setUserSetup(true) + + // THEN expand is enabled + assertThat(actual).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index 04c93cb71e42..9495fdd50a7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -21,6 +21,8 @@ import android.graphics.drawable.Drawable import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags.TRANSIT_CLOCK import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.ClockId import com.android.systemui.plugins.ClockMetadata @@ -68,6 +70,7 @@ class ClockRegistryTest : SysuiTestCase() { private lateinit var fakeDefaultProvider: FakeClockPlugin private lateinit var pluginListener: PluginListener<ClockProviderPlugin> private lateinit var registry: ClockRegistry + private val featureFlags = FakeFeatureFlags() companion object { private fun failFactory(clockId: ClockId): ClockController { @@ -448,4 +451,44 @@ class ClockRegistryTest : SysuiTestCase() { val actual = ClockSettings.serialize(ClockSettings("ID", null)) assertEquals(expected, actual) } + + @Test + fun testTransitClockEnabled_hasTransitClock() { + testTransitClockFlag(true) + } + + @Test + fun testTransitClockDisabled_noTransitClock() { + testTransitClockFlag(false) + } + + private fun testTransitClockFlag(flag: Boolean) { + featureFlags.set(TRANSIT_CLOCK, flag) + registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK) + val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>() + val plugin = FakeClockPlugin() + .addClock("clock_1", "clock 1") + .addClock("DIGITAL_CLOCK_METRO", "metro clock") + pluginListener.onPluginLoaded(plugin, mockContext, mockPluginLifecycle) + + val list = registry.getClocks() + if (flag) { + assertEquals( + setOf( + ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), + ClockMetadata("clock_1", "clock 1"), + ClockMetadata("DIGITAL_CLOCK_METRO", "metro clock") + ), + list.toSet() + ) + } else { + assertEquals( + setOf( + ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME), + ClockMetadata("clock_1", "clock 1") + ), + list.toSet() + ) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 1643e174ee13..385d556092a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; -import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; @@ -201,7 +200,7 @@ public class CommandQueueTest extends SysuiTestCase { mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true); waitForIdleSync(); - verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE), + verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(0), eq(BACK_DISPOSITION_DEFAULT), eq(false)); verify(mCallbacks).setImeWindowStatus( eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 48c3e2d85574..b1f5dde278be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -72,6 +72,7 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.graphics.Color; import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricSourceType; import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; @@ -98,14 +99,15 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FaceHelpMessageDeferral; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; @@ -213,6 +215,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private StatusBarStateController.StateListener mStatusBarStateListener; private ScreenLifecycle.Observer mScreenObserver; private BroadcastReceiver mBroadcastReceiver; + private IndicationHelper mIndicationHelper; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private TestableLooper mTestableLooper; private final int mCurrentUserId = 1; @@ -262,13 +265,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); - when(mDevicePolicyResourcesManager.getString(anyString(), any())) .thenReturn(mDisclosureGeneric); when(mDevicePolicyResourcesManager.getString(anyString(), any(), anyString())) .thenReturn(mDisclosureWithOrganization); when(mUserTracker.getUserId()).thenReturn(mCurrentUserId); + mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor); + mWakeLock = new WakeLockFake(); mWakeLockBuilder = new WakeLockFake.Builder(mContext); mWakeLockBuilder.setWakeLock(mWakeLock); @@ -304,7 +308,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mAlarmManager, mUserTracker, mock(BouncerMessageInteractor.class), - flags + flags, + mIndicationHelper ); mController.init(); mController.setIndicationArea(mIndicationArea); @@ -805,33 +810,19 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test - public void transientIndication_visibleWhenDozing_ignoresFingerprintCancellation() { + public void transientIndication_visibleWhenDozing_ignoresFingerprintErrorMsg() { createController(); - mController.setVisible(true); reset(mRotateTextViewController); - mController.getKeyguardCallback().onBiometricError( - FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED, "foo", - BiometricSourceType.FINGERPRINT); - mController.getKeyguardCallback().onBiometricError( - FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar", - BiometricSourceType.FINGERPRINT); - verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); - verifyNoMessage(INDICATION_TYPE_TRANSIENT); - } - - @Test - public void transientIndication_visibleWhenDozing_ignoresPowerPressed() { - createController(); - - mController.setVisible(true); - reset(mRotateTextViewController); + // WHEN a fingerprint error user cancelled message is received mController.getKeyguardCallback().onBiometricError( - FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "foo", + BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED, "foo", BiometricSourceType.FINGERPRINT); + // THEN no message is shown verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); + verifyNoMessage(INDICATION_TYPE_TRANSIENT); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index ced07348c27a..305f48b147f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -33,25 +33,21 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.RemoteInputControllerLogger; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.RemoteInputUriController; -import dagger.Lazy; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.Optional; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -69,6 +65,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { @Mock private RemoteInputUriController mRemoteInputUriController; @Mock private NotificationClickNotifier mClickNotifier; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; + @Mock private PowerInteractor mPowerInteractor; private TestableNotificationRemoteInputManager mRemoteInputManager; private NotificationEntry mEntry; @@ -82,7 +79,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { mLockscreenUserManager, mSmartReplyController, mVisibilityProvider, - () -> Optional.of(mock(CentralSurfaces.class)), + mPowerInteractor, mStateController, mRemoteInputUriController, mock(RemoteInputControllerLogger.class), @@ -140,7 +137,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { NotificationLockscreenUserManager lockscreenUserManager, SmartReplyController smartReplyController, NotificationVisibilityProvider visibilityProvider, - Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, + PowerInteractor powerInteractor, StatusBarStateController statusBarStateController, RemoteInputUriController remoteInputUriController, RemoteInputControllerLogger remoteInputControllerLogger, @@ -153,7 +150,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { lockscreenUserManager, smartReplyController, visibilityProvider, - centralSurfacesOptionalLazy, + powerInteractor, statusBarStateController, remoteInputUriController, remoteInputControllerLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt new file mode 100644 index 000000000000..580463a3010f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt @@ -0,0 +1,274 @@ +/* + * 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.statusbar.disableflags.data.repository + +import android.app.StatusBarManager.DISABLE2_NONE +import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE +import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS +import android.app.StatusBarManager.DISABLE_CLOCK +import android.app.StatusBarManager.DISABLE_NONE +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS +import android.content.res.Configuration +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBufferFactory +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class DisableFlagsRepositoryTest : SysuiTestCase() { + + private lateinit var underTest: DisableFlagsRepository + + private val testScope = TestScope(UnconfinedTestDispatcher()) + private val commandQueue: CommandQueue = mock() + private val configurationController: ConfigurationController = mock() + private val remoteInputQuickSettingsDisabler = + RemoteInputQuickSettingsDisabler( + context, + commandQueue, + configurationController, + ) + private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10) + private val disableFlagsLogger = DisableFlagsLogger() + + @Before + fun setUp() { + underTest = + DisableFlagsRepositoryImpl( + commandQueue, + DISPLAY_ID, + testScope.backgroundScope, + remoteInputQuickSettingsDisabler, + logBuffer, + disableFlagsLogger, + ) + } + + @Test + fun disableFlags_initialValue_none() { + assertThat(underTest.disableFlags.value) + .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)) + } + + @Test + fun disableFlags_noSubscribers_callbackStillRegistered() = + testScope.runTest { verify(commandQueue).addCallback(any()) } + + @Test + fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue() + } + + @Test + fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse() + } + + @Test + fun disableFlags_notifAlertsDisabled_differentDisplay_notifAlertsEnabledTrue() = + testScope.runTest { + val wrongDisplayId = DISPLAY_ID + 10 + + getCommandQueueCallback() + .disable( + wrongDisplayId, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + + // THEN our repo reports them as still enabled + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue() + } + + @Test + fun disableFlags_shadeNotDisabled_shadeEnabledTrue() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.isShadeEnabled()).isTrue() + } + + @Test + fun disableFlags_shadeDisabled_shadeEnabledFalse() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NONE, + DISABLE2_NOTIFICATION_SHADE, + /* animate= */ false, + ) + + assertThat(underTest.disableFlags.value.isShadeEnabled()).isFalse() + } + + @Test + fun disableFlags_shadeDisabled_differentDisplay_shadeEnabledTrue() = + testScope.runTest { + val wrongDisplayId = DISPLAY_ID + 10 + + getCommandQueueCallback() + .disable( + wrongDisplayId, + DISABLE_NONE, + DISABLE2_NOTIFICATION_SHADE, + /* animate= */ false, + ) + + // THEN our repo reports them as still enabled + assertThat(underTest.disableFlags.value.isShadeEnabled()).isTrue() + } + + @Test + fun disableFlags_quickSettingsNotDisabled_quickSettingsEnabledTrue() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isTrue() + } + + @Test + fun disableFlags_quickSettingsDisabled_quickSettingsEnabledFalse() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NONE, + DISABLE2_QUICK_SETTINGS, + /* animate= */ false, + ) + + assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse() + } + + @Test + fun disableFlags_quickSettingsDisabled_differentDisplay_quickSettingsEnabledTrue() = + testScope.runTest { + val wrongDisplayId = DISPLAY_ID + 10 + + getCommandQueueCallback() + .disable( + wrongDisplayId, + DISABLE_NONE, + DISABLE2_QUICK_SETTINGS, + /* animate= */ false, + ) + + // THEN our repo reports them as still enabled + assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isTrue() + } + + @Test + fun disableFlags_remoteInputActive_quickSettingsEnabledFalse() = + testScope.runTest { + // WHEN remote input is set up to be active + val configuration = Configuration(mContext.resources.configuration) + configuration.orientation = Configuration.ORIENTATION_LANDSCAPE + mContext.orCreateTestableResources.addOverride( + R.bool.config_use_split_notification_shade, + /* value= */ false + ) + remoteInputQuickSettingsDisabler.setRemoteInputActive(true) + remoteInputQuickSettingsDisabler.onConfigChanged(configuration) + + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NONE, + DISABLE2_NONE, + /* animate= */ false, + ) + + // THEN quick settings is disabled (even if the disable flags don't say so) + assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse() + } + + @Test + fun disableFlags_reactsToChanges() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse() + + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_CLOCK, // Unrelated to notifications + DISABLE2_NONE, + /* animate= */ false, + ) + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue() + + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_QUICK_SETTINGS or DISABLE2_NOTIFICATION_SHADE, + /* animate= */ false, + ) + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse() + assertThat(underTest.disableFlags.value.isShadeEnabled()).isFalse() + assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse() + } + + private fun getCommandQueueCallback(): CommandQueue.Callbacks { + val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>() + verify(commandQueue).addCallback(callbackCaptor.capture()) + return callbackCaptor.value + } + + private companion object { + const val DISPLAY_ID = 1 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt new file mode 100644 index 000000000000..b66231c63bc6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt @@ -0,0 +1,22 @@ +/* + * 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.statusbar.disableflags.data.repository + +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeDisableFlagsRepository : DisableFlagsRepository { + override val disableFlags = MutableStateFlow(DisableFlagsModel()) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java index ac66ad9e9c8d..2d044fec1eb6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java @@ -92,8 +92,9 @@ public class HighPriorityProviderTest extends SysuiTestCase { .getPeopleNotificationType(entry)) .thenReturn(TYPE_PERSON); - // THEN it has high priority + // THEN it has high priority BUT it has low explicit priority. assertTrue(mHighPriorityProvider.isHighPriority(entry)); + assertFalse(mHighPriorityProvider.isExplicitlyHighPriority(entry)); } @Test @@ -115,7 +116,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { @Test public void lowImportanceConversation() { - // GIVEN notification is high importance and is a people notification + // GIVEN notification is low importance and is a people notification final Notification notification = new Notification.Builder(mContext, "test") .build(); final NotificationEntry entry = new NotificationEntryBuilder() @@ -162,8 +163,9 @@ public class HighPriorityProviderTest extends SysuiTestCase { .getPeopleNotificationType(entry)) .thenReturn(TYPE_NON_PERSON); - // THEN it has high priority + // THEN it has high priority but low explicit priority assertTrue(mHighPriorityProvider.isHighPriority(entry)); + assertFalse(mHighPriorityProvider.isExplicitlyHighPriority(entry)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt new file mode 100644 index 000000000000..fe49016a5d45 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt @@ -0,0 +1,99 @@ +/* + * 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.statusbar.notification.domain.interactor + +import android.app.StatusBarManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBufferFactory +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepositoryImpl +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class NotificationsInteractorTest : SysuiTestCase() { + + private lateinit var underTest: NotificationsInteractor + + private val testScope = TestScope(UnconfinedTestDispatcher()) + private val commandQueue: CommandQueue = mock() + private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10) + private val disableFlagsLogger = DisableFlagsLogger() + private lateinit var disableFlagsRepository: DisableFlagsRepository + + @Before + fun setUp() { + disableFlagsRepository = + DisableFlagsRepositoryImpl( + commandQueue, + DISPLAY_ID, + testScope.backgroundScope, + mock(), + logBuffer, + disableFlagsLogger, + ) + underTest = NotificationsInteractor(disableFlagsRepository) + } + + @Test + fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() { + val callback = getCommandQueueCallback() + + callback.disable( + DISPLAY_ID, + StatusBarManager.DISABLE_NONE, + StatusBarManager.DISABLE2_NONE, + /* animate= */ false + ) + + assertThat(underTest.areNotificationAlertsEnabled()).isTrue() + } + + @Test + fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() { + val callback = getCommandQueueCallback() + + callback.disable( + DISPLAY_ID, + StatusBarManager.DISABLE_NOTIFICATION_ALERTS, + StatusBarManager.DISABLE2_NONE, + /* animate= */ false + ) + + assertThat(underTest.areNotificationAlertsEnabled()).isFalse() + } + + private fun getCommandQueueCallback(): CommandQueue.Callbacks { + val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>() + verify(commandQueue).addCallback(callbackCaptor.capture()) + return callbackCaptor.value + } + + private companion object { + const val DISPLAY_ID = 1 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java index 6bd3f7a27413..8f39ee6a6e9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java @@ -257,7 +257,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_LOW) .setParent(parent) .build(); - when(mHighPriorityProvider.isHighPriority(any())).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(any())).thenReturn(false); assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @@ -270,7 +270,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .setUser(new UserHandle(NOTIF_USER_ID)) .setImportance(IMPORTANCE_LOW) .build(); - when(mHighPriorityProvider.isHighPriority(any())).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(any())).thenReturn(false); assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @@ -292,7 +292,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_LOW) .setParent(parent) .build(); - when(mHighPriorityProvider.isHighPriority(any())).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(any())).thenReturn(false); assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @@ -309,7 +309,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { mEntry = new NotificationEntryBuilder() .setImportance(IMPORTANCE_LOW) .build(); - when(mHighPriorityProvider.isHighPriority(any())).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(any())).thenReturn(false); // THEN filter out the entry assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); @@ -328,7 +328,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { mEntry = new NotificationEntryBuilder() .setImportance(IMPORTANCE_LOW) .build(); - when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(mEntry)).thenReturn(false); // THEN do not filter out the entry assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); @@ -345,7 +345,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .setUser(new UserHandle(NOTIF_USER_ID)) .setImportance(IMPORTANCE_LOW) .build(); - when(mHighPriorityProvider.isHighPriority(any())).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(any())).thenReturn(false); // WhHEN the show silent notifs on lockscreen setting is unset assertNull(mFakeSettings.getString(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)); @@ -460,13 +460,33 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .setKey(mEntry.getKey()) .setImportance(IMPORTANCE_MIN) .build()); - when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(mEntry)).thenReturn(false); // THEN filter out the entry assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test + public void highPriorityCharacteristicsIgnored() { + // GIVEN an 'unfiltered-keyguard-showing' state with silent notifications hidden + setupUnfilteredState(mEntry); + mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true); + mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); + + // WHEN the notification doesn't exceed the threshold to show on the lockscreen, but does + // have the "high priority characteristics" that would promote it to high priority + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setImportance(IMPORTANCE_MIN) + .build()); + when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true); + when(mHighPriorityProvider.isExplicitlyHighPriority(mEntry)).thenReturn(false); + + // THEN filter out the entry anyway, because the user explicitly asked us to hide it + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); + } + + @Test public void notificationVisibilityPublic() { // GIVEN a VISIBILITY_PUBLIC notification NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder() @@ -538,14 +558,14 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { // WHEN its parent does exceed threshold tot show on the lockscreen mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false); - when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(true); + when(mHighPriorityProvider.isExplicitlyHighPriority(parent)).thenReturn(true); // THEN filter out the entry regardless of parent assertTrue( mKeyguardNotificationVisibilityProvider.shouldHideNotification(entryWithParent)); // WHEN its parent doesn't exceed threshold to show on lockscreen - when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(false); + when(mHighPriorityProvider.isExplicitlyHighPriority(parent)).thenReturn(false); modifyEntry(parent.getSummary(), builder -> builder .setImportance(IMPORTANCE_MIN) .done()); @@ -591,7 +611,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { // notification doesn't have a summary // notification is high priority, so it shouldn't be filtered - when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true); + when(mHighPriorityProvider.isExplicitlyHighPriority(mEntry)).thenReturn(true); } @SysUISingleton diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt new file mode 100644 index 000000000000..7707a7e8bf8c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt @@ -0,0 +1,77 @@ +/* + * 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.statusbar.notification.logging + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.LogcatEchoTracker +import com.android.systemui.statusbar.notification.stack.StackStateLogger +import com.google.common.truth.Truth +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class StackStateLoggerTest : SysuiTestCase() { + private val logBufferCounter = LogBufferCounter() + private lateinit var logger: StackStateLogger + + @Before + fun setup() { + logger = StackStateLogger(logBufferCounter.logBuffer, logBufferCounter.logBuffer) + } + + @Test + fun groupChildRemovalEvent() { + logger.groupChildRemovalEventProcessed(KEY) + verifyDidLog(1) + logger.groupChildRemovalAnimationEnded(KEY) + verifyDidLog(1) + } + + class LogBufferCounter { + val recentLogs = mutableListOf<Pair<String, LogLevel>>() + val tracker = + object : LogcatEchoTracker { + override val logInBackgroundThread: Boolean = false + override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = false + override fun isTagLoggable(tagName: String, level: LogLevel): Boolean { + recentLogs.add(tagName to level) + return true + } + } + val logBuffer = + LogBuffer(name = "test", maxSize = 1, logcatEchoTracker = tracker, systrace = false) + + fun verifyDidLog(times: Int) { + Truth.assertThat(recentLogs).hasSize(times) + recentLogs.clear() + } + } + + private fun verifyDidLog(times: Int) { + logBufferCounter.verifyDidLog(times) + } + + companion object { + private val KEY = "PACKAGE_NAME" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt index c2f1f61e8a5f..2e68cec1fe63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt @@ -36,6 +36,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.logging.NotificationLogger import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer +import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.HeadsUpManager @@ -107,6 +108,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() { rivSubComponentFactory, metricsLogger, logBufferLogger, + mock<NotificationChildrenContainerLogger>(), listContainer, smartReplyConstants, smartReplyController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 813bae893569..df470717faeb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -76,7 +76,7 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; -import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; +import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; @@ -581,6 +581,7 @@ public class NotificationTestHelper { mock(NotificationGutsManager.class), mDismissibilityProvider, mock(MetricsLogger.class), + mock(NotificationChildrenContainerLogger.class), mock(SmartReplyConstants.class), mock(SmartReplyController.class), mFeatureFlags, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index ee02a7b6e090..8ad271bef2e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -61,7 +61,6 @@ import android.view.WindowInsets; import android.view.WindowInsetsAnimation; import android.widget.TextView; -import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; @@ -84,12 +83,11 @@ import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -103,7 +101,6 @@ import java.util.ArrayList; /** * Tests for {@link NotificationStackScrollLayout}. */ -@Ignore("b/255552856") @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -113,7 +110,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below private AmbientState mAmbientState; private TestableResources mTestableResources; - @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Mock private NotificationsController mNotificationsController; @Mock private SysuiStatusBarStateController mBarState; @@ -128,7 +124,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationSection mNotificationSection; @Mock private NotificationSwipeHelper mNotificationSwipeHelper; @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController; - @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; + @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private NotificationShelf mNotificationShelf; @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -136,7 +132,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private FeatureFlags mFeatureFlags; @Before - @UiThreadTest public void setUp() throws Exception { allowTestableLooperAsMainThread(); mTestableResources = mContext.getOrCreateTestableResources(); @@ -162,7 +157,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mDependency.injectTestDependency(AmbientState.class, mAmbientState); mDependency.injectTestDependency(NotificationShelf.class, mNotificationShelf); mDependency.injectTestDependency( - UnlockedScreenOffAnimationController.class, mUnlockedScreenOffAnimationController); + ScreenOffAnimationController.class, mScreenOffAnimationController); NotificationShelfController notificationShelfController = mock(NotificationShelfController.class); @@ -364,7 +359,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @UiThreadTest public void testSetExpandedHeight_listenerReceivedCallbacks() { final float expectedHeight = 0f; @@ -375,20 +369,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testAppearFractionCalculation() { - // appear start position - when(mNotificationShelf.getIntrinsicHeight()).thenReturn(100); - // because it's the same as shelf height, appear start position equals shelf height - mStackScroller.mStatusBarHeight = 100; - // appear end position - when(mEmptyShadeView.getHeight()).thenReturn(200); - - assertEquals(0f, mStackScroller.calculateAppearFraction(100)); - assertEquals(1f, mStackScroller.calculateAppearFraction(200)); - assertEquals(0.5f, mStackScroller.calculateAppearFraction(150)); - } - - @Test public void testAppearFractionCalculationIsNotNegativeWhenShelfBecomesSmaller() { // this situation might occur if status bar height is defined in pixels while shelf height // in dp and screen density changes - appear start position @@ -405,7 +385,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @UiThreadTest public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() { mTestableResources .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true); @@ -590,6 +569,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testReInflatesFooterViews() { + when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text); clearInvocations(mStackScroller); mStackScroller.reinflateViews(); verify(mStackScroller).setFooterView(any()); @@ -597,21 +577,18 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @UiThreadTest public void testSetIsBeingDraggedResetsExposedMenu() { mStackScroller.setIsBeingDragged(true); verify(mNotificationSwipeHelper).resetExposedMenuView(true, true); } @Test - @UiThreadTest public void testPanelTrackingStartResetsExposedMenu() { mStackScroller.onPanelTrackingStarted(); verify(mNotificationSwipeHelper).resetExposedMenuView(true, true); } @Test - @UiThreadTest public void testDarkModeResetsExposedMenu() { mStackScroller.setHideAmount(0.1f, 0.1f); verify(mNotificationSwipeHelper).resetExposedMenuView(true, true); @@ -916,7 +893,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller.setHasFilteredOutSeenNotifications(true); mStackScroller.updateEmptyShadeView(true, false); - verify(mEmptyShadeView).setFooterText(not(0)); + verify(mEmptyShadeView).setFooterText(not(eq(0))); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index cb71fb8f703a..036b8becfbbc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -150,7 +150,6 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false); - verify(mCentralSurfaces).updateQsExpansionEnabled(); verify(mShadeController).animateCollapseShade(); } @@ -162,7 +161,6 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { when(mCommandQueue.panelsEnabled()).thenReturn(true); mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false); - verify(mCentralSurfaces).updateQsExpansionEnabled(); verify(mShadeController, never()).animateCollapseShade(); } 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 cc8324b22027..1ffffe4dca75 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 @@ -29,7 +29,6 @@ import static junit.framework.TestCase.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -510,6 +509,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { configurationController, mNotificationShadeWindowController, mNotificationShelfController, + mStackScrollerController, mDozeParameters, mScrimController, mLockscreenWallpaperLazy, @@ -593,8 +593,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mCentralSurfaces.mPresenter = mNotificationPresenter; mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController; mCentralSurfaces.mBarService = mBarService; - mCentralSurfaces.mStackScrollerController = mStackScrollerController; - mCentralSurfaces.mStackScroller = mStackScroller; mCentralSurfaces.mGestureWakeLock = mPowerManager.newWakeLock( PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "sysui:GestureWakeLock"); mCentralSurfaces.startKeyguard(); @@ -1253,34 +1251,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - public void dozing_wakeUp() throws RemoteException { - // GIVEN can wakeup when dozing & is dozing - when(mScreenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true); - setDozing(true); - - // WHEN wakeup is requested - final int wakeReason = PowerManager.WAKE_REASON_TAP; - mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason); - - // THEN power manager receives wakeup - verify(mPowerManagerService).wakeUp(eq(0L), eq(wakeReason), anyString(), anyString()); - } - - @Test - public void notDozing_noWakeUp() throws RemoteException { - // GIVEN can wakeup when dozing and NOT dozing - when(mScreenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true); - setDozing(false); - - // WHEN wakeup is requested - final int wakeReason = PowerManager.WAKE_REASON_TAP; - mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason); - - // THEN power manager receives wakeup - verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString()); - } - - @Test public void frpLockedDevice_shadeDisabled() { when(mDeviceProvisionedController.isFrpActive()).thenReturn(true); when(mDozeServiceHost.isPulsing()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 1724f27f63b8..5bd6ff4e73f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -37,6 +37,7 @@ import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.QuickSettingsController; @@ -55,6 +56,7 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; +import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -80,6 +82,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private FakeMetricsLogger mMetricsLogger; private final ShadeController mShadeController = mock(ShadeController.class); private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class); + private final NotificationsInteractor mNotificationsInteractor = + mock(NotificationsInteractor.class); private final KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class); private final NotifPipelineFlags mNotifPipelineFlags = mock(NotifPipelineFlags.class); @@ -122,7 +126,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(DynamicPrivacyController.class), mKeyguardStateController, mCentralSurfaces, + mNotificationsInteractor, mock(LockscreenShadeTransitionController.class), + mock(PowerInteractor.class), mCommandQueue, mock(NotificationLockscreenUserManager.class), mock(SysuiStatusBarStateController.class), @@ -233,9 +239,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { .setTag("a") .setNotification(n) .build(); - when(mCentralSurfaces.areNotificationAlertsDisabled()).thenReturn(true); + when(mNotificationsInteractor.areNotificationAlertsEnabled()).thenReturn(false); - assertTrue("CentralSurfaces alerts disabled shouldn't allow interruptions", + assertTrue("When alerts aren't enabled, interruptions are suppressed", mInterruptSuppressor.suppressInterruptions(entry)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt index 6e3af26121bf..932c4a17cae2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt @@ -152,13 +152,11 @@ class CarrierConfigRepositoryTest : SysuiTestCase() { } private fun sendConfig(subId: Int) { - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive( - context, - Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) - .putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId) - ) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId), + ) } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt index d1df6e3c2072..cf832b4ab059 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt @@ -379,9 +379,10 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { var latest: Int? = null val job = underTest.carrierId.onEach { latest = it }.launchIn(this) - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive(context, carrierIdIntent(carrierId = 4321)) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + carrierIdIntent(carrierId = 4321), + ) assertThat(latest).isEqualTo(4321) @@ -653,10 +654,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { val job = underTest.networkName.onEach { latest = it }.launchIn(this) val intent = spnIntent() - - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive(context, intent) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent) assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP)) @@ -670,17 +668,13 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { val job = underTest.networkName.onEach { latest = it }.launchIn(this) val intent = spnIntent() - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive(context, intent) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent) assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP)) // WHEN an intent with a different subId is sent val wrongSubIntent = spnIntent(subId = 101) - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive(context, wrongSubIntent) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, wrongSubIntent) // THEN the previous intent's name is still used assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP)) @@ -695,9 +689,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { val job = underTest.networkName.onEach { latest = it }.launchIn(this) val intent = spnIntent() - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive(context, intent) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent) assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP)) val intentWithoutInfo = @@ -706,9 +698,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() { showPlmn = false, ) - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive(context, intentWithoutInfo) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intentWithoutInfo) assertThat(latest).isEqualTo(DEFAULT_NAME) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index fd156d86c19e..74bcdeec25cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -626,23 +626,17 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(latest).isEqualTo(INVALID_SUBSCRIPTION_ID) - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive( - context, - Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) - .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_2_ID) - ) - } + val intent2 = + Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_2_ID) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent2) assertThat(latest).isEqualTo(SUB_2_ID) - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive( - context, - Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) - .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID) - ) - } + val intent1 = + Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent1) assertThat(latest).isEqualTo(SUB_1_ID) } @@ -1074,13 +1068,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(configFromContext.showAtLeast3G).isTrue() // WHEN the change event is fired - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive( - context, - Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) - .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID) - ) - } + val intent = + Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent) // THEN the config is updated assertTrue(latest!!.areEqual(configFromContext)) @@ -1099,12 +1090,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(configFromContext.showAtLeast3G).isTrue() // WHEN the change event is fired - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive( - context, - Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) - ) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), + ) // THEN the config is updated assertThat(latest!!.areEqual(configFromContext)).isTrue() @@ -1123,12 +1112,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(configFromContext.showAtLeast3G).isTrue() // WHEN the change event is fired - fakeBroadcastDispatcher.registeredReceivers.forEach { receiver -> - receiver.onReceive( - context, - Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) - ) - } + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), + ) // WHEN collection starts AFTER the broadcast is sent out val latest by collectLastValue(underTest.defaultDataSubRatConfig) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index dc68180d962d..5ed3a5c7aa41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod +import android.content.Intent import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities @@ -33,7 +34,6 @@ import android.net.wifi.WifiManager.UNKNOWN_SSID import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel @@ -46,13 +46,10 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.nullable import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.TestScope @@ -60,7 +57,6 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @@ -73,7 +69,6 @@ class WifiRepositoryImplTest : SysuiTestCase() { private lateinit var underTest: WifiRepositoryImpl - @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher @Mock private lateinit var logger: WifiInputLogger @Mock private lateinit var tableLogger: TableLogBuffer @Mock private lateinit var connectivityManager: ConnectivityManager @@ -86,15 +81,6 @@ class WifiRepositoryImplTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - whenever( - broadcastDispatcher.broadcastFlow( - any(), - nullable(), - anyInt(), - nullable(), - ) - ) - .thenReturn(flowOf(Unit)) executor = FakeExecutor(FakeSystemClock()) connectivityRepository = @@ -168,27 +154,23 @@ class WifiRepositoryImplTest : SysuiTestCase() { @Test fun isWifiEnabled_intentsReceived_valueUpdated() = testScope.runTest { - val intentFlow = MutableSharedFlow<Unit>() - whenever( - broadcastDispatcher.broadcastFlow( - any(), - nullable(), - anyInt(), - nullable(), - ) - ) - .thenReturn(intentFlow) underTest = createRepo() val job = underTest.isWifiEnabled.launchIn(this) whenever(wifiManager.isWifiEnabled).thenReturn(true) - intentFlow.emit(Unit) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(WifiManager.WIFI_STATE_CHANGED_ACTION), + ) assertThat(underTest.isWifiEnabled.value).isTrue() whenever(wifiManager.isWifiEnabled).thenReturn(false) - intentFlow.emit(Unit) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(WifiManager.WIFI_STATE_CHANGED_ACTION), + ) assertThat(underTest.isWifiEnabled.value).isFalse() @@ -198,23 +180,16 @@ class WifiRepositoryImplTest : SysuiTestCase() { @Test fun isWifiEnabled_bothIntentAndNetworkUpdates_valueAlwaysUpdated() = testScope.runTest { - val intentFlow = MutableSharedFlow<Unit>() - whenever( - broadcastDispatcher.broadcastFlow( - any(), - nullable(), - anyInt(), - nullable(), - ) - ) - .thenReturn(intentFlow) underTest = createRepo() val networkJob = underTest.wifiNetwork.launchIn(this) val enabledJob = underTest.isWifiEnabled.launchIn(this) whenever(wifiManager.isWifiEnabled).thenReturn(false) - intentFlow.emit(Unit) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(WifiManager.WIFI_STATE_CHANGED_ACTION), + ) assertThat(underTest.isWifiEnabled.value).isFalse() whenever(wifiManager.isWifiEnabled).thenReturn(true) @@ -227,7 +202,10 @@ class WifiRepositoryImplTest : SysuiTestCase() { assertThat(underTest.isWifiEnabled.value).isFalse() whenever(wifiManager.isWifiEnabled).thenReturn(true) - intentFlow.emit(Unit) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(WifiManager.WIFI_STATE_CHANGED_ACTION), + ) assertThat(underTest.isWifiEnabled.value).isTrue() networkJob.cancel() @@ -1317,7 +1295,7 @@ class WifiRepositoryImplTest : SysuiTestCase() { private fun createRepo(): WifiRepositoryImpl { return WifiRepositoryImpl( - broadcastDispatcher, + fakeBroadcastDispatcher, connectivityManager, connectivityRepository, logger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt index 67727ae21641..d1c38f6cbea7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt @@ -77,6 +77,7 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { ) whenever(controller.users).thenAnswer { users } + whenever(controller.isUserSwitcherEnabled).thenReturn(true) underTest = object : BaseUserSwitcherAdapter(controller) { @@ -162,6 +163,19 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() { } @Test + fun count_onlyShowsCurrentUserWhenMultiUserDisabled() { + whenever(controller.isUserSwitcherEnabled).thenReturn(false) + assertThat(underTest.count).isEqualTo(1) + assertThat(underTest.getItem(0).isCurrent).isTrue() + } + + @Test + fun count_doesNotIgnoreAllOtherUsersWhenMultiUserEnabled() { + whenever(controller.isUserSwitcherEnabled).thenReturn(true) + assertThat(underTest.count).isEqualTo(users.size) + } + + @Test fun getItem() { assertThat((0 until underTest.count).map { position -> underTest.getItem(position) }) .isEqualTo(users) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt index 8290dab19bdf..1ab62d00d307 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.qs.tiles.UserDetailItemView import com.android.systemui.user.data.source.UserRecord +import com.android.systemui.util.mockito.whenever import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue @@ -68,6 +69,8 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) + whenever(userSwitcherController.isUserSwitcherEnabled).thenReturn(true) + mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, layoutInflater) `when`(layoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean())) .thenReturn(inflatedUserDetailItemView) 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 2158396b4e15..06e6162f0261 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -133,15 +133,19 @@ import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; import com.android.wm.shell.bubbles.BubbleEntry; import com.android.wm.shell.bubbles.BubbleLogger; +import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubbleStackView; import com.android.wm.shell.bubbles.BubbleViewInfoTask; +import com.android.wm.shell.bubbles.BubbleViewProvider; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.bubbles.StackEducationViewKt; +import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.sysui.ShellCommandHandler; @@ -151,7 +155,6 @@ import com.android.wm.shell.taskview.TaskViewTransitions; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -300,6 +303,8 @@ public class BubblesTest extends SysuiTestCase { private UserHandle mUser0; + private FakeBubbleProperties mBubbleProperties; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -361,6 +366,7 @@ public class BubblesTest extends SysuiTestCase { mock(UserTracker.class) ); when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor); + mBubbleProperties = new FakeBubbleProperties(); mBubbleController = new TestableBubbleController( mContext, mShellInit, @@ -385,7 +391,8 @@ public class BubblesTest extends SysuiTestCase { mock(Handler.class), mTaskViewTransitions, mock(SyncTransactionQueue.class), - mock(IWindowManager.class)); + mock(IWindowManager.class), + mBubbleProperties); mBubbleController.setExpandListener(mBubbleExpandListener); spyOn(mBubbleController); @@ -476,7 +483,7 @@ public class BubblesTest extends SysuiTestCase { public void testAddBubble() { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -484,7 +491,7 @@ public class BubblesTest extends SysuiTestCase { assertFalse(mBubbleController.hasBubbles()); mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -499,7 +506,7 @@ public class BubblesTest extends SysuiTestCase { assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); verify(mNotifCallback, times(2)).invalidateNotifications(anyString()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -562,7 +569,7 @@ public class BubblesTest extends SysuiTestCase { assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey())); assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey())); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -581,7 +588,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Make sure the notif is suppressed assertBubbleNotificationSuppressedFromShade(mBubbleEntry); @@ -590,11 +597,10 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.collapseStack(); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test - @Ignore("Currently broken.") public void testCollapseAfterChangingExpandedBubble() { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow); @@ -613,7 +619,7 @@ public class BubblesTest extends SysuiTestCase { assertStackExpanded(); verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged( true, mRow2.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Last added is the one that is expanded assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey()); @@ -638,7 +644,7 @@ public class BubblesTest extends SysuiTestCase { // Collapse mBubbleController.collapseStack(); assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -658,7 +664,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Notif is suppressed after expansion assertBubbleNotificationSuppressedFromShade(mBubbleEntry); @@ -683,7 +689,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleData.setExpanded(true); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Notif is suppressed after expansion assertBubbleNotificationSuppressedFromShade(mBubbleEntry); @@ -712,7 +718,7 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey()); @@ -743,7 +749,7 @@ public class BubblesTest extends SysuiTestCase { // We should be collapsed verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); assertFalse(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -756,7 +762,7 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); assertStackExpanded(); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey()); @@ -771,7 +777,7 @@ public class BubblesTest extends SysuiTestCase { // We should be collapsed verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey()); assertFalse(mBubbleController.hasBubbles()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @@ -789,7 +795,7 @@ public class BubblesTest extends SysuiTestCase { verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */, mRow.getKey()); assertStackCollapsed(); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -805,7 +811,7 @@ public class BubblesTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */, mRow.getKey()); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -822,7 +828,7 @@ public class BubblesTest extends SysuiTestCase { // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -844,7 +850,7 @@ public class BubblesTest extends SysuiTestCase { // Dot + flyout is hidden because notif is suppressed assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout()); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -1249,11 +1255,11 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); assertTrue(stackView.isManageMenuSettingsVisible()); assertTrue(stackView.isManageMenuDontBubbleVisible()); } @@ -1267,11 +1273,11 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); assertFalse(stackView.isManageMenuSettingsVisible()); assertFalse(stackView.isManageMenuDontBubbleVisible()); } @@ -1285,15 +1291,15 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); // Hide the menu stackView.showManageMenu(false); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -1305,16 +1311,16 @@ public class BubblesTest extends SysuiTestCase { BubbleStackView stackView = mBubbleController.getStackView(); mBubbleData.setExpanded(true); assertStackExpanded(); - assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */); // Show the menu stackView.showManageMenu(true); - assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */); + assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */); // Collapse the stack mBubbleData.setExpanded(false); - assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */); + assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */); } @Test @@ -1576,7 +1582,7 @@ public class BubblesTest extends SysuiTestCase { NotificationListenerService.RankingMap rankingMap = mock(NotificationListenerService.RankingMap.class); - when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() }); + when(rankingMap.getOrderedKeys()).thenReturn(new String[]{mBubbleEntry.getKey()}); mBubbleController.onRankingUpdated(rankingMap, entryDataByKey); // Should no longer be in the stack @@ -1884,7 +1890,111 @@ public class BubblesTest extends SysuiTestCase { ); } - /** Creates a bubble using the userId and package. */ + @Test + public void registerBubbleBarListener_barDisabled_largeScreen_shouldBeIgnored() { + mBubbleProperties.mIsBubbleBarEnabled = false; + mPositioner.setIsLargeScreen(true); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + assertTrue(mBubbleController.hasBubbles()); + + assertStackMode(); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertStackMode(); + + assertThat(mBubbleController.getStackView().getBubbleCount()).isEqualTo(1); + } + + @Test + public void registerBubbleBarListener_barEnabled_smallScreen_shouldBeIgnored() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(false); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + assertTrue(mBubbleController.hasBubbles()); + + assertStackMode(); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertStackMode(); + + assertThat(mBubbleController.getStackView().getBubbleCount()).isEqualTo(1); + } + + @Test + public void registerBubbleBarListener_switchToBarAndBackToStack() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + assertTrue(mBubbleController.hasBubbles()); + + assertStackMode(); + + assertThat(mBubbleData.getBubbles()).hasSize(1); + assertBubbleIsInflatedForStack(mBubbleData.getBubbles().get(0)); + assertBubbleIsInflatedForStack(mBubbleData.getOverflow()); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertBarMode(); + + assertThat(mBubbleData.getBubbles()).hasSize(1); + assertBubbleIsInflatedForBar(mBubbleData.getBubbles().get(0)); + assertBubbleIsInflatedForBar(mBubbleData.getOverflow()); + + mBubbleController.unregisterBubbleStateListener(); + + assertStackMode(); + + assertThat(mBubbleData.getBubbles()).hasSize(1); + assertBubbleIsInflatedForStack(mBubbleData.getBubbles().get(0)); + assertBubbleIsInflatedForStack(mBubbleData.getOverflow()); + } + + @Test + public void switchBetweenBarAndStack_noBubbles_shouldBeIgnored() { + mBubbleProperties.mIsBubbleBarEnabled = false; + mPositioner.setIsLargeScreen(true); + assertFalse(mBubbleController.hasBubbles()); + + assertNoBubbleContainerViews(); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + assertNoBubbleContainerViews(); + + mBubbleController.unregisterBubbleStateListener(); + + assertNoBubbleContainerViews(); + } + + @Test + public void bubbleBarBubbleExpandedAndCollapsed() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mEntryListener.onEntryAdded(mRow); + mBubbleController.updateBubble(mBubbleEntry); + + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), true); + + assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); + + mBubbleController.collapseStack(); + + assertThat(mBubbleController.getLayerView().isExpanded()).isFalse(); + } + + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); NotificationEntry workEntry = new NotificationEntryBuilder() @@ -2000,6 +2110,44 @@ public class BubblesTest extends SysuiTestCase { assertFalse(mBubbleController.getImplCachedState().isStackExpanded()); } + /** Asserts that both the bubble stack and bar views don't exist. */ + private void assertNoBubbleContainerViews() { + assertThat(mBubbleController.getStackView()).isNull(); + assertThat(mBubbleController.getLayerView()).isNull(); + } + + /** Asserts that the stack is created and the bar is null. */ + private void assertStackMode() { + assertThat(mBubbleController.getStackView()).isNotNull(); + assertThat(mBubbleController.getLayerView()).isNull(); + } + + /** Asserts that the given bubble has the stack expanded view inflated. */ + private void assertBubbleIsInflatedForStack(BubbleViewProvider b) { + assertThat(b.getIconView()).isNotNull(); + assertThat(b.getExpandedView()).isNotNull(); + assertThat(b.getBubbleBarExpandedView()).isNull(); + } + + /** Asserts that the bar is created and the stack is null. */ + private void assertBarMode() { + assertThat(mBubbleController.getStackView()).isNull(); + assertThat(mBubbleController.getLayerView()).isNotNull(); + } + + /** Asserts that the given bubble has the bar expanded view inflated. */ + private void assertBubbleIsInflatedForBar(BubbleViewProvider b) { + // the icon view should be inflated for the overflow but not for other bubbles when showing + // in the bar + if (b instanceof Bubble) { + assertThat(b.getIconView()).isNull(); + } else if (b instanceof BubbleOverflow) { + assertThat(b.getIconView()).isNotNull(); + } + assertThat(b.getExpandedView()).isNull(); + assertThat(b.getBubbleBarExpandedView()).isNotNull(); + } + /** * Asserts that a bubble notification is suppressed from the shade and also validates the cached * state is updated. @@ -2029,4 +2177,19 @@ public class BubblesTest extends SysuiTestCase { assertThat(mSysUiStateBubblesExpanded).isEqualTo(stackExpanded); assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded); } + + private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener { + @Override + public void onBubbleStateChange(BubbleBarUpdate update) { + } + } + + private static class FakeBubbleProperties implements BubbleProperties { + boolean mIsBubbleBarEnabled = false; + + @Override + public boolean isBubbleBarEnabled() { + return mIsBubbleBarEnabled; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java index 14c3f3c4ada5..5855347c203b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -31,6 +31,7 @@ import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; import com.android.wm.shell.bubbles.BubbleLogger; import com.android.wm.shell.bubbles.BubblePositioner; +import com.android.wm.shell.bubbles.properties.BubbleProperties; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; @@ -74,13 +75,14 @@ public class TestableBubbleController extends BubbleController { Handler shellMainHandler, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue, - IWindowManager wmService) { + IWindowManager wmService, + BubbleProperties bubbleProperties) { super(context, shellInit, shellCommandHandler, shellController, data, Runnable::run, floatingContentCoordinator, dataRepository, statusBarService, windowManager, windowManagerShellWrapper, userManager, launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController, oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler, - new SyncExecutor(), taskViewTransitions, syncQueue, wmService); + new SyncExecutor(), taskViewTransitions, syncQueue, wmService, bubbleProperties); setInflateSynchronously(true); onInit(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java index 6edc373d2926..047dc65c4a6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java @@ -27,6 +27,7 @@ import com.android.wm.shell.bubbles.BubblePositioner; public class TestableBubblePositioner extends BubblePositioner { private int mMaxBubbles; + private boolean mIsLargeScreen = false; public TestableBubblePositioner(Context context, WindowManager windowManager) { @@ -46,4 +47,13 @@ public class TestableBubblePositioner extends BubblePositioner { public int getMaxBubbles() { return mMaxBubbles; } + + public void setIsLargeScreen(boolean isLargeScreen) { + mIsLargeScreen = isLargeScreen; + } + + @Override + public boolean isLargeScreen() { + return mIsLargeScreen; + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFaceSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFaceSettingsRepository.kt new file mode 100644 index 000000000000..af2706e6b287 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFaceSettingsRepository.kt @@ -0,0 +1,37 @@ +/* + * 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.data.repository + +import kotlinx.coroutines.flow.flowOf + +/** Fake settings for tests. */ +class FakeFaceSettingsRepository : FaceSettingsRepository { + + private val userRepositories = mutableMapOf<Int, FaceUserSettingsRepository>() + + /** Add fixed settings for a user. */ + fun setUserSettings(userId: Int, alwaysRequireConfirmationInApps: Boolean = false) { + userRepositories[userId] = + object : FaceUserSettingsRepository { + override val userId = userId + override val alwaysRequireConfirmationInApps = + flowOf(alwaysRequireConfirmationInApps) + } + } + + override fun forUser(id: Int?) = userRepositories[id] ?: FaceUserSettingsRepositoryImpl.Empty +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt index d270700aa856..42ec8fed0127 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt @@ -31,13 +31,20 @@ class FakePromptRepository : PromptRepository { userId: Int, gatekeeperChallenge: Long?, kind: PromptKind, - requireConfirmation: Boolean, + ) = setPrompt(promptInfo, userId, gatekeeperChallenge, kind, forceConfirmation = false) + + fun setPrompt( + promptInfo: PromptInfo, + userId: Int, + gatekeeperChallenge: Long?, + kind: PromptKind, + forceConfirmation: Boolean = false, ) { _promptInfo.value = promptInfo _userId.value = userId _challenge.value = gatekeeperChallenge _kind.value = kind - _isConfirmationRequired.value = requireConfirmation + _isConfirmationRequired.value = promptInfo.isConfirmationRequested || forceConfirmation } override fun unsetPrompt() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt index af940e4fa687..f19e19113b30 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt @@ -18,6 +18,7 @@ package com.android.systemui.broadcast import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent import android.content.IntentFilter import android.os.Handler import android.os.Looper @@ -31,6 +32,14 @@ import java.lang.IllegalStateException import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executor +/** + * A fake instance of [BroadcastDispatcher] for tests. + * + * Important: The *real* broadcast dispatcher will only send intents to receivers if the intent + * matches the [IntentFilter] that the [BroadcastReceiver] was registered with. This fake class does + * *not* do that matching by default. Use [sendIntentToMatchingReceiversOnly] to get the same + * matching behavior as the real broadcast dispatcher. + */ class FakeBroadcastDispatcher( context: SysuiTestableContext, mainExecutor: Executor, @@ -52,7 +61,10 @@ class FakeBroadcastDispatcher( PendingRemovalStore(logger) ) { - val registeredReceivers: MutableSet<BroadcastReceiver> = ConcurrentHashMap.newKeySet() + private val receivers: MutableSet<InternalReceiver> = ConcurrentHashMap.newKeySet() + + val registeredReceivers: Set<BroadcastReceiver> + get() = receivers.map { it.receiver }.toSet() override fun registerReceiverWithHandler( receiver: BroadcastReceiver, @@ -62,7 +74,7 @@ class FakeBroadcastDispatcher( @Context.RegisterReceiverFlags flags: Int, permission: String? ) { - registeredReceivers.add(receiver) + receivers.add(InternalReceiver(receiver, filter)) } override fun registerReceiver( @@ -73,15 +85,34 @@ class FakeBroadcastDispatcher( @Context.RegisterReceiverFlags flags: Int, permission: String? ) { - registeredReceivers.add(receiver) + receivers.add(InternalReceiver(receiver, filter)) } override fun unregisterReceiver(receiver: BroadcastReceiver) { - registeredReceivers.remove(receiver) + receivers.removeIf { it.receiver == receiver } } override fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) { - registeredReceivers.remove(receiver) + receivers.removeIf { it.receiver == receiver } + } + + /** + * Sends the given [intent] to *only* the receivers that were registered with an [IntentFilter] + * that matches the intent. + */ + fun sendIntentToMatchingReceiversOnly(context: Context, intent: Intent) { + receivers.forEach { + if ( + it.filter.match( + context.contentResolver, + intent, + /* resolve= */ false, + /* logTag= */ "FakeBroadcastDispatcher", + ) > 0 + ) { + it.receiver.onReceive(context, intent) + } + } } fun cleanUpReceivers(testName: String) { @@ -91,6 +122,11 @@ class FakeBroadcastDispatcher( throw IllegalStateException("Receiver not unregistered from dispatcher: $it") } } - registeredReceivers.clear() + receivers.clear() } + + private data class InternalReceiver( + val receiver: BroadcastReceiver, + val filter: IntentFilter, + ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 1b7542b42128..9c4fd9459b57 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -24,9 +24,9 @@ import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor -import com.android.systemui.scene.data.model.SceneContainerConfig import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -51,7 +51,7 @@ class SceneTestUtils( fakeSceneContainerConfig(CONTAINER_2), ) ): SceneContainerRepository { - return SceneContainerRepository(containerConfigurations) + return SceneContainerRepository(containerConfigurations.associateBy { it.name }) } fun fakeSceneKeys(): List<SceneKey> { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt index fbc2381c3031..61e5b5fc27ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt @@ -79,6 +79,10 @@ class FakeUserRepository : UserRepository { return _userSwitcherSettings.value.isSimpleUserSwitcher } + override fun isUserSwitcherEnabled(): Boolean { + return _userSwitcherSettings.value.isUserSwitcherEnabled + } + fun setUserInfos(infos: List<UserInfo>) { _userInfos.value = infos } diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 308e2cf01787..3406102b28ac 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -359,11 +359,7 @@ public class WallpaperBackupAgent extends BackupAgent { final File infoStage = new File(filesDir, WALLPAPER_INFO_STAGE); final File imageStage = new File(filesDir, SYSTEM_WALLPAPER_STAGE); final File lockImageStage = new File(filesDir, LOCK_WALLPAPER_STAGE); - - // If we restored separate lock imagery, the system wallpaper should be - // applied as system-only; but if there's no separate lock image, make - // sure to apply the restored system wallpaper as both. - final int sysWhich = FLAG_SYSTEM | (lockImageStage.exists() ? 0 : FLAG_LOCK); + boolean lockImageStageExists = lockImageStage.exists(); try { // First parse the live component name so that we know for logging if we care about @@ -371,14 +367,31 @@ public class WallpaperBackupAgent extends BackupAgent { ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); mSystemHasLiveComponent = wpService != null; + ComponentName kwpService = null; + boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled(); + if (lockscreenLiveWallpaper) { + kwpService = parseWallpaperComponent(infoStage, "kwp"); + } + mLockHasLiveComponent = kwpService != null; + boolean separateLockWallpaper = mLockHasLiveComponent || lockImageStage.exists(); + + // if there's no separate lock wallpaper, apply the system wallpaper to both screens. + final int sysWhich = separateLockWallpaper ? FLAG_SYSTEM : FLAG_SYSTEM | FLAG_LOCK; + // It is valid for the imagery to be absent; it means that we were not permitted // to back up the original image on the source device, or there was no user-supplied // wallpaper image present. - restoreFromStage(imageStage, infoStage, "wp", sysWhich); - restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); + if (!lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich); + if (lockImageStageExists) { + restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); + } + if (lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich); // And reset to the wallpaper service we should be using - updateWallpaperComponent(wpService, !lockImageStage.exists()); + if (lockscreenLiveWallpaper && mLockHasLiveComponent) { + updateWallpaperComponent(kwpService, false, FLAG_LOCK); + } + updateWallpaperComponent(wpService, !lockImageStageExists, sysWhich); } catch (Exception e) { Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage()); mEventLogger.onRestoreException(e); @@ -397,9 +410,21 @@ public class WallpaperBackupAgent extends BackupAgent { } @VisibleForTesting - void updateWallpaperComponent(ComponentName wpService, boolean applyToLock) throws IOException { + void updateWallpaperComponent(ComponentName wpService, boolean applyToLock, int which) + throws IOException { + boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled(); if (servicePackageExists(wpService)) { Slog.i(TAG, "Using wallpaper service " + wpService); + if (lockscreenLiveWallpaper) { + mWallpaperManager.setWallpaperComponentWithFlags(wpService, which); + if ((which & FLAG_LOCK) != 0) { + mEventLogger.onLockLiveWallpaperRestored(wpService); + } + if ((which & FLAG_SYSTEM) != 0) { + mEventLogger.onSystemLiveWallpaperRestored(wpService); + } + return; + } mWallpaperManager.setWallpaperComponent(wpService); if (applyToLock) { // We have a live wallpaper and no static lock image, @@ -414,7 +439,7 @@ public class WallpaperBackupAgent extends BackupAgent { // in reports from users if (wpService != null) { // TODO(b/268471749): Handle delayed case - applyComponentAtInstall(wpService, applyToLock); + applyComponentAtInstall(wpService, applyToLock, which); Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. " + " Will try to apply later"); } @@ -437,7 +462,8 @@ public class WallpaperBackupAgent extends BackupAgent { // And log the success if ((which & FLAG_SYSTEM) > 0) { mEventLogger.onSystemImageWallpaperRestored(); - } else { + } + if ((which & FLAG_LOCK) > 0) { mEventLogger.onLockImageWallpaperRestored(); } } @@ -460,7 +486,8 @@ public class WallpaperBackupAgent extends BackupAgent { private void logRestoreError(int which, String error) { if ((which & FLAG_SYSTEM) == FLAG_SYSTEM) { mEventLogger.onSystemImageWallpaperRestoreFailed(error); - } else if ((which & FLAG_LOCK) == FLAG_LOCK) { + } + if ((which & FLAG_LOCK) == FLAG_LOCK) { mEventLogger.onLockImageWallpaperRestoreFailed(error); } } @@ -552,16 +579,21 @@ public class WallpaperBackupAgent extends BackupAgent { // Intentionally blank } - private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock) { - PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, applyToLock); + private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock, + int which) { + PackageMonitor packageMonitor = getWallpaperPackageMonitor( + componentName, applyToLock, which); packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true); } @VisibleForTesting - PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock) { + PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock, + int which) { return new PackageMonitor() { @Override public void onPackageAdded(String packageName, int uid) { + boolean lockscreenLiveWallpaper = + mWallpaperManager.isLockscreenLiveWallpaperEnabled(); if (!isDeviceInRestore()) { // We don't want to reapply the wallpaper outside a restore. unregister(); @@ -582,16 +614,29 @@ public class WallpaperBackupAgent extends BackupAgent { if (componentName.getPackageName().equals(packageName)) { Slog.d(TAG, "Applying component " + componentName); - boolean sysResult = mWallpaperManager.setWallpaperComponent(componentName); + boolean success = lockscreenLiveWallpaper + ? mWallpaperManager.setWallpaperComponentWithFlags(componentName, which) + : mWallpaperManager.setWallpaperComponent(componentName); WallpaperEventLogger logger = new WallpaperEventLogger( mBackupManager.getDelayedRestoreLogger()); - if (sysResult) { - logger.onSystemLiveWallpaperRestored(componentName); + if (success) { + if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) { + logger.onSystemLiveWallpaperRestored(componentName); + } + if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) { + logger.onLockLiveWallpaperRestored(componentName); + } } else { - logger.onSystemLiveWallpaperRestoreFailed( - WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); + if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) { + logger.onSystemLiveWallpaperRestoreFailed( + WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); + } + if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) { + logger.onLockLiveWallpaperRestoreFailed( + WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); + } } - if (applyToLock) { + if (applyToLock && !lockscreenLiveWallpaper) { try { mWallpaperManager.clear(FLAG_LOCK); logger.onLockLiveWallpaperRestored(componentName); diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java index 9b07ad41ef9a..dc1126edde41 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java @@ -117,6 +117,7 @@ public class WallpaperBackupAgentTest { public void setUp() { MockitoAnnotations.initMocks(this); + when(mWallpaperManager.isLockscreenLiveWallpaperEnabled()).thenReturn(true); when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_SYSTEM))).thenReturn(true); when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_LOCK))).thenReturn(true); @@ -364,14 +365,23 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = true; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); - - verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); - verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK)); + if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) { + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK); + verify(mWallpaperManager, never()).clear(anyInt()); + } else { + verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK)); + } } @Test @@ -380,14 +390,24 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = true; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ false); + /* applyToLock */ false, FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); - verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); - verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK)); + if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) { + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()).clear(anyInt()); + } else { + verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK)); + } } @Test @@ -396,7 +416,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = false; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, @@ -412,7 +432,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = false; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate "wrong" wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(/* packageName */"", @@ -614,6 +634,13 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); assertThat(result).isNotNull(); assertThat(result.getSuccessCount()).isEqualTo(1); + + if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) { + result = getLoggingResult(WALLPAPER_IMG_LOCK, + mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); + assertThat(result).isNotNull(); + assertThat(result.getSuccessCount()).isEqualTo(1); + } } @Test @@ -649,19 +676,20 @@ public class WallpaperBackupAgentTest { } @Test - public void testOnRestore_lockWallpaperImgMissingAndNoLive_logsFailure() throws Exception { + public void testOnRestore_wallpaperImgMissingAndNoLive_logsFailure() throws Exception { mockStagedWallpaperFile(WALLPAPER_INFO_STAGE); - mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE); mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD, BackupAnnotations.OperationType.RESTORE); mWallpaperBackupAgent.onRestoreFinished(); - DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK, - mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); - assertThat(result).isNotNull(); - assertThat(result.getFailCount()).isEqualTo(1); - assertThat(result.getErrors()).containsKey(ERROR_NO_WALLPAPER); + for (String wallpaper: List.of(WALLPAPER_IMG_LOCK, WALLPAPER_IMG_SYSTEM)) { + DataTypeResult result = getLoggingResult(wallpaper, + mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); + assertThat(result).isNotNull(); + assertThat(result.getFailCount()).isEqualTo(1); + assertThat(result.getErrors()).containsKey(ERROR_NO_WALLPAPER); + } } @Test @@ -722,13 +750,15 @@ public class WallpaperBackupAgentTest { public void testUpdateWallpaperComponent_delayedRestore_logsSuccess() throws Exception { mWallpaperBackupAgent.mIsDeviceInRestore = true; when(mWallpaperManager.setWallpaperComponent(any())).thenReturn(true); + when(mWallpaperManager.setWallpaperComponentWithFlags(any(), eq(FLAG_LOCK | FLAG_SYSTEM))) + .thenReturn(true); BackupRestoreEventLogger logger = new BackupRestoreEventLogger( BackupAnnotations.OperationType.RESTORE); when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger); mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); @@ -752,7 +782,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); @@ -774,7 +804,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, @@ -909,8 +939,9 @@ public class WallpaperBackupAgentTest { @Override PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, - boolean applyToLock) { - mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, applyToLock); + boolean applyToLock, int which) { + mWallpaperPackageMonitor = super.getWallpaperPackageMonitor( + componentName, applyToLock, which); return mWallpaperPackageMonitor; } diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index ac770439a524..82af38200166 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -46,6 +46,7 @@ import com.android.internal.util.ArrayUtils; import com.android.server.utils.Slogf; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -319,6 +320,13 @@ public final class Helper { return context.createDisplayContext(display); } + static <T> @Nullable T weakDeref(WeakReference<T> weakRef, String tag, String prefix) { + T deref = weakRef.get(); + if (deref == null) { + Slog.wtf(tag, prefix + "fail to deref " + weakRef); + } + return deref; + } private interface ViewNodeFilter { boolean matches(ViewNode node); diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java index ea31074ff5d2..bcca0069eb34 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java @@ -44,6 +44,8 @@ import android.util.Slog; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.ServiceConnector; +import java.lang.ref.WeakReference; + /** * Class responsible for connection with the Remote {@link FieldClassificationService}. * This class is instantiated when {@link AutofillManagerServiceImpl} is established. @@ -133,7 +135,8 @@ final class RemoteFieldClassificationService } public void onFieldClassificationRequest(@NonNull FieldClassificationRequest request, - FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks) { + WeakReference<FieldClassificationServiceCallbacks> + fieldClassificationServiceCallbacksWeakRef) { final long startTime = SystemClock.elapsedRealtime(); if (sVerbose) { Slog.v(TAG, "onFieldClassificationRequest request:" + request); @@ -170,6 +173,15 @@ final class RemoteFieldClassificationService Slog.d(TAG, "onSuccess " + msg); } } + FieldClassificationServiceCallbacks + fieldClassificationServiceCallbacks = + Helper.weakDeref( + fieldClassificationServiceCallbacksWeakRef, + TAG, "onSuccess " + ); + if (fieldClassificationServiceCallbacks == null) { + return; + } fieldClassificationServiceCallbacks .onClassificationRequestSuccess(response); } @@ -180,6 +192,15 @@ final class RemoteFieldClassificationService if (sDebug) { Slog.d(TAG, "onFailure"); } + FieldClassificationServiceCallbacks + fieldClassificationServiceCallbacks = + Helper.weakDeref( + fieldClassificationServiceCallbacksWeakRef, + TAG, "onFailure " + ); + if (fieldClassificationServiceCallbacks == null) { + return; + } fieldClassificationServiceCallbacks .onClassificationRequestFailure(0, null); } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 0c3f8667f4f8..fb26f427dad7 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -850,8 +850,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState RemoteFieldClassificationService remoteFieldClassificationService = mService.getRemoteFieldClassificationServiceLocked(); if (remoteFieldClassificationService != null) { + WeakReference<RemoteFieldClassificationService.FieldClassificationServiceCallbacks> + fieldClassificationServiceCallbacksWeakRef = + new WeakReference<>(Session.this); remoteFieldClassificationService.onFieldClassificationRequest( - mClassificationState.mPendingFieldClassificationRequest, Session.this); + mClassificationState.mPendingFieldClassificationRequest, + fieldClassificationServiceCallbacksWeakRef); } mClassificationState.onFieldClassificationRequestSent(); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index a1ccade51214..611541f671cf 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -64,6 +64,7 @@ import android.companion.IOnAssociationsChangedListener; import android.companion.IOnMessageReceivedListener; import android.companion.IOnTransportsChangedListener; import android.companion.ISystemDataTransferCallback; +import android.companion.utils.FeatureUtils; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; @@ -746,6 +747,11 @@ public class CompanionDeviceManagerService extends SystemService { @Override public PendingIntent buildPermissionTransferUserConsentIntent(String packageName, int userId, int associationId) { + if (!FeatureUtils.isPermSyncEnabled()) { + throw new UnsupportedOperationException("Calling" + + " buildPermissionTransferUserConsentIntent, but this API is disabled by" + + " the system."); + } return mSystemDataTransferProcessor.buildPermissionTransferUserConsentIntent( packageName, userId, associationId); } @@ -753,6 +759,10 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void startSystemDataTransfer(String packageName, int userId, int associationId, ISystemDataTransferCallback callback) { + if (!FeatureUtils.isPermSyncEnabled()) { + throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this" + + " API is disabled by the system."); + } mSystemDataTransferProcessor.startSystemDataTransfer(packageName, userId, associationId, callback); } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java index fac1c8981061..3187de548ea7 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java @@ -175,7 +175,8 @@ public class CallMetadataSyncConnectionService extends ConnectionService { : handle.getComponentName().getShortClassName(), phoneAccount != null ? phoneAccount.getExtras().getString( CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID) - : handle.getComponentName().getPackageName()); + : handle.getComponentName().getPackageName(), + handle.getComponentName().flattenToString()); call.setFacilitator(callFacilitator); call.setDirection(android.companion.Telecom.Call.OUTGOING); call.setCallerId(connectionRequest.getAddress().getSchemeSpecificPart()); diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java index 74641a48c4dd..08811ba30d8d 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java @@ -75,13 +75,15 @@ class CallMetadataSyncData { public static class CallFacilitator { private String mName; private String mIdentifier; + private String mExtendedIdentifier; private boolean mIsTel; CallFacilitator() {} - CallFacilitator(String name, String identifier) { + CallFacilitator(String name, String identifier, String extendedIdentifier) { mName = name; mIdentifier = identifier; + mExtendedIdentifier = extendedIdentifier; } public String getName() { @@ -92,6 +94,10 @@ class CallMetadataSyncData { return mIdentifier; } + public String getExtendedIdentifier() { + return mExtendedIdentifier; + } + public boolean isTel() { return mIsTel; } @@ -104,6 +110,10 @@ class CallMetadataSyncData { mIdentifier = identifier; } + public void setExtendedIdentifier(String extendedIdentifier) { + mExtendedIdentifier = extendedIdentifier; + } + public void setIsTel(boolean isTel) { mIsTel = isTel; } @@ -170,6 +180,8 @@ class CallMetadataSyncData { "com.android.server.companion.datatransfer.contextsync.extra.FACILITATOR_NAME"; private static final String EXTRA_FACILITATOR_ID = "com.android.server.companion.datatransfer.contextsync.extra.FACILITATOR_ID"; + private static final String EXTRA_FACILITATOR_EXT_ID = + "com.android.server.companion.datatransfer.contextsync.extra.FACILITATOR_EXT_ID"; private static final String EXTRA_STATUS = "com.android.server.companion.datatransfer.contextsync.extra.STATUS"; private static final String EXTRA_DIRECTION = @@ -192,7 +204,10 @@ class CallMetadataSyncData { call.setAppIcon(bundle.getByteArray(EXTRA_APP_ICON)); final String facilitatorName = bundle.getString(EXTRA_FACILITATOR_NAME); final String facilitatorIdentifier = bundle.getString(EXTRA_FACILITATOR_ID); - call.setFacilitator(new CallFacilitator(facilitatorName, facilitatorIdentifier)); + final String facilitatorExtendedIdentifier = + bundle.getString(EXTRA_FACILITATOR_EXT_ID); + call.setFacilitator(new CallFacilitator(facilitatorName, facilitatorIdentifier, + facilitatorExtendedIdentifier)); call.setStatus(bundle.getInt(EXTRA_STATUS)); call.setDirection(bundle.getInt(EXTRA_DIRECTION)); call.setControls(new HashSet<>(bundle.getIntegerArrayList(EXTRA_CONTROLS))); @@ -207,6 +222,7 @@ class CallMetadataSyncData { bundle.putByteArray(EXTRA_APP_ICON, mAppIcon); bundle.putString(EXTRA_FACILITATOR_NAME, mFacilitator.getName()); bundle.putString(EXTRA_FACILITATOR_ID, mFacilitator.getIdentifier()); + bundle.putString(EXTRA_FACILITATOR_EXT_ID, mFacilitator.getExtendedIdentifier()); bundle.putInt(EXTRA_STATUS, mStatus); bundle.putInt(EXTRA_DIRECTION, mDirection); bundle.putIntegerArrayList(EXTRA_CONTROLS, new ArrayList<>(mControls)); diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java index e8392d29365e..2a507990b0d5 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java @@ -48,6 +48,7 @@ public class CrossDeviceCall { private final int mUserId; @VisibleForTesting boolean mIsEnterprise; private final String mCallingAppPackageName; + private final String mSerializedPhoneAccountHandle; private String mCallingAppName; private byte[] mCallingAppIcon; private String mCallerDisplayName; @@ -89,6 +90,8 @@ public class CrossDeviceCall { .equals(handle.getComponentName()); mCallingAppPackageName = handle != null ? callDetails.getAccountHandle().getComponentName().getPackageName() : ""; + mSerializedPhoneAccountHandle = handle != null + ? handle.getId() + SEPARATOR + handle.getComponentName().flattenToString() : ""; mIsEnterprise = (callDetails.getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL) == Call.Details.PROPERTY_ENTERPRISE_CALL; final PackageManager packageManager = context.getPackageManager(); @@ -247,6 +250,10 @@ public class CrossDeviceCall { return mCallingAppPackageName; } + public String getSerializedPhoneAccountHandle() { + return mSerializedPhoneAccountHandle; + } + /** * Get a human-readable "caller id" to display as the origin of the call. * diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java index 31694590a930..9bd5af956a6e 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java @@ -280,7 +280,7 @@ public class CrossDeviceSyncController { mCallFacilitators.add( new CallMetadataSyncData.CallFacilitator( defaultOutgoingTelAccount.getLabel().toString(), - FACILITATOR_ID_SYSTEM)); + FACILITATOR_ID_SYSTEM, FACILITATOR_ID_SYSTEM)); } } } @@ -574,6 +574,10 @@ public class CrossDeviceSyncController { case (int) Telecom.CallFacilitator.IDENTIFIER: facilitator.setIdentifier(pis.readString(Telecom.CallFacilitator.IDENTIFIER)); break; + case (int) Telecom.CallFacilitator.EXTENDED_IDENTIFIER: + facilitator.setExtendedIdentifier( + pis.readString(Telecom.CallFacilitator.EXTENDED_IDENTIFIER)); + break; default: Slog.e(TAG, "Unhandled field in Facilitator:" + ProtoUtils.currentFieldToString(pis)); @@ -649,6 +653,8 @@ public class CrossDeviceSyncController { final long facilitatorToken = pos.start(Telecom.Call.Origin.FACILITATOR); pos.write(Telecom.CallFacilitator.NAME, call.getCallingAppName()); pos.write(Telecom.CallFacilitator.IDENTIFIER, call.getCallingAppPackageName()); + pos.write(Telecom.CallFacilitator.EXTENDED_IDENTIFIER, + call.getSerializedPhoneAccountHandle()); pos.end(facilitatorToken); pos.end(originToken); pos.write(Telecom.Call.STATUS, call.getStatus()); @@ -662,6 +668,8 @@ public class CrossDeviceSyncController { final long facilitatorsToken = pos.start(Telecom.FACILITATORS); pos.write(Telecom.CallFacilitator.NAME, facilitator.getName()); pos.write(Telecom.CallFacilitator.IDENTIFIER, facilitator.getIdentifier()); + pos.write(Telecom.CallFacilitator.EXTENDED_IDENTIFIER, + facilitator.getExtendedIdentifier()); pos.end(facilitatorsToken); } pos.end(telecomToken); diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java index 5a3db4b18a1a..3cb9ac823bac 100644 --- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java @@ -130,6 +130,7 @@ public class SecureChannel { if (DEBUG) { Slog.d(TAG, "Starting secure channel."); } + mStopped = false; new Thread(() -> { try { // 1. Wait for the next handshake message and process it. @@ -185,6 +186,17 @@ public class SecureChannel { } /** + * Return true if the channel is currently inactive. + * The channel could have been stopped by either {@link SecureChannel#stop()} or by + * encountering a fatal error. + * + * @return true if the channel is currently inactive. + */ + public boolean isStopped() { + return mStopped; + } + + /** * Start exchanging handshakes to create a secure layer asynchronously. When the handshake is * completed successfully, then the {@link Callback#onSecureConnection()} will trigger. Any * error that occurs during the handshake will be passed by {@link Callback#onError(Throwable)}. @@ -290,6 +302,7 @@ public class SecureChannel { try { data = new byte[length]; } catch (OutOfMemoryError error) { + Streams.skipByReading(mInput, Long.MAX_VALUE); throw new SecureChannelException("Payload is too large.", error); } diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java index 0f00f5f1c3a5..a49021a8eec4 100644 --- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java @@ -22,14 +22,10 @@ import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_P import android.annotation.NonNull; import android.annotation.SuppressLint; -import android.app.ActivityManagerInternal; import android.companion.AssociationInfo; import android.companion.IOnMessageReceivedListener; import android.companion.IOnTransportsChangedListener; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Binder; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.RemoteCallbackList; @@ -38,7 +34,6 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.server.LocalServices; import com.android.server.companion.AssociationStore; import java.io.FileDescriptor; @@ -137,44 +132,15 @@ public class CompanionTransportManager { synchronized (mTransports) { for (int i = 0; i < associationIds.length; i++) { if (mTransports.contains(associationIds[i])) { - try { - mTransports.get(associationIds[i]).sendMessage(message, data); - } catch (IOException e) { - Slog.e(TAG, "Failed to send message 0x" + Integer.toHexString(message) - + " data length " + data.length + " to association " - + associationIds[i]); - } + mTransports.get(associationIds[i]).requestForResponse(message, data); } } } } - /** - * For the moment, we only offer transporting of system data to built-in - * companion apps; future work will improve the security model to support - * third-party companion apps. - */ - private void enforceCallerCanTransportSystemData(String packageName, int userId) { - mContext.enforceCallingOrSelfPermission(DELIVER_COMPANION_MESSAGES, TAG); - - try { - final ApplicationInfo info = mContext.getPackageManager().getApplicationInfoAsUser( - packageName, 0, userId); - final int instrumentationUid = LocalServices.getService(ActivityManagerInternal.class) - .getInstrumentationSourceUid(Binder.getCallingUid()); - if (!Build.isDebuggable() && !info.isSystemApp() - && instrumentationUid == android.os.Process.INVALID_UID) { - throw new SecurityException("Transporting of system data currently only available " - + "to built-in companion apps or tests"); - } - } catch (NameNotFoundException e) { - throw new IllegalArgumentException(e); - } - } - public void attachSystemDataTransport(String packageName, int userId, int associationId, ParcelFileDescriptor fd) { - enforceCallerCanTransportSystemData(packageName, userId); + mContext.enforceCallingOrSelfPermission(DELIVER_COMPANION_MESSAGES, TAG); synchronized (mTransports) { if (mTransports.contains(associationId)) { detachSystemDataTransport(packageName, userId, associationId); @@ -188,7 +154,7 @@ public class CompanionTransportManager { } public void detachSystemDataTransport(String packageName, int userId, int associationId) { - enforceCallerCanTransportSystemData(packageName, userId); + mContext.enforceCallingOrSelfPermission(DELIVER_COMPANION_MESSAGES, TAG); synchronized (mTransports) { final Transport transport = mTransports.get(associationId); if (transport != null) { @@ -244,6 +210,7 @@ public class CompanionTransportManager { } addMessageListenersToTransport(transport); + transport.setOnTransportClosedListener(this::detachSystemDataTransport); transport.start(); synchronized (mTransports) { mTransports.put(associationId, transport); @@ -321,4 +288,14 @@ public class CompanionTransportManager { transport.addListener(mMessageListeners.keyAt(i), mMessageListeners.valueAt(i)); } } + + void detachSystemDataTransport(Transport transport) { + int associationId = transport.mAssociationId; + AssociationInfo association = mAssociationStore.getAssociationById(associationId); + if (association != null) { + detachSystemDataTransport(association.getPackageName(), + association.getUserId(), + association.getId()); + } + } } diff --git a/services/companion/java/com/android/server/companion/transport/RawTransport.java b/services/companion/java/com/android/server/companion/transport/RawTransport.java index e64509facbb4..ca169aac6a37 100644 --- a/services/companion/java/com/android/server/companion/transport/RawTransport.java +++ b/services/companion/java/com/android/server/companion/transport/RawTransport.java @@ -70,6 +70,8 @@ class RawTransport extends Transport { } IoUtils.closeQuietly(mRemoteIn); IoUtils.closeQuietly(mRemoteOut); + + super.close(); } @Override diff --git a/services/companion/java/com/android/server/companion/transport/SecureTransport.java b/services/companion/java/com/android/server/companion/transport/SecureTransport.java index 2d856b9614cb..a0301a920d96 100644 --- a/services/companion/java/com/android/server/companion/transport/SecureTransport.java +++ b/services/companion/java/com/android/server/companion/transport/SecureTransport.java @@ -21,7 +21,6 @@ import android.content.Context; import android.os.ParcelFileDescriptor; import android.util.Slog; -import com.android.internal.annotations.GuardedBy; import com.android.server.companion.securechannel.AttestationVerifier; import com.android.server.companion.securechannel.SecureChannel; @@ -35,7 +34,6 @@ class SecureTransport extends Transport implements SecureChannel.Callback { private volatile boolean mShouldProcessRequests = false; - @GuardedBy("mRequestQueue") private final BlockingQueue<byte[]> mRequestQueue = new ArrayBlockingQueue<>(100); SecureTransport(int associationId, ParcelFileDescriptor fd, Context context) { @@ -64,6 +62,8 @@ class SecureTransport extends Transport implements SecureChannel.Callback { void close() { mSecureChannel.close(); mShouldProcessRequests = false; + + super.close(); } @Override @@ -81,13 +81,19 @@ class SecureTransport extends Transport implements SecureChannel.Callback { } // Queue up a message to send - synchronized (mRequestQueue) { + try { mRequestQueue.add(ByteBuffer.allocate(HEADER_LENGTH + data.length) .putInt(message) .putInt(sequence) .putInt(data.length) .put(data) .array()); + } catch (IllegalStateException e) { + // Request buffer can only be full if too many requests are being added or + // the request processing thread is dead. Assume latter and detach the transport. + Slog.w(TAG, "Failed to queue message 0x" + Integer.toHexString(message) + + " . Request buffer is full; detaching transport.", e); + close(); } } @@ -96,8 +102,8 @@ class SecureTransport extends Transport implements SecureChannel.Callback { try { mSecureChannel.establishSecureConnection(); } catch (Exception e) { - Slog.w(TAG, "Failed to initiate secure channel handshake.", e); - onError(e); + Slog.e(TAG, "Failed to initiate secure channel handshake.", e); + close(); } } @@ -108,17 +114,14 @@ class SecureTransport extends Transport implements SecureChannel.Callback { // TODO: find a better way to handle incoming requests than a dedicated thread. new Thread(() -> { - try { - while (mShouldProcessRequests) { - synchronized (mRequestQueue) { - byte[] request = mRequestQueue.poll(); - if (request != null) { - mSecureChannel.sendSecureMessage(request); - } - } + while (mShouldProcessRequests) { + try { + byte[] request = mRequestQueue.take(); + mSecureChannel.sendSecureMessage(request); + } catch (Exception e) { + Slog.e(TAG, "Failed to send secure message.", e); + close(); } - } catch (IOException e) { - onError(e); } }).start(); } @@ -135,13 +138,18 @@ class SecureTransport extends Transport implements SecureChannel.Callback { try { handleMessage(message, sequence, content); } catch (IOException error) { - onError(error); + // IOException won't be thrown here because a separate thread is handling + // the write operations inside onSecureConnection(). } } @Override public void onError(Throwable error) { - mShouldProcessRequests = false; - Slog.e(TAG, error.getMessage(), error); + Slog.e(TAG, "Secure transport encountered an error.", error); + + // If the channel was stopped as a result of the error, then detach itself. + if (mSecureChannel.isStopped()) { + close(); + } } } diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java index 6ad6d3a4aa72..5af3b98d71cc 100644 --- a/services/companion/java/com/android/server/companion/transport/Transport.java +++ b/services/companion/java/com/android/server/companion/transport/Transport.java @@ -19,7 +19,6 @@ package com.android.server.companion.transport; import android.annotation.NonNull; import android.companion.IOnMessageReceivedListener; import android.content.Context; -import android.content.pm.PackageManager; import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -70,6 +69,8 @@ public abstract class Transport { */ private final Map<Integer, IOnMessageReceivedListener> mListeners; + private OnTransportClosedListener mOnTransportClosed; + private static boolean isRequest(int message) { return (message & 0xFF000000) == 0x63000000; } @@ -120,20 +121,18 @@ public abstract class Transport { abstract void stop(); /** - * Stop listening to the incoming data and close the streams. + * Stop listening to the incoming data and close the streams. If a listener for closed event + * is set, then trigger it to assist with its clean-up. */ - abstract void close(); + void close() { + if (mOnTransportClosed != null) { + mOnTransportClosed.onClosed(this); + } + } protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data) throws IOException; - /** - * Send a message. - */ - public void sendMessage(int message, @NonNull byte[] data) throws IOException { - sendMessage(message, mNextSequence.incrementAndGet(), data); - } - public Future<byte[]> requestForResponse(int message, byte[] data) { if (DEBUG) Slog.d(TAG, "Requesting for response"); final int sequence = mNextSequence.incrementAndGet(); @@ -188,12 +187,6 @@ public abstract class Transport { break; } case MESSAGE_REQUEST_PERMISSION_RESTORE: { - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH) - && !Build.isDebuggable()) { - Slog.w(TAG, "Restoring permissions only supported on watches"); - sendMessage(MESSAGE_RESPONSE_FAILURE, sequence, EmptyArray.BYTE); - break; - } try { callback(message, data); sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE); @@ -247,4 +240,14 @@ public abstract class Transport { } } } + + void setOnTransportClosedListener(OnTransportClosedListener callback) { + this.mOnTransportClosed = callback; + } + + // Interface to pass transport to the transport manager to assist with detachment. + @FunctionalInterface + interface OnTransportClosedListener { + void onClosed(Transport transport); + } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index fc84e1386d1f..c1239d53058c 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -406,6 +406,8 @@ public final class ActiveServices { // allowlisted packageName. ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>(); + String mCachedDeviceProvisioningPackage; + // TODO: remove this after feature development is done private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -480,7 +482,8 @@ public final class ActiveServices { // (REASON_ALARM_MANAGER_ALARM_CLOCK), allow it to continue and do not stop it, // even the app is background-restricted. if (r.isForeground - && r.mAllowStartForegroundAtEntering != REASON_ALARM_MANAGER_ALARM_CLOCK) { + && r.mAllowStartForegroundAtEntering != REASON_ALARM_MANAGER_ALARM_CLOCK + && !isDeviceProvisioningPackage(r.packageName)) { toStop.add(r); } } @@ -880,7 +883,8 @@ public final class ActiveServices { boolean forcedStandby = false; if (bgLaunch && appRestrictedAnyInBackground(appUid, appPackageName) - && !isTempAllowedByAlarmClock(appUid)) { + && !isTempAllowedByAlarmClock(appUid) + && !isDeviceProvisioningPackage(appPackageName)) { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG, "Forcing bg-only service start only for " + r.shortInstanceName + " : bgLaunch=" + bgLaunch + " callerFg=" + callerFg); @@ -1926,6 +1930,9 @@ public final class ActiveServices { */ private boolean isForegroundServiceAllowedInBackgroundRestricted(ProcessRecord app) { final ProcessStateRecord state = app.mState; + if (isDeviceProvisioningPackage(app.info.packageName)) { + return true; + } if (!state.isBackgroundRestricted() || state.getSetProcState() <= ActivityManager.PROCESS_STATE_BOUND_TOP) { return true; @@ -8404,4 +8411,13 @@ public final class ActiveServices { } return results; } + + private boolean isDeviceProvisioningPackage(String packageName) { + if (mCachedDeviceProvisioningPackage == null) { + mCachedDeviceProvisioningPackage = mAm.mContext.getResources().getString( + com.android.internal.R.string.config_deviceProvisioningPackage); + } + return mCachedDeviceProvisioningPackage != null + && mCachedDeviceProvisioningPackage.equals(packageName); + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 8c31209aeeb4..ae24f1e05851 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -1347,7 +1347,7 @@ final class ActivityManagerConstants extends ContentObserver { updateForegroundServiceStartsLoggingEnabled(); } - private void loadDeviceConfigConstants() { + void loadDeviceConfigConstants() { mOnDeviceConfigChangedListener.onPropertiesChanged( DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER)); mOnDeviceConfigChangedForComponentAliasListener.onPropertiesChanged( diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ad76bbca79ab..7ba720eedfba 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4579,14 +4579,8 @@ public class ActivityManagerService extends IActivityManager.Stub EventLogTags.writeAmProcBound(app.userId, pid, app.processName); synchronized (mProcLock) { - app.mState.setCurAdj(ProcessList.INVALID_ADJ); - app.mState.setSetAdj(ProcessList.INVALID_ADJ); - app.mState.setVerifiedAdj(ProcessList.INVALID_ADJ); - mOomAdjuster.setAttachingSchedGroupLSP(app); - app.mState.setForcingToImportant(null); + mOomAdjuster.setAttachingProcessStatesLSP(app); clearProcessForegroundLocked(app); - app.mState.setHasShownUi(false); - app.mState.setCached(false); app.setDebugging(false); app.setKilledByAm(false); app.setKilled(false); @@ -4753,8 +4747,14 @@ public class ActivityManagerService extends IActivityManager.Stub app.makeActive(thread, mProcessStats); checkTime(startTime, "attachApplicationLocked: immediately after bindApplication"); } + app.setPendingFinishAttach(true); + updateLruProcessLocked(app, false, null); checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked"); + + updateOomAdjLocked(app, OOM_ADJ_REASON_PROCESS_BEGIN); + checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked"); + final long now = SystemClock.uptimeMillis(); synchronized (mAppProfiler.mProfilerLock) { app.mProfile.setLastRequestedGc(now); @@ -4770,8 +4770,6 @@ public class ActivityManagerService extends IActivityManager.Stub if (!mConstants.mEnableWaitForFinishAttachApplication) { finishAttachApplicationInner(startSeq, callingUid, pid); - } else { - app.setPendingFinishAttach(true); } } catch (Exception e) { // We need kill the process group here. (b/148588589) @@ -14449,6 +14447,8 @@ public class ActivityManagerService extends IActivityManager.Stub && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { Slog.w(TAG, "Skipping broadcast of " + intent + ": user " + userId + " and its parent (if any) are stopped"); + scheduleCanceledResultTo(resultToApp, resultTo, intent, userId, + brOptions, callingUid, callerPackage); return ActivityManager.BROADCAST_FAILED_USER_STOPPED; } } @@ -14520,6 +14520,8 @@ public class ActivityManagerService extends IActivityManager.Stub isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action); } catch (RemoteException e) { Slog.w(TAG, "Remote exception", e); + scheduleCanceledResultTo(resultToApp, resultTo, intent, + userId, brOptions, callingUid, callerPackage); return ActivityManager.BROADCAST_SUCCESS; } @@ -14753,6 +14755,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (aInfo == null) { Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:" + " ssp=" + ssp + " data=" + data); + scheduleCanceledResultTo(resultToApp, resultTo, intent, + userId, brOptions, callingUid, callerPackage); return ActivityManager.BROADCAST_SUCCESS; } updateAssociationForApp(aInfo); @@ -14838,6 +14842,8 @@ public class ActivityManagerService extends IActivityManager.Stub // Apps should now be using ShortcutManager.pinRequestShortcut(). Log.w(TAG, "Broadcast " + action + " no longer supported. It will not be delivered."); + scheduleCanceledResultTo(resultToApp, resultTo, intent, + userId, brOptions, callingUid, callerPackage); return ActivityManager.BROADCAST_SUCCESS; case Intent.ACTION_PRE_BOOT_COMPLETED: timeoutExempt = true; @@ -14845,6 +14851,8 @@ public class ActivityManagerService extends IActivityManager.Stub case Intent.ACTION_CLOSE_SYSTEM_DIALOGS: if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid, callerPackage)) { + scheduleCanceledResultTo(resultToApp, resultTo, intent, + userId, brOptions, callingUid, callerPackage); // Returning success seems to be the pattern here return ActivityManager.BROADCAST_SUCCESS; } @@ -14879,6 +14887,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (requiredPermissions != null && requiredPermissions.length > 0) { Slog.w(TAG, "Can't broadcast sticky intent " + intent + " and enforce permissions " + Arrays.toString(requiredPermissions)); + scheduleCanceledResultTo(resultToApp, resultTo, intent, + userId, brOptions, callingUid, callerPackage); return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION; } if (intent.getComponent() != null) { @@ -15130,6 +15140,33 @@ public class ActivityManagerService extends IActivityManager.Stub } @GuardedBy("this") + private void scheduleCanceledResultTo(ProcessRecord resultToApp, IIntentReceiver resultTo, + Intent intent, int userId, BroadcastOptions options, int callingUid, + String callingPackage) { + if (resultTo == null) { + return; + } + final ProcessRecord app = resultToApp; + final IApplicationThread thread = (app != null) ? app.getOnewayThread() : null; + if (thread != null) { + try { + final boolean shareIdentity = (options != null && options.isShareIdentityEnabled()); + thread.scheduleRegisteredReceiver( + resultTo, intent, Activity.RESULT_CANCELED, null, null, + false, false, true, userId, app.mState.getReportedProcState(), + shareIdentity ? callingUid : Process.INVALID_UID, + shareIdentity ? callingPackage : null); + } catch (RemoteException e) { + final String msg = "Failed to schedule result of " + intent + " via " + + app + ": " + e; + app.killLocked("Can't schedule resultTo", ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true); + Slog.d(TAG, msg); + } + } + } + + @GuardedBy("this") private int getRealProcessStateLocked(ProcessRecord app, int pid) { if (app == null) { synchronized (mPidsSelfLocked) { @@ -17272,6 +17309,12 @@ public class ActivityManagerService extends IActivityManager.Stub } } + void onProcessFreezableChangedLocked(ProcessRecord app) { + if (mEnableModernQueue) { + mBroadcastQueues[0].onProcessFreezableChangedLocked(app); + } + } + @VisibleForTesting public final class LocalService extends ActivityManagerInternal implements ActivityManagerLocal { @@ -18983,25 +19026,27 @@ public class ActivityManagerService extends IActivityManager.Stub // too quickly in parallel below pingCount.incrementAndGet(); - synchronized (mProcLock) { - final ArrayMap<String, SparseArray<ProcessRecord>> pmap = - mProcessList.getProcessNamesLOSP().getMap(); - final int numProc = pmap.size(); - for (int iProc = 0; iProc < numProc; iProc++) { - final SparseArray<ProcessRecord> apps = pmap.valueAt(iProc); - for (int iApp = 0, numApps = apps.size(); iApp < numApps; iApp++) { - final ProcessRecord app = apps.valueAt(iApp); - final IApplicationThread thread = app.getOnewayThread(); - if (thread != null) { - mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app, - CachedAppOptimizer.UNFREEZE_REASON_PING); - pingCount.incrementAndGet(); - try { - thread.schedulePing(pongCallback); - } catch (RemoteException ignored) { - // When we failed to ping remote process, pretend as - // if we received the expected pong - pongCallback.sendResult(null); + synchronized (ActivityManagerService.this) { + synchronized (mProcLock) { + final ArrayMap<String, SparseArray<ProcessRecord>> pmap = + mProcessList.getProcessNamesLOSP().getMap(); + final int numProc = pmap.size(); + for (int iProc = 0; iProc < numProc; iProc++) { + final SparseArray<ProcessRecord> apps = pmap.valueAt(iProc); + for (int iApp = 0, numApps = apps.size(); iApp < numApps; iApp++) { + final ProcessRecord app = apps.valueAt(iApp); + final IApplicationThread thread = app.getOnewayThread(); + if (thread != null) { + mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app, + CachedAppOptimizer.UNFREEZE_REASON_PING); + pingCount.incrementAndGet(); + try { + thread.schedulePing(pongCallback); + } catch (RemoteException ignored) { + // When we failed to ping remote process, pretend as + // if we received the expected pong + pongCallback.sendResult(null); + } } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index ba8a1b9d4690..6801d27c8ff9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -928,8 +928,8 @@ final class ActivityManagerShellCommand extends ShellCommand { final int result = mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null, requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, bundle, true, false, mUserId); - Slogf.i(TAG, "Broadcasted %s: " + result, intent); - if (!mAsync) { + Slogf.i(TAG, "Enqueued broadcast %s: " + result, intent); + if (result == ActivityManager.BROADCAST_SUCCESS && !mAsync) { receiver.waitForFinish(); } return 0; @@ -3699,14 +3699,11 @@ final class ActivityManagerShellCommand extends ShellCommand { if (foregroundActivities) { try { int prcState = mIam.getUidProcessState(uid, "android"); - ProcessRecord topApp = mInternal.getTopApp(); - if (topApp == null) { - mPw.println("No top app found"); + + if (prcState == ProcessStateEnum.TOP) { + mPw.println("New foreground process: " + pid); } else { - int topPid = topApp.getPid(); - if (prcState == ProcessStateEnum.TOP && topPid == pid) { - mPw.println("New foreground process: " + pid); - } + mPw.println("No top app found"); } mPw.flush(); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index a80ad599a3e2..0fcec6faa0cc 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -25,6 +25,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; +import android.app.ActivityManager; import android.app.BroadcastOptions; import android.content.Intent; import android.content.pm.ResolveInfo; @@ -208,7 +209,7 @@ class BroadcastProcessQueue { private boolean mLastDeferredStates; private boolean mUidForeground; - private boolean mUidCached; + private boolean mProcessFreezable; private boolean mProcessInstrumented; private boolean mProcessPersistent; @@ -440,7 +441,7 @@ class BroadcastProcessQueue { */ @CheckResult public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground, - boolean uidCached) { + boolean processFreezable) { this.app = app; // Since we may have just changed our PID, invalidate cached strings @@ -449,13 +450,13 @@ class BroadcastProcessQueue { boolean didSomething = false; if (app != null) { - didSomething |= setUidCached(uidCached); didSomething |= setUidForeground(uidForeground); + didSomething |= setProcessFreezable(processFreezable); didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null); didSomething |= setProcessPersistent(app.isPersistent()); } else { - didSomething |= setUidCached(uidCached); didSomething |= setUidForeground(false); + didSomething |= setProcessFreezable(false); didSomething |= setProcessInstrumented(false); didSomething |= setProcessPersistent(false); } @@ -479,13 +480,13 @@ class BroadcastProcessQueue { } /** - * Update if this process is in the "cached" state, typically signaling that + * Update if this process is in the "freezable" state, typically signaling that * broadcast dispatch should be paused or delayed. */ @CheckResult - private boolean setUidCached(boolean uidCached) { - if (mUidCached != uidCached) { - mUidCached = uidCached; + private boolean setProcessFreezable(boolean freezable) { + if (mProcessFreezable != freezable) { + mProcessFreezable = freezable; invalidateRunnableAt(); return true; } else { @@ -1045,6 +1046,7 @@ class BroadcastProcessQueue { static final int REASON_CONTAINS_MANIFEST = 17; static final int REASON_FOREGROUND = 18; static final int REASON_CORE_UID = 19; + static final int REASON_TOP_PROCESS = 20; @IntDef(flag = false, prefix = { "REASON_" }, value = { REASON_EMPTY, @@ -1066,6 +1068,7 @@ class BroadcastProcessQueue { REASON_CONTAINS_MANIFEST, REASON_FOREGROUND, REASON_CORE_UID, + REASON_TOP_PROCESS, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} @@ -1091,6 +1094,7 @@ class BroadcastProcessQueue { case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST"; case REASON_FOREGROUND: return "FOREGROUND"; case REASON_CORE_UID: return "CORE_UID"; + case REASON_TOP_PROCESS: return "TOP_PROCESS"; default: return Integer.toString(reason); } } @@ -1132,6 +1136,11 @@ class BroadcastProcessQueue { } else if (mUidForeground) { mRunnableAt = runnableAt + constants.DELAY_FOREGROUND_PROC_MILLIS; mRunnableAtReason = REASON_FOREGROUND; + } else if (app != null && app.getSetProcState() == ActivityManager.PROCESS_STATE_TOP) { + // TODO (b/287676625): Use a callback to check when a process goes in and out of + // the TOP state. + mRunnableAt = runnableAt + constants.DELAY_FOREGROUND_PROC_MILLIS; + mRunnableAtReason = REASON_TOP_PROCESS; } else if (mProcessPersistent) { mRunnableAt = runnableAt + constants.DELAY_PERSISTENT_PROC_MILLIS; mRunnableAtReason = REASON_PERSISTENT; @@ -1150,7 +1159,7 @@ class BroadcastProcessQueue { } else if (mCountManifest > 0) { mRunnableAt = runnableAt; mRunnableAtReason = REASON_CONTAINS_MANIFEST; - } else if (mUidCached) { + } else if (mProcessFreezable) { if (r.deferUntilActive) { // All enqueued broadcasts are deferrable, defer if (mCountDeferred == mCountEnqueued) { @@ -1220,7 +1229,7 @@ class BroadcastProcessQueue { // When all we have pending is deferred broadcasts, and we're cached, // then we want everything to be marked deferred final boolean wantDeferredStates = (mCountDeferred > 0) - && (mCountDeferred == mCountEnqueued) && mUidCached; + && (mCountDeferred == mCountEnqueued) && mProcessFreezable; if (mLastDeferredStates != wantDeferredStates) { mLastDeferredStates = wantDeferredStates; @@ -1407,9 +1416,9 @@ class BroadcastProcessQueue { if (mUidForeground) { sb.append("FG"); } - if (mUidCached) { + if (mProcessFreezable) { if (sb.length() > 0) sb.append("|"); - sb.append("CACHED"); + sb.append("FRZ"); } if (mProcessInstrumented) { if (sb.length() > 0) sb.append("|"); diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index e38a2eefcd92..c1f1dfd4fe75 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -174,6 +174,13 @@ public abstract class BroadcastQueue { public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app); /** + * Signal from OS internals that the given process is in a freezable state and will be + * frozen soon after. + */ + @GuardedBy("mService") + public abstract void onProcessFreezableChangedLocked(@NonNull ProcessRecord app); + + /** * Signal from OS internals that the given package (or some subset of that * package) has been disabled or uninstalled, and that any pending * broadcasts should be cleaned up. diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java index f13dc89f2bd2..127c5b389d79 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java @@ -466,6 +466,10 @@ public class BroadcastQueueImpl extends BroadcastQueue { skipCurrentOrPendingReceiverLocked(app); } + public void onProcessFreezableChangedLocked(ProcessRecord app) { + // Not supported; ignore + } + public boolean sendPendingBroadcastsLocked(ProcessRecord app) throws BroadcastDeliveryFailedException { boolean didSomething = false; diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 42a4a853f827..f420619db490 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -232,16 +232,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { @GuardedBy("mService") private final SparseBooleanArray mUidForeground = new SparseBooleanArray(); - /** - * Map from UID to its last known "cached" state. - * <p> - * We manually maintain this data structure since the lifecycle of - * {@link ProcessRecord} and {@link BroadcastProcessQueue} can be - * mismatched. - */ - @GuardedBy("mService") - private final SparseBooleanArray mUidCached = new SparseBooleanArray(); - private final BroadcastConstants mConstants; private final BroadcastConstants mFgConstants; private final BroadcastConstants mBgConstants; @@ -258,6 +248,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 4; private static final int MSG_CHECK_HEALTH = 5; private static final int MSG_CHECK_PENDING_COLD_START_VALIDITY = 6; + private static final int MSG_PROCESS_FREEZABLE_CHANGED = 7; private void enqueueUpdateRunningList() { mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST); @@ -298,6 +289,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue { checkPendingColdStartValidity(); return true; } + case MSG_PROCESS_FREEZABLE_CHANGED: { + synchronized (mService) { + refreshProcessQueueLocked((ProcessRecord) msg.obj); + } + return true; + } } return false; }; @@ -458,7 +455,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue { updateWarmProcess(queue); final boolean processWarm = queue.isProcessWarm(); - if (!processWarm) { + if (processWarm) { + mService.mOomAdjuster.unfreezeTemporarily(queue.app, + CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER); + // The process could be killed as part of unfreezing. So, check again if it + // is still warm. + if (!queue.isProcessWarm()) { + queue = nextQueue; + enqueueUpdateRunningList(); + continue; + } + } else { // We only offer to run one cold-start at a time to preserve // system resources; below we either claim that single slot or // skip to look for another warm process @@ -530,6 +537,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mRunningColdStart.reEnqueueActiveBroadcast(); demoteFromRunningLocked(mRunningColdStart); clearRunningColdStart(); + enqueueUpdateRunningList(); } private void checkPendingColdStartValidity() { @@ -564,6 +572,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { @Override public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app) throws BroadcastDeliveryFailedException { + if (DEBUG_BROADCAST) { + logv("Process " + app + " is attached"); + } // Process records can be recycled, so always start by looking up the // relevant per-process queue final BroadcastProcessQueue queue = getProcessQueue(app); @@ -613,18 +624,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue { @Override public void onApplicationCleanupLocked(@NonNull ProcessRecord app) { - // Process records can be recycled, so always start by looking up the - // relevant per-process queue - final BroadcastProcessQueue queue = getProcessQueue(app); - if (queue != null) { - setQueueProcess(queue, null); + if (DEBUG_BROADCAST) { + logv("Process " + app + " is cleaned up"); } - if ((mRunningColdStart != null) && (mRunningColdStart == queue)) { + // This cleanup callback could be for an old process and not for the one we are waiting + // on, so explicitly check if this for the same ProcessRecord that a queue has. + final BroadcastProcessQueue queue = getProcessQueue(app); + if ((mRunningColdStart != null) && (mRunningColdStart == queue) + && mRunningColdStart.app == app) { clearRunningColdStart(); } - if (queue != null) { + if (queue != null && queue.app == app) { + setQueueProcess(queue, null); + // If queue was running a broadcast, fail it if (queue.isActive()) { finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, @@ -656,6 +670,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } @Override + public void onProcessFreezableChangedLocked(@NonNull ProcessRecord app) { + mLocalHandler.removeMessages(MSG_PROCESS_FREEZABLE_CHANGED, app); + mLocalHandler.sendMessage(mHandler.obtainMessage(MSG_PROCESS_FREEZABLE_CHANGED, app)); + } + + @Override public int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app) { final BroadcastProcessQueue queue = getProcessQueue(app); if ((queue != null) && getRunningIndexOf(queue) >= 0) { @@ -1073,6 +1093,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // If we were trying to deliver a manifest broadcast, throw the error as we need // to try redelivering the broadcast to this receiver. if (receiver instanceof ResolveInfo) { + mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue); throw new BroadcastDeliveryFailedException(e); } finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, @@ -1447,7 +1468,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { }; broadcastPredicate = BROADCAST_PREDICATE_ANY; - cleanupUserStateLocked(mUidCached, userId); cleanupUserStateLocked(mUidForeground, userId); } return forEachMatchingBroadcast(queuePredicate, broadcastPredicate, @@ -1590,19 +1610,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { refreshProcessQueuesLocked(uid); } } - - @Override - public void onUidCachedChanged(int uid, boolean cached) { - synchronized (mService) { - if (cached) { - mUidCached.put(uid, true); - } else { - mUidCached.delete(uid); - } - refreshProcessQueuesLocked(uid); - } - } - }, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CACHED, + }, ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_TOP, "android"); // Kick off periodic health checks @@ -1792,10 +1800,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // warm via this operation, we're going to immediately promote it to // be running, and any side effect of this operation will then apply // after it's finished and is returned to the runnable list. - queue.setProcessAndUidState( - mService.getProcessRecordLocked(queue.processName, queue.uid), - mUidForeground.get(queue.uid, false), - mUidCached.get(queue.uid, false)); + final ProcessRecord app = mService.getProcessRecordLocked(queue.processName, queue.uid); + queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false), + isProcessFreezable(app)); } } @@ -1807,11 +1814,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private void setQueueProcess(@NonNull BroadcastProcessQueue queue, @Nullable ProcessRecord app) { if (queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false), - mUidCached.get(queue.uid, false))) { + isProcessFreezable(app))) { updateRunnableList(queue); } } + @GuardedBy("mService") + private boolean isProcessFreezable(@Nullable ProcessRecord app) { + if (app == null) { + return false; + } + synchronized (mService.mProcLock) { + return app.mOptRecord.isPendingFreeze() || app.mOptRecord.isFrozen(); + } + } + /** * Refresh the process queues with the latest process state so that runnableAt * can be updated. @@ -1829,6 +1846,20 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } /** + * Refresh the process queue corresponding to {@code app} with the latest process state + * so that runnableAt can be updated. + */ + @GuardedBy("mService") + private void refreshProcessQueueLocked(@NonNull ProcessRecord app) { + final BroadcastProcessQueue queue = getProcessQueue(app.processName, app.uid); + if (queue == null || queue.app == null || queue.app.getPid() != app.getPid()) { + return; + } + setQueueProcess(queue, queue.app); + enqueueUpdateRunningList(); + } + + /** * Inform other parts of OS that the given broadcast queue has started * running, typically for internal bookkeeping. */ @@ -2154,12 +2185,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { ipw.decreaseIndent(); ipw.println(); - ipw.println("Cached UIDs:"); - ipw.increaseIndent(); - ipw.println(mUidCached); - ipw.decreaseIndent(); - ipw.println(); - ipw.println("Foreground UIDs:"); ipw.increaseIndent(); ipw.println(mUidForeground); diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 7773190d22b6..e588a9e51cd9 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -123,7 +123,14 @@ public final class CachedAppOptimizer { "freeze_debounce_timeout"; @VisibleForTesting static final String KEY_FREEZER_EXEMPT_INST_PKG = "freeze_exempt_inst_pkg"; - + @VisibleForTesting static final String KEY_FREEZER_BINDER_ENABLED = + "freeze_binder_enabled"; + @VisibleForTesting static final String KEY_FREEZER_BINDER_DIVISOR = + "freeze_binder_divisor"; + @VisibleForTesting static final String KEY_FREEZER_BINDER_OFFSET = + "freeze_binder_offset"; + @VisibleForTesting static final String KEY_FREEZER_BINDER_THRESHOLD = + "freeze_binder_threshold"; static final int UNFREEZE_REASON_NONE = FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_NONE; @@ -237,8 +244,8 @@ public final class CachedAppOptimizer { @VisibleForTesting static final boolean ENABLE_FILE_COMPACT = false; // Defaults for phenotype flags. - @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = true; - @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true; + @VisibleForTesting static final boolean DEFAULT_USE_COMPACTION = true; + @VisibleForTesting static final boolean DEFAULT_USE_FREEZER = true; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500; @@ -257,7 +264,11 @@ public final class CachedAppOptimizer { @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE = String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER); @VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 10_000L; - @VisibleForTesting static final Boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true; + @VisibleForTesting static final boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true; + @VisibleForTesting static final boolean DEFAULT_FREEZER_BINDER_ENABLED = true; + @VisibleForTesting static final long DEFAULT_FREEZER_BINDER_DIVISOR = 4; + @VisibleForTesting static final int DEFAULT_FREEZER_BINDER_OFFSET = 500; + @VisibleForTesting static final long DEFAULT_FREEZER_BINDER_THRESHOLD = 1_000; @VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor( Settings.Global.CACHED_APPS_FREEZER_ENABLED); @@ -393,6 +404,11 @@ public final class CachedAppOptimizer { updateFreezerDebounceTimeout(); } else if (KEY_FREEZER_EXEMPT_INST_PKG.equals(name)) { updateFreezerExemptInstPkg(); + } else if (KEY_FREEZER_BINDER_ENABLED.equals(name) + || KEY_FREEZER_BINDER_DIVISOR.equals(name) + || KEY_FREEZER_BINDER_THRESHOLD.equals(name) + || KEY_FREEZER_BINDER_OFFSET.equals(name)) { + updateFreezerBinderState(); } } } @@ -455,6 +471,16 @@ public final class CachedAppOptimizer { @GuardedBy("mPhenotypeFlagLock") @VisibleForTesting final Set<Integer> mProcStateThrottle; + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile boolean mFreezerBinderEnabled = DEFAULT_FREEZER_BINDER_ENABLED; + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mFreezerBinderDivisor = DEFAULT_FREEZER_BINDER_DIVISOR; + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile int mFreezerBinderOffset = DEFAULT_FREEZER_BINDER_OFFSET; + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mFreezerBinderThreshold = DEFAULT_FREEZER_BINDER_THRESHOLD; + + // Handler on which compaction runs. @VisibleForTesting Handler mCompactionHandler; @@ -759,6 +785,10 @@ public final class CachedAppOptimizer { pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate); pw.println(" " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout); pw.println(" " + KEY_FREEZER_EXEMPT_INST_PKG + "=" + mFreezerExemptInstPkg); + pw.println(" " + KEY_FREEZER_BINDER_ENABLED + "=" + mFreezerBinderEnabled); + pw.println(" " + KEY_FREEZER_BINDER_THRESHOLD + "=" + mFreezerBinderThreshold); + pw.println(" " + KEY_FREEZER_BINDER_DIVISOR + "=" + mFreezerBinderDivisor); + pw.println(" " + KEY_FREEZER_BINDER_OFFSET + "=" + mFreezerBinderOffset); synchronized (mProcLock) { int size = mFrozenProcesses.size(); pw.println(" Apps frozen: " + size); @@ -1264,6 +1294,26 @@ public final class CachedAppOptimizer { Slog.d(TAG_AM, "Freezer exemption set to " + mFreezerExemptInstPkg); } + @GuardedBy("mPhenotypeFlagLock") + private void updateFreezerBinderState() { + mFreezerBinderEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + KEY_FREEZER_BINDER_ENABLED, DEFAULT_FREEZER_BINDER_ENABLED); + mFreezerBinderDivisor = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + KEY_FREEZER_BINDER_DIVISOR, DEFAULT_FREEZER_BINDER_DIVISOR); + mFreezerBinderOffset = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + KEY_FREEZER_BINDER_OFFSET, DEFAULT_FREEZER_BINDER_OFFSET); + mFreezerBinderThreshold = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + KEY_FREEZER_BINDER_THRESHOLD, DEFAULT_FREEZER_BINDER_THRESHOLD); + Slog.d(TAG_AM, "Freezer binder state set to enabled=" + mFreezerBinderEnabled + + ", divisor=" + mFreezerBinderDivisor + + ", offset=" + mFreezerBinderOffset + + ", threshold=" + mFreezerBinderThreshold); + } + private boolean parseProcStateThrottle(String procStateThrottleString) { String[] procStates = TextUtils.split(procStateThrottleString, ","); mProcStateThrottle.clear(); @@ -1352,6 +1402,8 @@ public final class CachedAppOptimizer { } } } + reportProcessFreezableChangedLocked(app); + app.mOptRecord.setLastUsedTimeout(delayMillis); mFreezeHandler.sendMessageDelayed( mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app), delayMillis); @@ -1389,6 +1441,7 @@ public final class CachedAppOptimizer { uidRec.setFrozen(false); postUidFrozenMessage(uidRec.getUid(), false); } + reportProcessFreezableChangedLocked(app); opt.setFreezerOverride(false); if (pid == 0 || !opt.isFrozen()) { @@ -1525,10 +1578,14 @@ public final class CachedAppOptimizer { opt.setPendingFreeze(false); } - UidRecord uidRec = app.getUidRecord(); - if (uidRec != null && uidRec.isFrozen()) { - uidRec.setFrozen(false); - postUidFrozenMessage(uidRec.getUid(), false); + final UidRecord uidRec = app.getUidRecord(); + if (uidRec != null) { + final boolean isFrozen = uidRec.getNumOfProcs() > 1 + && uidRec.areAllProcessesFrozen(app); + if (isFrozen != uidRec.isFrozen()) { + uidRec.setFrozen(isFrozen); + postUidFrozenMessage(uidRec.getUid(), isFrozen); + } } mFrozenProcesses.delete(app.getPid()); @@ -2066,6 +2123,11 @@ public final class CachedAppOptimizer { 0, uidObj)); } + @GuardedBy("mAm") + private void reportProcessFreezableChangedLocked(ProcessRecord app) { + mAm.onProcessFreezableChangedLocked(app); + } + private final class FreezeHandler extends Handler implements ProcLocksReader.ProcLocksReaderCallback { private FreezeHandler() { @@ -2075,7 +2137,7 @@ public final class CachedAppOptimizer { @Override public void handleMessage(Message msg) { switch (msg.what) { - case SET_FROZEN_PROCESS_MSG: + case SET_FROZEN_PROCESS_MSG: { ProcessRecord proc = (ProcessRecord) msg.obj; synchronized (mAm) { freezeProcess(proc); @@ -2085,8 +2147,8 @@ public final class CachedAppOptimizer { removeMessages(DEADLOCK_WATCHDOG_MSG); sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS); } - break; - case REPORT_UNFREEZE_MSG: + } break; + case REPORT_UNFREEZE_MSG: { int pid = msg.arg1; int frozenDuration = msg.arg2; Pair<String, Integer> obj = (Pair<String, Integer>) msg.obj; @@ -2094,13 +2156,13 @@ public final class CachedAppOptimizer { int reason = obj.second; reportUnfreeze(pid, frozenDuration, processName, reason); - break; - case UID_FROZEN_STATE_CHANGED_MSG: + } break; + case UID_FROZEN_STATE_CHANGED_MSG: { final boolean frozen = (msg.arg1 == 1); final int uid = (int) msg.obj; reportOneUidFrozenStateChanged(uid, frozen); - break; - case DEADLOCK_WATCHDOG_MSG: + } break; + case DEADLOCK_WATCHDOG_MSG: { try { // post-check to prevent deadlock if (DEBUG_FREEZER) { @@ -2110,19 +2172,61 @@ public final class CachedAppOptimizer { } catch (IOException e) { Slog.w(TAG_AM, "Unable to check file locks"); } - break; + } break; default: return; } } @GuardedBy({"mAm", "mProcLock"}) - private void rescheduleFreeze(final ProcessRecord proc, final String reason, - @UnfreezeReason int reasonCode) { + private void handleBinderFreezerFailure(final ProcessRecord proc, final String reason) { + if (!mFreezerBinderEnabled) { + // Just reschedule indefinitely. + unfreezeAppLSP(proc, UNFREEZE_REASON_BINDER_TXNS); + freezeAppAsyncLSP(proc); + return; + } + /* + * This handles the case where a process couldn't be frozen due to pending binder + * transactions. In order to prevent apps from avoiding the freezer by spamming binder + * transactions, there is an exponential decrease in freezer retry times plus a random + * offset per attempt to avoid phase issues. Once the last-attempted timeout is below a + * threshold, we assume that the app is spamming binder calls and can never be frozen, + * and we will then crash the app. + */ + if (proc.mOptRecord.getLastUsedTimeout() <= mFreezerBinderThreshold) { + // We've given the app plenty of chances, assume broken. Time to die. + Slog.d(TAG_AM, "Kill app due to repeated failure to freeze binder: " + + proc.getPid() + " " + proc.processName); + mAm.mHandler.post(() -> { + synchronized (mAm) { + // Crash regardless of procstate in case the app has found another way + // to abuse oom_adj + if (proc.getThread() == null) { + return; + } + proc.killLocked("excessive binder traffic during cached", + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, + ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU, + true); + } + }); + return; + } + + long timeout = proc.mOptRecord.getLastUsedTimeout() / mFreezerBinderDivisor; + // range is [-mFreezerBinderOffset, +mFreezerBinderOffset] + int offset = mRandom.nextInt(mFreezerBinderOffset * 2) - mFreezerBinderOffset; + timeout = Math.max(timeout + offset, mFreezerBinderThreshold); + Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid() - + " " + proc.processName + " (" + reason + ")"); - unfreezeAppLSP(proc, reasonCode); - freezeAppAsyncLSP(proc); + + " " + proc.processName + " (" + reason + "), timeout=" + timeout); + Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_FREEZER_TRACK, + "Reschedule freeze " + proc.processName + ":" + proc.getPid() + + " timeout=" + timeout + ", reason=" + reason); + + unfreezeAppLSP(proc, UNFREEZE_REASON_BINDER_TXNS); + freezeAppAsyncLSP(proc, timeout); } /** @@ -2169,7 +2273,7 @@ public final class CachedAppOptimizer { // transactions that might be pending. try { if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) { - rescheduleFreeze(proc, "outstanding txns", UNFREEZE_REASON_BINDER_TXNS); + handleBinderFreezerFailure(proc, "outstanding txns"); return; } } catch (RuntimeException e) { @@ -2230,7 +2334,7 @@ public final class CachedAppOptimizer { if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) { synchronized (mProcLock) { - rescheduleFreeze(proc, "new pending txns", UNFREEZE_REASON_BINDER_TXNS); + handleBinderFreezerFailure(proc, "new pending txns"); } return; } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 764bbe8bd191..e51fc0a2cef7 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1009,7 +1009,6 @@ public class OomAdjuster { mCacheOomRanker.reRankLruCachedAppsLSP(mProcessList.getLruProcessesLSP(), mProcessList.getLruProcessServiceStartLOSP()); } - assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP()); if (computeClients) { // There won't be cycles if we didn't compute clients above. // Cycle strategy: @@ -1034,7 +1033,7 @@ public class OomAdjuster { ProcessRecord app = activeProcesses.get(i); final ProcessStateRecord state = app.mState; if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) { - if (computeOomAdjLSP(app, state.getCurRawAdj(), topApp, true, now, + if (computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, true, now, true, true)) { retryCycles = true; } @@ -1044,6 +1043,8 @@ public class OomAdjuster { } mProcessesInCycle.clear(); + assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP()); + mNumNonCachedProcs = 0; mNumCachedHiddenProcs = 0; @@ -1292,12 +1293,19 @@ public class OomAdjuster { for (int i = numLru - 1; i >= 0; i--) { ProcessRecord app = lruList.get(i); final ProcessStateRecord state = app.mState; - if (!app.isKilledByAm() && app.getThread() != null && !app.isPendingFinishAttach()) { + if (!app.isKilledByAm() && app.getThread() != null) { // We don't need to apply the update for the process which didn't get computed if (state.getCompletedAdjSeq() == mAdjSeq) { applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason); } + if (app.isPendingFinishAttach()) { + // Avoid trimming processes that are still initializing. If they aren't + // hosting any components yet because they may be unfairly killed. + // We however apply the oom scores set at #setAttachingProcessStatesLSP. + continue; + } + final ProcessServiceRecord psr = app.mServices; // Count the number of process types. switch (state.getCurProcState()) { @@ -2805,6 +2813,18 @@ public class OomAdjuster { capability &= ~PROCESS_CAPABILITY_BFSL; } + + if (app.isPendingFinishAttach()) { + // If the app is still starting up. We reset the computations to the + // hardcoded values in setAttachingProcessStatesLSP. This ensures that the app keeps + // hard-coded default 'startup' oom scores while starting up. When it finishes startup, + // we'll recompute oom scores based on it's actual hosted compoenents. + setAttachingProcessStatesLSP(app); + state.setAdjSeq(mAdjSeq); + state.setCompletedAdjSeq(state.getAdjSeq()); + return false; + } + // Do final modification to adj. Everything we do between here and applying // the final setAdj must be done in this function, because we will also use // it when computing the final cached adj later. Note that we don't need to @@ -3242,8 +3262,11 @@ public class OomAdjuster { } @GuardedBy({"mService", "mProcLock"}) - void setAttachingSchedGroupLSP(ProcessRecord app) { + void setAttachingProcessStatesLSP(ProcessRecord app) { int initialSchedGroup = SCHED_GROUP_DEFAULT; + int initialProcState = PROCESS_STATE_CACHED_EMPTY; + int initialCapability = PROCESS_CAPABILITY_NONE; + boolean initialCached = true; final ProcessStateRecord state = app.mState; // If the process has been marked as foreground, it is starting as the top app (with // Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread. @@ -3259,13 +3282,24 @@ public class OomAdjuster { setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST); } initialSchedGroup = SCHED_GROUP_TOP_APP; + initialProcState = PROCESS_STATE_TOP; + initialCapability = PROCESS_CAPABILITY_ALL; + initialCached = false; } catch (Exception e) { Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e); } } - state.setSetSchedGroup(initialSchedGroup); state.setCurrentSchedulingGroup(initialSchedGroup); + state.setCurProcState(initialProcState); + state.setCurRawProcState(initialProcState); + state.setCurCapability(initialCapability); + state.setCached(initialCached); + + state.setCurAdj(ProcessList.FOREGROUND_APP_ADJ); + state.setCurRawAdj(ProcessList.FOREGROUND_APP_ADJ); + state.setForcingToImportant(null); + state.setHasShownUi(false); } // ONLY used for unit testing in OomAdjusterTests.java diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java index ffe5a6e6b958..f5c5ea8ae55a 100644 --- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java +++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java @@ -127,6 +127,12 @@ final class ProcessCachedOptimizerRecord { @GuardedBy("mProcLock") private @UptimeMillisLong long mEarliestFreezableTimeMillis; + /** + * This is the most recently used timeout for freezing the app in millis + */ + @GuardedBy("mProcLock") + private long mLastUsedTimeout; + @GuardedBy("mProcLock") long getLastCompactTime() { return mLastCompactTime; @@ -282,6 +288,16 @@ final class ProcessCachedOptimizerRecord { } @GuardedBy("mProcLock") + long getLastUsedTimeout() { + return mLastUsedTimeout; + } + + @GuardedBy("mProcLock") + void setLastUsedTimeout(long lastUsedTimeout) { + mLastUsedTimeout = lastUsedTimeout; + } + + @GuardedBy("mProcLock") boolean isFreezeExempt() { return mFreezeExempt; } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 267d2464e0d5..f532122c10d9 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -674,6 +674,11 @@ class ProcessRecord implements WindowProcessListener { return mState.getCurProcState(); } + @GuardedBy(anyOf = {"mService", "mProcLock"}) + int getSetProcState() { + return mState.getSetProcState(); + } + @GuardedBy({"mService", "mProcLock"}) public void makeActive(IApplicationThread thread, ProcessStatsService tracker) { mProfile.onProcessActive(thread, tracker); @@ -1197,6 +1202,9 @@ class ProcessRecord implements WindowProcessListener { "Killing " + toShortString() + " (adj " + mState.getSetAdj() + "): " + reason, info.uid); } + // Since the process is getting killed, reset the freezable related state. + mOptRecord.setPendingFreeze(false); + mOptRecord.setFrozen(false); if (mPid > 0) { mService.mProcessList.noteAppKill(this, reasonCode, subReason, description); EventLog.writeEvent(EventLogTags.AM_KILL, diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java index af99684db6f4..cf69b535151c 100644 --- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java +++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java @@ -229,7 +229,7 @@ public class StackTracesDumpHelper { firstPidEnd = new File(tracesFile).length(); } // Append the Durations/latency comma separated array after the first PID. - if (latencyTracker != null) { + if (firstPidTempDumpCopied && latencyTracker != null) { appendtoANRFile(tracesFile, latencyTracker.dumpAsCommaSeparatedArrayWithHeader()); } diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index 993088ef106e..4329afc7e863 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -345,21 +345,32 @@ public final class UidRecord { } /** + * Check whether all processes in the Uid are frozen. + * + * @param excluding Skip this process record during the check. * @return true if all processes in the Uid are frozen, false otherwise. */ @GuardedBy(anyOf = {"mService", "mProcLock"}) - public boolean areAllProcessesFrozen() { + public boolean areAllProcessesFrozen(ProcessRecord excluding) { for (int i = mProcRecords.size() - 1; i >= 0; i--) { final ProcessRecord app = mProcRecords.valueAt(i); final ProcessCachedOptimizerRecord opt = app.mOptRecord; - if (!opt.isFrozen()) { + if (excluding != app && !opt.isFrozen()) { return false; } } return true; } + /** + * @return true if all processes in the Uid are frozen, false otherwise. + */ + @GuardedBy(anyOf = {"mService", "mProcLock"}) + public boolean areAllProcessesFrozen() { + return areAllProcessesFrozen(null); + } + @GuardedBy(anyOf = {"mService", "mProcLock"}) public void setFrozen(boolean frozen) { mUidIsFrozen = frozen; diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index 4e7865c2d7a6..2d7456471be4 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -88,8 +88,6 @@ class UserSwitchingDialog extends Dialog { UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser, String switchingFromSystemUserMessage, String switchingToSystemUserMessage, WindowManagerService windowManager) { - // TODO(b/278857848): Make full screen user switcher cover top part of the screen as well. - // This problem is seen only on phones, it works fine on tablets. super(context, R.style.Theme_Material_NoActionBar_Fullscreen); mContext = context; @@ -112,9 +110,12 @@ class UserSwitchingDialog extends Dialog { final WindowManager.LayoutParams attrs = window.getAttributes(); attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + attrs.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; window.setAttributes(attrs); window.setBackgroundDrawableResource(android.R.color.transparent); window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); + window.setDecorFitsSystemWindows(false); } void inflateContent() { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index ec85d577a8e7..0c7f11f98809 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -1199,11 +1199,16 @@ public class AudioDeviceInventory { AudioDeviceInfo device = Stream.of(connectedDevices) .filter(d -> d.getInternalType() == ada.getInternalType()) .filter(d -> (!AudioSystem.isBluetoothDevice(d.getInternalType()) - || (d.getAddress() == ada.getAddress()))) + || (d.getAddress().equals(ada.getAddress())))) .findFirst() .orElse(null); if (device == null) { + if (AudioService.DEBUG_DEVICES) { + Slog.i(TAG, "purgeRoles() removing device: " + ada.toString() + + ", for strategy: " + keyRole.first + + " and role: " + keyRole.second); + } asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada)); itDev.remove(); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c177c4efb5ef..53ed38edffe4 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3666,25 +3666,29 @@ public class AudioService extends IAudioService.Stub * and aliases before mute change changed and after. */ private void muteAliasStreams(int streamAlias, boolean state) { - synchronized (VolumeStreamState.class) { - List<Integer> streamsToMute = new ArrayList<>(); - for (int stream = 0; stream < mStreamStates.length; stream++) { - VolumeStreamState vss = mStreamStates[stream]; - if (streamAlias == mStreamVolumeAlias[stream] && vss.isMutable()) { - if (!(mCameraSoundForced - && (vss.getStreamType() - == AudioSystem.STREAM_SYSTEM_ENFORCED))) { - boolean changed = vss.mute(state, /* apply= */ false, "muteAliasStreams"); - if (changed) { - streamsToMute.add(stream); + // Locking mSettingsLock to avoid inversion when calling doMute -> updateVolumeGroupIndex + synchronized (mSettingsLock) { + synchronized (VolumeStreamState.class) { + List<Integer> streamsToMute = new ArrayList<>(); + for (int stream = 0; stream < mStreamStates.length; stream++) { + VolumeStreamState vss = mStreamStates[stream]; + if (streamAlias == mStreamVolumeAlias[stream] && vss.isMutable()) { + if (!(mCameraSoundForced + && (vss.getStreamType() + == AudioSystem.STREAM_SYSTEM_ENFORCED))) { + boolean changed = vss.mute(state, /* apply= */ false, + "muteAliasStreams"); + if (changed) { + streamsToMute.add(stream); + } } } } + streamsToMute.forEach(streamToMute -> { + mStreamStates[streamToMute].doMute(); + broadcastMuteSetting(streamToMute, state); + }); } - streamsToMute.forEach(streamToMute -> { - mStreamStates[streamToMute].doMute(); - broadcastMuteSetting(streamToMute, state); - }); } } @@ -3699,18 +3703,22 @@ public class AudioService extends IAudioService.Stub // Called after a delay when volume down is pressed while muted private void onUnmuteStream(int stream, int flags) { boolean wasMuted; - synchronized (VolumeStreamState.class) { - final VolumeStreamState streamState = mStreamStates[stream]; - // if unmuting causes a change, it was muted - wasMuted = streamState.mute(false, "onUnmuteStream"); + // Locking mSettingsLock to avoid inversion when calling vss.mute -> vss.doMute -> + // vss.updateVolumeGroupIndex + synchronized (mSettingsLock) { + synchronized (VolumeStreamState.class) { + final VolumeStreamState streamState = mStreamStates[stream]; + // if unmuting causes a change, it was muted + wasMuted = streamState.mute(false, "onUnmuteStream"); - final int device = getDeviceForStream(stream); - final int index = streamState.getIndex(device); - sendVolumeUpdate(stream, index, index, flags, device); - } - if (stream == AudioSystem.STREAM_MUSIC && wasMuted) { - synchronized (mHdmiClientLock) { - maybeSendSystemAudioStatusCommand(true); + final int device = getDeviceForStream(stream); + final int index = streamState.getIndex(device); + sendVolumeUpdate(stream, index, index, flags, device); + } + if (stream == AudioSystem.STREAM_MUSIC && wasMuted) { + synchronized (mHdmiClientLock) { + maybeSendSystemAudioStatusCommand(true); + } } } } @@ -7611,7 +7619,11 @@ public class AudioService extends IAudioService.Stub Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]", newDevice, AudioSystem.getOutputDeviceName(newDevice))); } - mStreamStates[AudioSystem.STREAM_MUSIC].mute(false, "onAccessoryPlugMediaUnmute"); + // Locking mSettingsLock to avoid inversion when calling vss.mute -> vss.doMute -> + // vss.updateVolumeGroupIndex + synchronized (mSettingsLock) { + mStreamStates[AudioSystem.STREAM_MUSIC].mute(false, "onAccessoryPlugMediaUnmute"); + } } } @@ -7646,9 +7658,14 @@ public class AudioService extends IAudioService.Stub continue; } } - for (int i = 0; i < sVolumeGroupStates.size(); i++) { - final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); - vgs.applyAllVolumes(/* userSwitch= */ false); + + // need mSettingsLock for vgs.applyAllVolumes -> vss.setIndex which grabs this lock after + // VSS.class. Locking order needs to be preserved + synchronized (mSettingsLock) { + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.applyAllVolumes(/* userSwitch= */ false); + } } } @@ -7685,9 +7702,14 @@ public class AudioService extends IAudioService.Stub if (DEBUG_VOL) { Log.v(TAG, "restoreVolumeGroups"); } - for (int i = 0; i < sVolumeGroupStates.size(); i++) { - final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); - vgs.applyAllVolumes(false/*userSwitch*/); + + // need mSettingsLock for vgs.applyAllVolumes -> vss.setIndex which grabs this lock after + // VSS.class. Locking order needs to be preserved + synchronized (mSettingsLock) { + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.applyAllVolumes(false/*userSwitch*/); + } } } @@ -7824,49 +7846,51 @@ public class AudioService extends IAudioService.Stub } public void adjustVolume(int direction, int flags) { - synchronized (AudioService.VolumeStreamState.class) { - int device = getDeviceForVolume(); - int previousIndex = getIndex(device); - if (isMuteAdjust(direction) && !isMutable()) { - // Non mutable volume group - if (DEBUG_VOL) { - Log.d(TAG, "invalid mute on unmutable volume group " + name()); - } - return; - } - switch (direction) { - case AudioManager.ADJUST_TOGGLE_MUTE: { - // Note: If muted by volume 0, unmute will restore volume 0. - mute(!mIsMuted); - break; + synchronized (mSettingsLock) { + synchronized (AudioService.VolumeStreamState.class) { + int device = getDeviceForVolume(); + int previousIndex = getIndex(device); + if (isMuteAdjust(direction) && !isMutable()) { + // Non mutable volume group + if (DEBUG_VOL) { + Log.d(TAG, "invalid mute on unmutable volume group " + name()); + } + return; } - case AudioManager.ADJUST_UNMUTE: - // Note: If muted by volume 0, unmute will restore volume 0. - mute(false); - break; - case AudioManager.ADJUST_MUTE: - // May be already muted by setvolume 0, prevent from setting same value - if (previousIndex != 0) { - // bypass persist - mute(true); + switch (direction) { + case AudioManager.ADJUST_TOGGLE_MUTE: { + // Note: If muted by volume 0, unmute will restore volume 0. + mute(!mIsMuted); + break; } - mIsMuted = true; - break; - case AudioManager.ADJUST_RAISE: - // As for stream, RAISE during mute will increment the index - setVolumeIndex(Math.min(previousIndex + 1, mIndexMax), device, flags); - break; - case AudioManager.ADJUST_LOWER: - // For stream, ADJUST_LOWER on a muted VSS is a no-op - // If we decide to unmute on ADJUST_LOWER, cannot fallback on - // adjustStreamVolume for group associated to legacy stream type - if (isMuted() && previousIndex != 0) { + case AudioManager.ADJUST_UNMUTE: + // Note: If muted by volume 0, unmute will restore volume 0. mute(false); - } else { - int newIndex = Math.max(previousIndex - 1, mIndexMin); - setVolumeIndex(newIndex, device, flags); - } - break; + break; + case AudioManager.ADJUST_MUTE: + // May be already muted by setvolume 0, prevent from setting same value + if (previousIndex != 0) { + // bypass persist + mute(true); + } + mIsMuted = true; + break; + case AudioManager.ADJUST_RAISE: + // As for stream, RAISE during mute will increment the index + setVolumeIndex(Math.min(previousIndex + 1, mIndexMax), device, flags); + break; + case AudioManager.ADJUST_LOWER: + // For stream, ADJUST_LOWER on a muted VSS is a no-op + // If we decide to unmute on ADJUST_LOWER, cannot fallback on + // adjustStreamVolume for group associated to legacy stream type + if (isMuted() && previousIndex != 0) { + mute(false); + } else { + int newIndex = Math.max(previousIndex - 1, mIndexMin); + setVolumeIndex(newIndex, device, flags); + } + break; + } } } } @@ -7878,11 +7902,13 @@ public class AudioService extends IAudioService.Stub } public void setVolumeIndex(int index, int flags) { - synchronized (AudioService.VolumeStreamState.class) { - if (mUseFixedVolume) { - return; + synchronized (mSettingsLock) { + synchronized (AudioService.VolumeStreamState.class) { + if (mUseFixedVolume) { + return; + } + setVolumeIndex(index, getDeviceForVolume(), flags); } - setVolumeIndex(index, getDeviceForVolume(), flags); } } @@ -8689,24 +8715,30 @@ public class AudioService extends IAudioService.Stub // If associated to volume group, update group cache private void updateVolumeGroupIndex(int device, boolean forceMuteState) { - synchronized (VolumeStreamState.class) { - if (mVolumeGroupState != null) { - int groupIndex = (getIndex(device) + 5) / 10; - if (DEBUG_VOL) { - Log.d(TAG, "updateVolumeGroupIndex for stream " + mStreamType - + ", muted=" + mIsMuted + ", device=" + device + ", index=" - + getIndex(device) + ", group " + mVolumeGroupState.name() - + " Muted=" + mVolumeGroupState.isMuted() + ", Index=" + groupIndex - + ", forceMuteState=" + forceMuteState); - } - mVolumeGroupState.updateVolumeIndex(groupIndex, device); - // Only propage mute of stream when applicable - if (isMutable()) { - // For call stream, align mute only when muted, not when index is set to 0 - mVolumeGroupState.mute( - forceMuteState ? mIsMuted : - (groupIndex == 0 && !isCallStream(mStreamType)) - || mIsMuted); + // need mSettingsLock when called from setIndex for vgs.mute -> vgs.applyAllVolumes -> + // vss.setIndex which grabs this lock after VSS.class. Locking order needs to be + // preserved + synchronized (mSettingsLock) { + synchronized (VolumeStreamState.class) { + if (mVolumeGroupState != null) { + int groupIndex = (getIndex(device) + 5) / 10; + if (DEBUG_VOL) { + Log.d(TAG, "updateVolumeGroupIndex for stream " + mStreamType + + ", muted=" + mIsMuted + ", device=" + device + ", index=" + + getIndex(device) + ", group " + mVolumeGroupState.name() + + " Muted=" + mVolumeGroupState.isMuted() + ", Index=" + + groupIndex + ", forceMuteState=" + forceMuteState); + } + mVolumeGroupState.updateVolumeIndex(groupIndex, device); + // Only propage mute of stream when applicable + if (isMutable()) { + // For call stream, align mute only when muted, not when index is set to + // 0 + mVolumeGroupState.mute( + forceMuteState ? mIsMuted : + (groupIndex == 0 && !isCallStream(mStreamType)) + || mIsMuted); + } } } } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index c8a17e5eea5a..3560797ce2cf 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -218,8 +218,8 @@ public class BtHelper { if (AudioService.DEBUG_VOL) { AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent( "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG)); - return; } + return; } if (!mAvrcpAbsVolSupported) { AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent( diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 9fc00382d396..50ffbcb9f4c4 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -812,8 +812,7 @@ public final class PlaybackActivityMonitor return false; } final int reqUsage = requester.getAudioAttributes().getUsage(); - if ((reqUsage == AudioAttributes.USAGE_ASSISTANT) - || (reqUsage == AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)) { + if (reqUsage == AudioAttributes.USAGE_ASSISTANT) { return true; } return false; diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 44e3d1ede19c..e6f25cb88006 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -267,17 +267,30 @@ class PreAuthInfo { } private Pair<BiometricSensor, Integer> calculateErrorByPriority() { - // If the caller requested STRONG, and the device contains both STRONG and non-STRONG - // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's - // BIOMETRIC_INSUFFICIENT_STRENGTH error. Pretty sure we can always prioritize - // BIOMETRIC_NOT_ENROLLED over any other error (unless of course its calculation is - // wrong, in which case we should fix that instead). + Pair<BiometricSensor, Integer> sensorNotEnrolled = null; + Pair<BiometricSensor, Integer> sensorLockout = null; for (Pair<BiometricSensor, Integer> pair : ineligibleSensors) { + int status = pair.second; + if (status == BIOMETRIC_LOCKOUT_TIMED || status == BIOMETRIC_LOCKOUT_PERMANENT) { + sensorLockout = pair; + } if (pair.second == BIOMETRIC_NOT_ENROLLED) { - return pair; + sensorNotEnrolled = pair; } } + // If there is a sensor locked out, prioritize lockout over other sensor's error. + // See b/286923477. + if (sensorLockout != null) { + return sensorLockout; + } + + // If the caller requested STRONG, and the device contains both STRONG and non-STRONG + // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's + // BIOMETRIC_INSUFFICIENT_STRENGTH error. + if (sensorNotEnrolled != null) { + return sensorNotEnrolled; + } return ineligibleSensors.get(0); } diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index 46c77e8a82f2..aa6a0f1bb55f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -210,4 +210,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement public boolean isInterruptable() { return true; } + + public boolean isAlreadyCancelled() { + return mAlreadyCancelled; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 7fa4d6ce4d49..78c38089e803 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -268,6 +268,14 @@ public class BiometricScheduler { return; } + if (mCurrentOperation.isAcquisitionOperation()) { + AcquisitionClient client = (AcquisitionClient) mCurrentOperation.getClientMonitor(); + if (client.isAlreadyCancelled()) { + mCurrentOperation.cancel(mHandler, mInternalCallback); + return; + } + } + if (mGestureAvailabilityDispatcher != null && mCurrentOperation.isAcquisitionOperation()) { mGestureAvailabilityDispatcher.markSensorActive( mCurrentOperation.getSensorId(), true /* active */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 7ae31b2a114d..35fc43ae5f74 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -43,7 +43,6 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; -import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback; @@ -212,6 +211,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut // 1) Authenticated == true // 2) Error occurred // 3) Authenticated == false + // 4) onLockout + // 5) onLockoutTimed mCallback.onClientFinished(this, true /* success */); } @@ -240,9 +241,6 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut vendorCode, getTargetUserId())); - if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) { - BiometricNotificationUtils.showReEnrollmentNotification(getContext()); - } super.onError(error, vendorCode); } @@ -304,11 +302,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut PerformanceTracker.getInstanceForSensorId(getSensorId()) .incrementTimedLockoutForUser(getTargetUserId()); - try { - getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } + onError(error, 0 /* vendorCode */); } @Override @@ -323,10 +317,6 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut PerformanceTracker.getInstanceForSensorId(getSensorId()) .incrementPermanentLockoutForUser(getTargetUserId()); - try { - getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - } + onError(error, 0 /* vendorCode */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 5aa1175b92cd..33ed63ce07e0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -487,7 +487,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { BaseClientMonitor clientMonitor, boolean success) { mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId), - sensorId, requestId, success); + sensorId, requestId, client.wasAuthSuccessful()); } }); }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 3d6a156d6022..ea6bb626837f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -913,7 +913,6 @@ public class FingerprintService extends SystemService { } provider.onPointerDown(requestId, sensorId, pc); } - @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override @@ -1184,4 +1183,15 @@ public class FingerprintService extends SystemService { } } } + + void simulateVhalFingerDown() { + if (Utils.isVirtualEnabled(getContext())) { + Slog.i(TAG, "Simulate virtual HAL finger down event"); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); + if (provider != null) { + provider.second.simulateVhalFingerDown(UserHandle.getCallingUserId(), + provider.first); + } + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java index 636413f75cf5..dc6a63f82bb1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java @@ -45,6 +45,8 @@ public class FingerprintShellCommand extends ShellCommand { return doHelp(); case "sync": return doSync(); + case "fingerdown": + return doSimulateVhalFingerDown(); default: getOutPrintWriter().println("Unrecognized command: " + cmd); } @@ -62,6 +64,8 @@ public class FingerprintShellCommand extends ShellCommand { pw.println(" Print this help text."); pw.println(" sync"); pw.println(" Sync enrollments now (virtualized sensors only)."); + pw.println(" fingerdown"); + pw.println(" Simulate finger down event (virtualized sensors only)."); } private int doHelp() { @@ -73,4 +77,9 @@ public class FingerprintShellCommand extends ShellCommand { mService.syncEnrollmentsNow(); return 0; } + + private int doSimulateVhalFingerDown() { + mService.simulateVhalFingerDown(); + return 0; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 26701c110c39..d70ca8c131de 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -157,4 +157,11 @@ public interface ServiceProvider extends * @param sensorId sensor ID of the associated operation */ default void scheduleWatchdog(int sensorId) {} + + /** + * Simulate fingerprint down touch event for virtual HAL + * @param userId user ID + * @param sensorId sensor ID + */ + default void simulateVhalFingerDown(int userId, int sensorId) {}; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index e682fe71b53c..f8d256651280 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -857,4 +857,18 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } biometricScheduler.startWatchdog(); } + + @Override + public void simulateVhalFingerDown(int userId, int sensorId) { + Slog.d(getTag(), "Simulate virtual HAL finger down event"); + final AidlSession session = mFingerprintSensors.get(sensorId).getSessionForUser(userId); + final PointerContext pc = new PointerContext(); + try { + session.getSession().onPointerDownWithContext(pc); + session.getSession().onUiReady(); + session.getSession().onPointerUpWithContext(pc); + } catch (RemoteException e) { + Slog.e(getTag(), "failed hal operation ", e); + } + } } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index bfb77b2e9c25..d9cb299db69f 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -1340,7 +1340,7 @@ public class DisplayDeviceConfig { } @Nullable - SensorData getProximitySensor() { + public SensorData getProximitySensor() { return mProximitySensor; } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 3832e6e372c5..858800a82151 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -4260,6 +4260,13 @@ public final class DisplayManagerService extends SystemService { } @VisibleForTesting + void overrideSensorManager(SensorManager sensorManager) { + synchronized (mSyncRoot) { + mSensorManager = sensorManager; + } + } + + @VisibleForTesting final class LocalService extends DisplayManagerInternal { @Override @@ -4512,7 +4519,7 @@ public final class DisplayManagerService extends SystemService { } final DisplayDeviceConfig config = device.getDisplayDeviceConfig(); SensorData sensorData = config.getProximitySensor(); - if (sensorData.matches(sensorName, sensorType)) { + if (sensorData != null && sensorData.matches(sensorName, sensorType)) { return new RefreshRateRange(sensorData.minRefreshRate, sensorData.maxRefreshRate); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index c1cc1dad2acf..ffecf2b7018d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1590,12 +1590,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); final boolean autoBrightnessEnabled = mUseAutoBrightness && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) - && Float.isNaN(brightnessState) - && mAutomaticBrightnessController != null - && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER; + && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_OVERRIDE + && mAutomaticBrightnessController != null; final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); final int autoBrightnessState = autoBrightnessEnabled + && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED : autoBrightnessDisabledDueToDisplayOff ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE @@ -1655,9 +1655,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mShouldResetShortTermModel); mShouldResetShortTermModel = false; } - mBrightnessRangeController.setAutoBrightnessEnabled(mUseAutoBrightness + mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessEnabled ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + : autoBrightnessDisabledDueToDisplayOff + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE + : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); if (mBrightnessTracker != null) { mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 287ff5562249..7043af863301 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -1296,7 +1296,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mAutomaticBrightnessStrategy.isShortTermModelActive(); mAutomaticBrightnessStrategy.setAutoBrightnessState(state, mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(), - brightnessState, mBrightnessReasonTemp.getReason(), mPowerRequest.policy, + mBrightnessReasonTemp.getReason(), mPowerRequest.policy, mDisplayBrightnessController.getLastUserSetScreenBrightness(), userSetBrightnessChanged); @@ -1306,10 +1306,12 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged() || userSetBrightnessChanged); - mBrightnessRangeController.setAutoBrightnessEnabled(mAutomaticBrightnessStrategy - .shouldUseAutoBrightness() + mBrightnessRangeController.setAutoBrightnessEnabled( + mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff() + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE + : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); boolean updateScreenBrightnessSetting = false; float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness(); diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index 9b39a7dedf12..bcd52598edd2 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -73,6 +73,9 @@ public class AutomaticBrightnessStrategy { // the user has enabled the auto-brightness from the settings, it is disabled because the // display is off private boolean mIsAutoBrightnessEnabled = false; + // Indicates if auto-brightness is disabled due to the display being off. Needed for metric + // purposes. + private boolean mAutoBrightnessDisabledDueToDisplayOff; // If the auto-brightness model for the last manual changes done by the user. private boolean mIsShortTermModelActive = false; @@ -96,24 +99,21 @@ public class AutomaticBrightnessStrategy { * AutomaticBrightnessController accounting for any manual changes made by the user. */ public void setAutoBrightnessState(int targetDisplayState, - boolean allowAutoBrightnessWhileDozingConfig, - float brightnessState, int brightnessReason, int policy, + boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy, float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) { final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig && Display.isDozeState(targetDisplayState); mIsAutoBrightnessEnabled = shouldUseAutoBrightness() && (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze) - && (Float.isNaN(brightnessState) - || brightnessReason == BrightnessReason.REASON_TEMPORARY - || brightnessReason == BrightnessReason.REASON_BOOST) - && mAutomaticBrightnessController != null - && brightnessReason != BrightnessReason.REASON_FOLLOWER; - final boolean autoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness() + && brightnessReason != BrightnessReason.REASON_OVERRIDE + && mAutomaticBrightnessController != null; + mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness() && !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze); final int autoBrightnessState = mIsAutoBrightnessEnabled + && brightnessReason != BrightnessReason.REASON_FOLLOWER ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : autoBrightnessDisabledDueToDisplayOff + : mAutoBrightnessDisabledDueToDisplayOff ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; @@ -125,6 +125,10 @@ public class AutomaticBrightnessStrategy { return mIsAutoBrightnessEnabled; } + public boolean isAutoBrightnessDisabledDueToDisplayOff() { + return mAutoBrightnessDisabledDueToDisplayOff; + } + /** * Updates the {@link BrightnessConfiguration} that is currently being used by the associated * display. diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 41651fd5553c..68cf59f0ae1f 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -203,9 +203,7 @@ public final class DreamManagerService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri) { - synchronized (mLock) { - updateWhenToDreamSettings(); - } + updateWhenToDreamSettings(); } } @@ -257,15 +255,7 @@ public final class DreamManagerService extends SystemService { if (Build.IS_DEBUGGABLE) { SystemProperties.addChangeCallback(mSystemPropertiesChanged); } - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - writePulseGestureEnabled(); - synchronized (mLock) { - stopDreamLocked(false /*immediate*/, "user switched"); - } - } - }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); + mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE), false, mDozeEnabledObserver, UserHandle.USER_ALL); @@ -303,6 +293,18 @@ public final class DreamManagerService extends SystemService { } } + @Override + public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { + updateWhenToDreamSettings(); + + mHandler.post(() -> { + writePulseGestureEnabled(); + synchronized (mLock) { + stopDreamLocked(false /*immediate*/, "user switched"); + } + }); + } + private void dumpInternal(PrintWriter pw) { synchronized (mLock) { pw.println("DREAM MANAGER (dumpsys dreams)"); @@ -318,6 +320,7 @@ public final class DreamManagerService extends SystemService { pw.println("mWhenToDream=" + mWhenToDream); pw.println("mKeepDreamingWhenUnpluggingDefault=" + mKeepDreamingWhenUnpluggingDefault); pw.println("getDozeComponent()=" + getDozeComponent()); + pw.println("mDreamOverlayServiceName=" + mDreamOverlayServiceName.flattenToString()); pw.println(); DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> mController.dump(pw1), pw, "", 200); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index ba9e280be49d..b0b1d676bc4b 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -352,6 +352,7 @@ final class InputMethodBindingController { clearCurMethodAndSessions(); mService.clearInputShownLocked(); mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME); + mService.resetSystemUiLocked(); } } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 57f8d1478905..4dbd82065a66 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -633,8 +633,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private InputMethodSubtype mCurrentSubtype; /** - * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController} + * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}. */ + @GuardedBy("ImfLock.class") private boolean mCurPerceptible; /** @@ -748,33 +749,26 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub SparseArray<AccessibilitySessionState> mEnabledAccessibilitySessions = new SparseArray<>(); /** - * True if the device is currently interactive with user. The value is true initially. + * {@code true} if the device is currently interactive with the user, initially true. + * + * @see #handleSetInteractive */ + @GuardedBy("ImfLock.class") boolean mIsInteractive = true; + @GuardedBy("ImfLock.class") + @InputMethodService.BackDispositionMode int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** - * A set of status bits regarding the active IME. + * The {@link InputMethodService.ImeWindowVisibility} of the currently bound IME, + * or {@code 0} if no IME is bound. * - * <p>This value is a combination of following two bits:</p> - * <dl> - * <dt>{@link InputMethodService#IME_ACTIVE}</dt> - * <dd> - * If this bit is ON, connected IME is ready to accept touch/key events. - * </dd> - * <dt>{@link InputMethodService#IME_VISIBLE}</dt> - * <dd> - * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible. - * </dd> - * <dt>{@link InputMethodService#IME_INVISIBLE}</dt> - * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is - * currently invisible. - * </dd> - * </dl> - * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and + * <p><em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and * {@link InputMethodBindingController#unbindCurrentMethod()}.</em> */ + @GuardedBy("ImfLock.class") + @InputMethodService.ImeWindowVisibility int mImeWindowVis; private LocaleList mLastSystemLocales; @@ -1535,7 +1529,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Uh oh, current input method is no longer around! // Pick another one... Slog.i(TAG, "Current input method removed: " + curInputMethodId); - updateSystemUiLocked(0 /* vis */, mBackDisposition); if (!chooseNewDefaultIMELocked()) { changed = true; curIm = null; @@ -2938,7 +2931,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub sessionState.mSession.finishSession(); } catch (RemoteException e) { Slog.w(TAG, "Session failed to close due to remote exception", e); - updateSystemUiLocked(0 /* vis */, mBackDisposition); } sessionState.mSession = null; } @@ -3048,15 +3040,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("ImfLock.class") - private boolean shouldShowImeSwitcherLocked(int visibility) { + private boolean shouldShowImeSwitcherLocked( + @InputMethodService.ImeWindowVisibility int visibility) { if (!mShowOngoingImeSwitcherForPhones) return false; + // When the IME switcher dialog is shown, the IME switcher button should be hidden. if (mMenuController.getSwitchingDialogLocked() != null) return false; + // When we are switching IMEs, the IME switcher button should be hidden. + if (!Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) { + return false; + } if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded() && mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId())) { return false; } - if ((visibility & InputMethodService.IME_ACTIVE) == 0 - || (visibility & InputMethodService.IME_INVISIBLE) != 0) { + if ((visibility & InputMethodService.IME_ACTIVE) == 0) { return false; } if (mWindowManagerInternal.isHardKeyboardAvailable()) { @@ -3115,7 +3112,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @SuppressWarnings("deprecation") - private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) { + private void setImeWindowStatus(@NonNull IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId(); synchronized (ImfLock.class) { @@ -3132,7 +3131,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mImeWindowVis = vis; mBackDisposition = backDisposition; - updateSystemUiLocked(vis, backDisposition); + updateSystemUiLocked(mImeWindowVis, mBackDisposition); } final boolean dismissImeOnBackKeyPressed; @@ -3167,37 +3166,46 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private void updateImeWindowStatus(boolean disableImeIcon) { synchronized (ImfLock.class) { - if (disableImeIcon) { - updateSystemUiLocked(0, mBackDisposition); - } else { - updateSystemUiLocked(); - } + // TODO(b/285109020): disableImeIcon should be stored in a property like + // mIsSwitcherIconDisabled, but it is currently not reliably cleared. + updateSystemUiLocked(disableImeIcon ? 0 : mImeWindowVis, mBackDisposition); } } @GuardedBy("ImfLock.class") void updateSystemUiLocked() { + // This is only used by InputMethodMenuController to trigger the IME switcher icon + // visibility, by having {@code shouldShowImeSwitcherLocked} called, which depends on the + // visibility of the IME switcher dialog. updateSystemUiLocked(mImeWindowVis, mBackDisposition); } // Caution! This method is called in this class. Handle multi-user carefully @GuardedBy("ImfLock.class") - private void updateSystemUiLocked(int vis, int backDisposition) { + private void updateSystemUiLocked(@InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { if (getCurTokenLocked() == null) { return; } if (DEBUG) { Slog.d(TAG, "IME window vis: " + vis - + " active: " + (vis & InputMethodService.IME_ACTIVE) - + " inv: " + (vis & InputMethodService.IME_INVISIBLE) + + " active: " + ((vis & InputMethodService.IME_ACTIVE) != 0) + + " visible: " + ((vis & InputMethodService.IME_VISIBLE) != 0) + + " backDisposition: " + backDisposition + + " isInteractive: " + mIsInteractive + + " curPerceptible: " + mCurPerceptible + " displayId: " + mCurTokenDisplayId); } // TODO: Move this clearing calling identity block to setImeWindowStatus after making sure - // all updateSystemUi happens on system privilege. + // all updateSystemUi happens on system privilege. final long ident = Binder.clearCallingIdentity(); try { - if (!mCurPerceptible) { + if (!mIsInteractive) { + // When we are not interactive, + // the visibility should be 0 (no IME icons should be shown). + vis = 0; + } else if (!mCurPerceptible) { if ((vis & InputMethodService.IME_VISIBLE) != 0) { vis &= ~InputMethodService.IME_VISIBLE; vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE; @@ -3205,7 +3213,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } else { vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE; } - // mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked(). + if (mMenuController.getSwitchingDialogLocked() != null + || !Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) { + // When the IME switcher dialog is shown, or we are switching IMEs, + // the back button should be in the default state (as if the IME is not shown). + backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING; + } final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis); if (mStatusBarManagerInternal != null) { mStatusBarManagerInternal.setImeWindowStatus(mCurTokenDisplayId, @@ -3527,7 +3540,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return; } mCurPerceptible = perceptible; - updateSystemUiLocked(); + updateSystemUiLocked(mImeWindowVis, mBackDisposition); } }); } @@ -5089,8 +5102,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private void handleSetInteractive(final boolean interactive) { synchronized (ImfLock.class) { + if (mIsInteractive == interactive) { + return; + } mIsInteractive = interactive; - updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition); + updateSystemUiLocked(mImeWindowVis, mBackDisposition); // Inform the current client of the change in active status if (mCurClient == null || mCurClient.mClient == null) { @@ -5769,7 +5785,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // input target changed, in case seeing the dialog dismiss flickering during // the next focused window starting the input connection. if (mLastImeTargetWindow != mCurFocusedWindow) { - mMenuController.hideInputMethodMenu(); + mMenuController.hideInputMethodMenuLocked(); } } } @@ -6725,7 +6741,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void setImeWindowStatusAsync(int vis, int backDisposition) { + public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { mImms.setImeWindowStatus(mToken, vis, backDisposition); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java index c212e8e3c82c..c2ef83d06690 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java @@ -175,13 +175,13 @@ final class InputMethodMenuController { int subtypeId = mSubtypeIds[which]; adapter.mCheckedItem = which; adapter.notifyDataSetChanged(); - hideInputMethodMenu(); if (im != null) { if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) { subtypeId = NOT_A_SUBTYPE_ID; } mService.setInputMethodLocked(im.getId(), subtypeId); } + hideInputMethodMenuLocked(); } }; mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener); @@ -220,12 +220,18 @@ final class InputMethodMenuController { } } + /** + * Hides the input method switcher menu. + */ void hideInputMethodMenu() { synchronized (ImfLock.class) { hideInputMethodMenuLocked(); } } + /** + * Hides the input method switcher menu, synchronised version of {@link #hideInputMethodMenu}. + */ @GuardedBy("ImfLock.class") void hideInputMethodMenuLocked() { if (DEBUG) Slog.v(TAG, "Hide switching menu"); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 47f648547a88..9185a00da570 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -16,6 +16,7 @@ package com.android.server.media; +import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED; import static android.media.VolumeProvider.VOLUME_CONTROL_ABSOLUTE; import static android.media.VolumeProvider.VOLUME_CONTROL_FIXED; import static android.media.VolumeProvider.VOLUME_CONTROL_RELATIVE; @@ -44,7 +45,9 @@ import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; import android.media.MediaMetadata; +import android.media.MediaRouter2Manager; import android.media.Rating; +import android.media.RoutingSessionInfo; import android.media.VolumeProvider; import android.media.session.ISession; import android.media.session.ISessionCallback; @@ -510,7 +513,33 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR @Override public boolean canHandleVolumeKey() { - return mVolumeControlType != VOLUME_CONTROL_FIXED; + if (isPlaybackTypeLocal()) { + return true; + } + if (mVolumeControlType == VOLUME_CONTROL_FIXED) { + return false; + } + if (mVolumeAdjustmentForRemoteGroupSessions) { + return true; + } + // See b/228021646 for details. + MediaRouter2Manager mRouter2Manager = MediaRouter2Manager.getInstance(mContext); + List<RoutingSessionInfo> sessions = mRouter2Manager.getRoutingSessions(mPackageName); + boolean foundNonSystemSession = false; + boolean remoteSessionAllowVolumeAdjustment = true; + for (RoutingSessionInfo session : sessions) { + if (!session.isSystemSession()) { + foundNonSystemSession = true; + if (session.getVolumeHandling() == PLAYBACK_VOLUME_FIXED) { + remoteSessionAllowVolumeAdjustment = false; + } + } + } + if (!foundNonSystemSession) { + Log.d(TAG, "Package " + mPackageName + + " has a remote media session but no associated routing session"); + } + return foundNonSystemSession && remoteSessionAllowVolumeAdjustment; } @Override diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 5324acd565dd..65e34e682724 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -1078,7 +1078,7 @@ public final class MediaProjectionManagerService extends SystemService // Tear down projection here; necessary to ensure (among other reasons) that // stop is dispatched to client and cast icon disappears from status bar. mProjectionGrant.stop(); - throw new IllegalStateException("Don't re-use the resultData to retrieve " + throw new SecurityException("Don't re-use the resultData to retrieve " + "the same projection instance, and don't use a token that has " + "timed out. Don't take multiple captures by invoking " + "MediaProjection#createVirtualDisplay multiple times on the " diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7c3ff7ef39a9..5b3227388460 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -8715,13 +8715,7 @@ public class NotificationManagerService extends SystemService { ToastRecord lastToast = mToastQueue.remove(index); - mWindowManagerInternal.removeWindowToken(lastToast.windowToken, false /* removeWindows */, - lastToast.displayId); - // We passed 'false' for 'removeWindows' so that the client has time to stop - // rendering (as hide above is a one-way message), otherwise we could crash - // a client which was actively using a surface made from the token. However - // we need to schedule a timeout to make sure the token is eventually killed - // one way or another. + // We need to schedule a timeout to make sure the token is eventually killed scheduleKillTokenTimeout(lastToast); keepProcessAliveForToastIfNeededLocked(record.pid); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index abfc1d7ff2d2..78f1fa60b69f 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -4311,7 +4311,12 @@ public class ComputerEngine implements Computer { if (Process.isIsolatedUid(uid) && mPermissionManager.getHotwordDetectionServiceProvider() != null && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) { - uid = getIsolatedOwner(uid); + try { + uid = getIsolatedOwner(uid); + } catch (IllegalStateException e) { + // If the owner uid doesn't exist, just use the current uid + Slog.wtf(TAG, "Expected isolated uid " + uid + " to have an owner", e); + } } final int callingUserId = UserHandle.getUserId(callingUid); final int appId = UserHandle.getAppId(uid); @@ -4352,7 +4357,12 @@ public class ComputerEngine implements Computer { if (Process.isIsolatedUid(uid) && mPermissionManager.getHotwordDetectionServiceProvider() != null && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) { - uid = getIsolatedOwner(uid); + try { + uid = getIsolatedOwner(uid); + } catch (IllegalStateException e) { + // If the owner uid doesn't exist, just use the current uid + Slog.wtf(TAG, "Expected isolated uid " + uid + " to have an owner", e); + } } final int appId = UserHandle.getAppId(uid); final Object obj = mSettings.getSettingBase(appId); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 3d72bae03012..a792b9c0dbdb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3320,12 +3320,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in" + " interceptKeyBeforeQueueing"); return key_consumed; - case KeyEvent.KEYCODE_MACRO_1: - case KeyEvent.KEYCODE_MACRO_2: - case KeyEvent.KEYCODE_MACRO_3: - case KeyEvent.KEYCODE_MACRO_4: - Slog.wtf(TAG, "KEYCODE_MACRO_x should be handled in interceptKeyBeforeQueueing"); - return key_consumed; } if (isValidGlobalKey(keyCode) @@ -4475,7 +4469,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_MACRO_2: case KeyEvent.KEYCODE_MACRO_3: case KeyEvent.KEYCODE_MACRO_4: - // TODO(b/266098478): Add logic to handle KEYCODE_MACROx feature result &= ~ACTION_PASS_TO_USER; break; } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index efd8b6d9a943..6e9a22c7872b 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.statusbar; import android.annotation.Nullable; import android.app.ITransientNotificationCallback; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; +import android.inputmethodservice.InputMethodService; import android.os.Bundle; import android.os.IBinder; import android.view.WindowInsets.Type.InsetsType; @@ -54,13 +55,13 @@ public interface StatusBarManagerInternal { * @param displayId The display to which the IME is bound to. * @param token The IME token. * @param vis Bit flags about the IME visibility. - * (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE}) * @param backDisposition Bit flags about the IME back disposition. - * (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT}) * @param showImeSwitcher {@code true} when the IME switcher button should be shown. */ - void setImeWindowStatus(int displayId, IBinder token, int vis, - int backDisposition, boolean showImeSwitcher); + void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, + boolean showImeSwitcher); /** * See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}. diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 044d30b368da..719b2d2f0355 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -59,6 +59,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; +import android.inputmethodservice.InputMethodService; import android.media.INearbyMediaDevicesProvider; import android.media.MediaRoute2Info; import android.net.Uri; @@ -516,7 +517,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { StatusBarManagerService.this.setImeWindowStatus(displayId, token, vis, backDisposition, showImeSwitcher); @@ -1221,12 +1224,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void setImeWindowStatus(int displayId, final IBinder token, final int vis, - final int backDisposition, final boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, final IBinder token, + @InputMethodService.ImeWindowVisibility final int vis, + @InputMethodService.BackDispositionMode final int backDisposition, + final boolean showImeSwitcher) { enforceStatusBar(); if (SPEW) { - Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition); + Slog.d(TAG, "setImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition); } synchronized(mLock) { @@ -1289,7 +1294,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private String mPackageName = "none"; private int mDisabled1 = 0; private int mDisabled2 = 0; + @InputMethodService.ImeWindowVisibility private int mImeWindowVis = 0; + @InputMethodService.BackDispositionMode private int mImeBackDisposition = 0; private boolean mShowImeSwitcher = false; private IBinder mImeToken = null; @@ -1334,7 +1341,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D return mDisabled1 == disabled1 && mDisabled2 == disabled2; } - private void setImeWindowState(final int vis, final int backDisposition, + private void setImeWindowState(@InputMethodService.ImeWindowVisibility final int vis, + @InputMethodService.BackDispositionMode final int backDisposition, final boolean showImeSwitcher, final IBinder token) { mImeWindowVis = vis; mImeBackDisposition = backDisposition; diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java index 49b125cd49e8..b773ade09b89 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java @@ -81,7 +81,7 @@ class WallpaperCropper { if (DEBUG) { Slog.v(TAG, "Generating crop for new wallpaper(s): 0x" + Integer.toHexString(wallpaper.mWhich) - + " to " + wallpaper.cropFile.getName() + + " to " + wallpaper.getCropFile().getName() + " crop=(" + cropHint.width() + 'x' + cropHint.height() + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')'); } @@ -89,7 +89,7 @@ class WallpaperCropper { // Analyse the source; needed in multiple cases BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options); + BitmapFactory.decodeFile(wallpaper.getWallpaperFile().getAbsolutePath(), options); if (options.outWidth <= 0 || options.outHeight <= 0) { Slog.w(TAG, "Invalid wallpaper data"); success = false; @@ -154,11 +154,10 @@ class WallpaperCropper { // may be we can try to remove this optimized way in the future, // that means, we will always go into the 'else' block. - success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); + success = FileUtils.copyFile(wallpaper.getWallpaperFile(), wallpaper.getCropFile()); if (!success) { - wallpaper.cropFile.delete(); - // TODO: fall back to default wallpaper in this case + wallpaper.getCropFile().delete(); } if (DEBUG) { @@ -226,7 +225,7 @@ class WallpaperCropper { //Create a record file and will delete if ImageDecoder work well. final String recordName = - (wallpaper.wallpaperFile.getName().equals(WALLPAPER) + (wallpaper.getWallpaperFile().getName().equals(WALLPAPER) ? RECORD_FILE : RECORD_LOCK_FILE); final File record = new File(getWallpaperDir(wallpaper.userId), recordName); record.createNewFile(); @@ -234,7 +233,7 @@ class WallpaperCropper { + ", record name =" + record.getName()); final ImageDecoder.Source srcData = - ImageDecoder.createSource(wallpaper.wallpaperFile); + ImageDecoder.createSource(wallpaper.getWallpaperFile()); final int sampleSize = scale; Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> { decoder.setTargetSampleSize(sampleSize); @@ -257,7 +256,7 @@ class WallpaperCropper { + " h=" + finalCrop.getHeight()); } - f = new FileOutputStream(wallpaper.cropFile); + f = new FileOutputStream(wallpaper.getCropFile()); bos = new BufferedOutputStream(f, 32 * 1024); finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos); // don't rely on the implicit flush-at-close when noting success @@ -277,11 +276,11 @@ class WallpaperCropper { if (!success) { Slog.e(TAG, "Unable to apply new wallpaper"); - wallpaper.cropFile.delete(); + wallpaper.getCropFile().delete(); } - if (wallpaper.cropFile.exists()) { - boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile()); + if (wallpaper.getCropFile().exists()) { + boolean didRestorecon = SELinux.restorecon(wallpaper.getCropFile().getAbsoluteFile()); if (DEBUG) { Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java index d87fca4d3c71..b0b66cfa247f 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperData.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java @@ -40,10 +40,7 @@ import java.io.File; */ class WallpaperData { - int userId; - - final File wallpaperFile; // source image - final File cropFile; // eventual destination + final int userId; /** * True while the client is writing a new wallpaper @@ -133,14 +130,13 @@ class WallpaperData { */ final Rect cropHint = new Rect(0, 0, 0, 0); + // map of which -> File + private final SparseArray<File> mWallpaperFiles = new SparseArray<>(); + private final SparseArray<File> mCropFiles = new SparseArray<>(); + WallpaperData(int userId, @SetWallpaperFlags int wallpaperType) { this.userId = userId; this.mWhich = wallpaperType; - File wallpaperDir = getWallpaperDir(userId); - String wallpaperFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER; - String cropFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP; - this.wallpaperFile = new File(wallpaperDir, wallpaperFileName); - this.cropFile = new File(wallpaperDir, cropFileName); } /** @@ -154,8 +150,6 @@ class WallpaperData { */ WallpaperData(WallpaperData source) { this.userId = source.userId; - this.wallpaperFile = source.wallpaperFile; - this.cropFile = source.cropFile; this.wallpaperComponent = source.wallpaperComponent; this.mWhich = source.mWhich; this.wallpaperId = source.wallpaperId; @@ -169,6 +163,25 @@ class WallpaperData { } } + File getWallpaperFile() { + String fileName = mWhich == FLAG_LOCK ? WALLPAPER_LOCK_ORIG : WALLPAPER; + return getFile(mWallpaperFiles, fileName); + } + + File getCropFile() { + String fileName = mWhich == FLAG_LOCK ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP; + return getFile(mCropFiles, fileName); + } + + private File getFile(SparseArray<File> map, String fileName) { + File result = map.get(mWhich); + if (result == null) { + result = new File(getWallpaperDir(userId), fileName); + map.put(userId, result); + } + return result; + } + @Override public String toString() { StringBuilder out = new StringBuilder(defaultString(this)); @@ -177,7 +190,7 @@ class WallpaperData { out.append(", which: "); out.append(mWhich); out.append(", file mod: "); - out.append(wallpaperFile != null ? wallpaperFile.lastModified() : "null"); + out.append(getWallpaperFile() != null ? getWallpaperFile().lastModified() : "null"); if (connection == null) { out.append(", no connection"); } else { @@ -202,10 +215,10 @@ class WallpaperData { // Called during initialization of a given user's wallpaper bookkeeping boolean cropExists() { - return cropFile.exists(); + return getCropFile().exists(); } boolean sourceExists() { - return wallpaperFile.exists(); + return getWallpaperFile().exists(); } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java index 1133dba27ace..c54e3bdf0d51 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java @@ -542,12 +542,12 @@ class WallpaperDataParser { } res = r.openRawResource(resId); - if (wallpaper.wallpaperFile.exists()) { - wallpaper.wallpaperFile.delete(); - wallpaper.cropFile.delete(); + if (wallpaper.getWallpaperFile().exists()) { + wallpaper.getWallpaperFile().delete(); + wallpaper.getCropFile().delete(); } - fos = new FileOutputStream(wallpaper.wallpaperFile); - cos = new FileOutputStream(wallpaper.cropFile); + fos = new FileOutputStream(wallpaper.getWallpaperFile()); + cos = new FileOutputStream(wallpaper.getCropFile()); byte[] buffer = new byte[32768]; int amt; diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 322083ce4cb6..9b2cdd72a338 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -670,8 +670,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Not having a wallpaperComponent means it's a lock screen wallpaper. final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent) || wallpaper.wallpaperComponent == null; - if (imageWallpaper && wallpaper.cropFile != null && wallpaper.cropFile.exists()) { - cropFile = wallpaper.cropFile.getAbsolutePath(); + if (imageWallpaper && wallpaper.getCropFile().exists()) { + cropFile = wallpaper.getCropFile().getAbsolutePath(); } else if (imageWallpaper && !wallpaper.cropExists() && !wallpaper.sourceExists()) { defaultImageWallpaper = true; } @@ -1794,8 +1794,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private boolean clearWallpaperBitmaps(WallpaperData wallpaper) { boolean sourceExists = wallpaper.sourceExists(); boolean cropExists = wallpaper.cropExists(); - if (sourceExists) wallpaper.wallpaperFile.delete(); - if (cropExists) wallpaper.cropFile.delete(); + if (sourceExists) wallpaper.getWallpaperFile().delete(); + if (cropExists) wallpaper.getCropFile().delete(); return sourceExists || cropExists; } @@ -2394,13 +2394,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.callbacks.register(cb); } - File fileToReturn = getCropped ? wallpaper.cropFile : wallpaper.wallpaperFile; + File result = getCropped ? wallpaper.getCropFile() : wallpaper.getWallpaperFile(); - if (!fileToReturn.exists()) { + if (!result.exists()) { return null; } - return ParcelFileDescriptor.open(fileToReturn, MODE_READ_ONLY); + return ParcelFileDescriptor.open(result, MODE_READ_ONLY); } catch (FileNotFoundException e) { /* Shouldn't happen as we check to see if the file exists */ Slog.w(TAG, "Error getting wallpaper", e); @@ -3161,16 +3161,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Migrate the bitmap files outright; no need to copy try { - if (!mIsLockscreenLiveWallpaperEnabled || sysWP.wallpaperFile.exists()) { - Os.rename(sysWP.wallpaperFile.getAbsolutePath(), - lockWP.wallpaperFile.getAbsolutePath()); + if (!mIsLockscreenLiveWallpaperEnabled || sysWP.getWallpaperFile().exists()) { + Os.rename(sysWP.getWallpaperFile().getAbsolutePath(), + lockWP.getWallpaperFile().getAbsolutePath()); } - if (!mIsLockscreenLiveWallpaperEnabled || sysWP.cropFile.exists()) { - Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath()); + if (!mIsLockscreenLiveWallpaperEnabled || sysWP.getCropFile().exists()) { + Os.rename(sysWP.getCropFile().getAbsolutePath(), + lockWP.getCropFile().getAbsolutePath()); } mLockWallpaperMap.put(userId, lockWP); if (mIsLockscreenLiveWallpaperEnabled) { - SELinux.restorecon(lockWP.wallpaperFile); + SELinux.restorecon(lockWP.getWallpaperFile()); mLastLockWallpaper = lockWP; } } catch (ErrnoException e) { @@ -3191,11 +3192,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, -1, -1); } - ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile, + ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.getWallpaperFile(), MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE); - if (!SELinux.restorecon(wallpaper.wallpaperFile)) { + if (!SELinux.restorecon(wallpaper.getWallpaperFile())) { Slog.w(TAG, "restorecon failed for wallpaper file: " + - wallpaper.wallpaperFile.getPath()); + wallpaper.getWallpaperFile().getPath()); return null; } wallpaper.name = name; @@ -3206,7 +3207,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Nullify field to require new computation wallpaper.primaryColors = null; Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId - + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); + + " name=" + name + " file=" + wallpaper.getWallpaperFile().getName()); return fd; } catch (FileNotFoundException e) { Slog.w(TAG, "Error setting wallpaper", e); @@ -3370,7 +3371,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData wallpaper; synchronized (mLock) { - Slog.v(TAG, "setWallpaperComponent name=" + name); + Slog.v(TAG, "setWallpaperComponent name=" + name + ", which=" + which); wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); @@ -3401,7 +3402,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.mWhich = which; wallpaper.fromForegroundApp = isFromForegroundApp(callingPackage); boolean same = changingToSame(name, wallpaper); - if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { + + // force rebind when reapplying a system-only wallpaper to system+lock + boolean forceRebind = same && mLockWallpaperMap.get(userId) != null + && which == (FLAG_SYSTEM | FLAG_LOCK); + if (bindWallpaperComponentLocked(name, forceRebind, true, wallpaper, null)) { if (!same) { wallpaper.primaryColors = null; } else { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 3ba410c65ab8..4262e94e2894 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1588,21 +1588,14 @@ class ActivityStarter { newTransition = null; } } - if (isTransientLaunch) { - if (forceTransientTransition) { - transitionController.collect(mLastStartActivityRecord); - transitionController.collect(mPriorAboveTask); - } - // `started` isn't guaranteed to be the actual relevant activity, so we must wait - // until after we launched to identify the relevant activity. - transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask); - if (forceTransientTransition) { - final DisplayContent dc = mLastStartActivityRecord.getDisplayContent(); - // update wallpaper target to TransientHide - dc.mWallpaperController.adjustWallpaperWindows(); - // execute transition because there is no change - transitionController.setReady(dc, true /* ready */); - } + if (forceTransientTransition) { + transitionController.collect(mLastStartActivityRecord); + transitionController.collect(mPriorAboveTask); + final DisplayContent dc = mLastStartActivityRecord.getDisplayContent(); + // update wallpaper target to TransientHide + dc.mWallpaperController.adjustWallpaperWindows(); + // execute transition because there is no change + transitionController.setReady(dc, true /* ready */); } if (!userLeaving) { // no-user-leaving implies not entering PiP. @@ -1712,6 +1705,7 @@ class ActivityStarter { activity.destroyIfPossible("Removes redundant singleInstance"); } } + recordTransientLaunchIfNeeded(targetTaskTop); // Recycle the target task for this launch. startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants); if (startResult != START_SUCCESS) { @@ -1743,6 +1737,9 @@ class ActivityStarter { addOrReparentStartingActivity(targetTask, "adding to task"); } + // After activity is attached to task, but before actual start + recordTransientLaunchIfNeeded(mLastStartActivityRecord); + if (!mAvoidMoveToFront && mDoResume) { mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask); if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming() @@ -1839,6 +1836,14 @@ class ActivityStarter { return START_SUCCESS; } + private void recordTransientLaunchIfNeeded(ActivityRecord r) { + if (r == null || !mTransientLaunch) return; + final TransitionController controller = r.mTransitionController; + if (controller.isCollecting() && !controller.isTransientCollect(r)) { + controller.setTransientLaunch(r, mPriorAboveTask); + } + } + /** Returns the leaf task where the target activity may be placed. */ private Task computeTargetTask() { if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java index db4762e5f877..857e03d71f3f 100644 --- a/services/core/java/com/android/server/wm/DeviceStateController.java +++ b/services/core/java/com/android/server/wm/DeviceStateController.java @@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.content.Context; import android.util.ArrayMap; +import android.util.Pair; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -165,19 +166,28 @@ final class DeviceStateController { // Make a copy here because it's possible that the consumer tries to remove a callback // while we're still iterating through the list, which would end up in a - // ConcurrentModificationException. - final List<Map.Entry<Consumer<DeviceState>, Executor>> entries = new ArrayList<>(); - synchronized (mWmLock) { - for (Map.Entry<Consumer<DeviceState>, Executor> entry - : mDeviceStateCallbacks.entrySet()) { - entries.add(entry); - } - } + // ConcurrentModificationException. Note that cannot use a List<Map.Entry> because the + // entries are tied to the backing map. So, if a client removes a callback while + // we are notifying clients, we will get a NPE. + final List<Pair<Consumer<DeviceState>, Executor>> entries = copyDeviceStateCallbacks(); for (int i = 0; i < entries.size(); i++) { - Map.Entry<Consumer<DeviceState>, Executor> entry = entries.get(i); - entry.getValue().execute(() -> entry.getKey().accept(mCurrentDeviceState)); + final Pair<Consumer<DeviceState>, Executor> entry = entries.get(i); + entry.second.execute(() -> entry.first.accept(deviceState)); } } } + + @VisibleForTesting + @NonNull + List<Pair<Consumer<DeviceState>, Executor>> copyDeviceStateCallbacks() { + final List<Pair<Consumer<DeviceState>, Executor>> entries = new ArrayList<>(); + + synchronized (mWmLock) { + mDeviceStateCallbacks.forEach((deviceStateConsumer, executor) -> { + entries.add(new Pair<>(deviceStateConsumer, executor)); + }); + } + return entries; + } } diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index 8660becf56a9..89f044bdd163 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -178,6 +178,10 @@ class Dimmer { mSurfaceAnimatorStarter = surfaceAnimatorStarter; } + WindowContainer<?> getHost() { + return mHost; + } + private SurfaceControl makeDimLayer() { return mHost.makeChildSurface(null) .setParent(mHost.getSurfaceControl()) diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 9f59f5a30caf..f81e5d453434 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -790,7 +790,8 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer // on the display level fades out. - if (forAllTasks(task -> !task.canAffectSystemUiFlags())) { + if (!mTransitionController.isShellTransitionsEnabled() + && forAllTasks(task -> !task.canAffectSystemUiFlags())) { mDimmer.resetDimStates(); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ef19eef22794..2dc133f060ff 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1214,8 +1214,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplayRotationCompatPolicy = // Not checking DeviceConfig value here to allow enabling via DeviceConfig // without the need to restart the device. - mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ false) + mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime() ? new DisplayRotationCompatPolicy(this) : null; mRotationReversionController = new DisplayRotationReversionController(this); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 45e4991fbf97..a02fd11ba832 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1527,9 +1527,14 @@ public class DisplayPolicy { } } else if (win.isDimming()) { if (mStatusBar != null) { - if (addStatusBarAppearanceRegionsForDimmingWindow( - win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS, - mStatusBar.getFrame(), win.getBounds(), win.getFrame())) { + // If the dim window is below status bar window, we should update the appearance + // region if needed. Otherwise, leave it as it is. + final int statusBarLayer = mStatusBar.mToken.getWindowLayerFromType(); + final int targetWindowLayer = win.mToken.getWindowLayerFromType(); + if (targetWindowLayer < statusBarLayer + && addStatusBarAppearanceRegionsForDimmingWindow( + win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS, + mStatusBar.getFrame(), win.getBounds(), win.getFrame())) { addSystemBarColorApp(win); } } @@ -2371,7 +2376,8 @@ public class DisplayPolicy { // We need to force the consumption of the system bars if they are force shown or if they // are controlled by a remote insets controller. mForceConsumeSystemBars = mForceShowSystemBars - || mDisplayContent.getInsetsPolicy().remoteInsetsControllerControlsSystemBars(win); + || getInsetsPolicy().remoteInsetsControllerControlsSystemBars(win) + || getInsetsPolicy().forcesShowingNavigationBars(win); mDisplayContent.getInsetsPolicy().updateBarControlTarget(win); final boolean topAppHidesStatusBar = topAppHidesSystemBar(Type.statusBars()); @@ -2582,7 +2588,8 @@ public class DisplayPolicy { if (win == null) { return false; } - if (win == getNotificationShade() || win.isActivityTypeDream()) { + if (win.mPolicy.getWindowLayerLw(win) > win.mPolicy.getWindowLayerFromTypeLw( + WindowManager.LayoutParams.TYPE_STATUS_BAR) || win.isActivityTypeDream()) { return false; } return getInsetsPolicy().hasHiddenSources(Type.navigationBars()); diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index 2b34bb22729d..f96f99d50053 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -336,8 +336,7 @@ final class DisplayRotationCompatPolicy { * </ul> */ private boolean isTreatmentEnabledForDisplay() { - return mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true) + return mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled() && mDisplayContent.getIgnoreOrientationRequest() // TODO(b/225928882): Support camera compat rotation for external displays && mDisplayContent.getDisplay().getType() == TYPE_INTERNAL; diff --git a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java index 74494ddd9f59..de70c4df7985 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java @@ -44,7 +44,7 @@ final class DisplayRotationImmersiveAppCompatPolicy { @NonNull final DisplayRotation displayRotation, @NonNull final DisplayContent displayContent) { if (!letterboxConfiguration - .isDisplayRotationImmersiveAppCompatPolicyEnabled(/* checkDeviceConfig */ false)) { + .isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime()) { return null; } @@ -87,8 +87,7 @@ final class DisplayRotationImmersiveAppCompatPolicy { * @return {@code true}, if there is a need to lock screen rotation, {@code false} otherwise. */ boolean isRotationLockEnforced(@Surface.Rotation final int proposedRotation) { - if (!mLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled( - /* checkDeviceConfig */ true)) { + if (!mLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled()) { return false; } synchronized (mDisplayContent.mWmService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index cea886ff6e87..825d38b3eed7 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -49,6 +49,7 @@ import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS; import static java.lang.Integer.MAX_VALUE; import android.annotation.Nullable; +import android.graphics.Rect; import android.graphics.Region; import android.os.Handler; import android.os.IBinder; @@ -558,7 +559,8 @@ final class InputMonitor { private boolean mAddWallpaperInputConsumerHandle; private boolean mAddRecentsAnimationInputConsumerHandle; - boolean mInDrag; + private boolean mInDrag; + private final Rect mTmpRect = new Rect(); private void updateInputWindows(boolean inDrag) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); @@ -582,8 +584,11 @@ final class InputMonitor { layer = layer != null ? layer : activeRecents; // Handle edge-case for SUW where windows don't exist yet if (layer.getSurfaceControl() != null) { - mRecentsAnimationInputConsumer.mWindowHandle - .replaceTouchableRegionWithCrop(layer.getSurfaceControl()); + final WindowState targetAppMainWindow = activeRecents.findMainWindow(); + if (targetAppMainWindow != null) { + targetAppMainWindow.getBounds(mTmpRect); + mRecentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); + } mRecentsAnimationInputConsumer.show(mInputTransaction, layer); mAddRecentsAnimationInputConsumerHandle = false; } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 698c9abee1f5..798dc85ec11b 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -567,8 +567,7 @@ class InsetsPolicy { return focusedWin; } } - if (mPolicy.isForceShowNavigationBarEnabled() && focusedWin != null - && focusedWin.getActivityType() == ACTIVITY_TYPE_STANDARD) { + if (forcesShowingNavigationBars(focusedWin)) { // When "force show navigation bar" is enabled, it means both force visible is true, and // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could @@ -604,6 +603,11 @@ class InsetsPolicy { return focusedWin; } + boolean forcesShowingNavigationBars(WindowState win) { + return mPolicy.isForceShowNavigationBarEnabled() && win != null + && win.getActivityType() == ACTIVITY_TYPE_STANDARD; + } + /** * Determines whether the remote insets controller should take control of system bars for all * windows. diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index cfb9534b558e..fda22cab6276 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -78,6 +78,13 @@ final class LetterboxConfiguration { private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true; + // Whether per-app user aspect ratio override settings is enabled + private static final String KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS = + "enable_app_compat_user_aspect_ratio_settings"; + + // TODO(b/288142656): Enable user aspect ratio settings by default. + private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS = false; + /** * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with * set-fixed-orientation-letterbox-aspect-ratio or via {@link @@ -242,6 +249,9 @@ final class LetterboxConfiguration { // Allows to enable letterboxing strategy for translucent activities ignoring flags. private boolean mTranslucentLetterboxingOverrideEnabled; + // Allows to enable user aspect ratio settings ignoring flags. + private boolean mUserAppAspectRatioSettingsOverrideEnabled; + // Whether we should use split screen aspect ratio for the activity when camera compat treatment // is enabled and activity is connected to the camera in fullscreen. private final boolean mIsCameraCompatSplitScreenAspectRatioEnabled; @@ -345,6 +355,10 @@ final class LetterboxConfiguration { DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY, mContext.getResources().getBoolean( R.bool.config_letterboxIsEnabledForTranslucentActivities)) + .addDeviceConfigEntry(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, + DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS, + mContext.getResources().getBoolean( + R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled)) .build(); } @@ -1125,15 +1139,19 @@ final class LetterboxConfiguration { } /** - * Whether camera compatibility treatment is enabled. - * - * @param checkDeviceConfig whether it should check both build time flag and a dynamic property - * from {@link DeviceConfig} or only build time flag value. + * @return Whether camera compatibility treatment is currently enabled. + */ + boolean isCameraCompatTreatmentEnabled() { + return mDeviceConfig.getFlagValue(KEY_ENABLE_CAMERA_COMPAT_TREATMENT); + } + + /** + * @return Whether camera compatibility treatment is enabled at build time. This is used when + * we need to safely initialize a component before the {@link DeviceConfig} flag value is + * available. */ - boolean isCameraCompatTreatmentEnabled(boolean checkDeviceConfig) { - return mDeviceConfig.isBuildTimeFlagEnabled(KEY_ENABLE_CAMERA_COMPAT_TREATMENT) - && (!checkDeviceConfig - || mDeviceConfig.getFlagValue(KEY_ENABLE_CAMERA_COMPAT_TREATMENT)); + boolean isCameraCompatTreatmentEnabledAtBuildTime() { + return mDeviceConfig.isBuildTimeFlagEnabled(KEY_ENABLE_CAMERA_COMPAT_TREATMENT); } /** Whether camera compatibility refresh is enabled. */ @@ -1179,20 +1197,48 @@ final class LetterboxConfiguration { /** * Checks whether rotation compat policy for immersive apps that prevents auto rotation - * into non-optimal screen orientation while in fullscreen is enabled. + * into non-optimal screen orientation while in fullscreen is enabled at build time. This is + * used when we need to safely initialize a component before the {@link DeviceConfig} flag + * value is available. * * <p>This is needed because immersive apps, such as games, are often not optimized for all * orientations and can have a poor UX when rotated. Additionally, some games rely on sensors * for the gameplay so users can trigger such rotations accidentally when auto rotation is on. - * - * @param checkDeviceConfig whether it should check both build time flag and a dynamic property - * from {@link DeviceConfig} or only build time flag value. */ - boolean isDisplayRotationImmersiveAppCompatPolicyEnabled(final boolean checkDeviceConfig) { + boolean isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime() { return mDeviceConfig.isBuildTimeFlagEnabled( - KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY) && (!checkDeviceConfig - || mDeviceConfig.getFlagValue( - KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY)); + KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY); + } + + /** + * Checks whether rotation compat policy for immersive apps that prevents auto rotation + * into non-optimal screen orientation while in fullscreen is currently enabled. + * + * <p>This is needed because immersive apps, such as games, are often not optimized for all + * orientations and can have a poor UX when rotated. Additionally, some games rely on sensors + * for the gameplay so users can trigger such rotations accidentally when auto rotation is on. + */ + boolean isDisplayRotationImmersiveAppCompatPolicyEnabled() { + return mDeviceConfig.getFlagValue(KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY); + } + + /** + * Whether per-app user aspect ratio override settings is enabled + */ + boolean isUserAppAspectRatioSettingsEnabled() { + return mUserAppAspectRatioSettingsOverrideEnabled + || mDeviceConfig.getFlagValue(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS); + } + + void setUserAppAspectRatioSettingsOverrideEnabled(boolean enabled) { + mUserAppAspectRatioSettingsOverrideEnabled = enabled; } + /** + * Resets whether per-app user aspect ratio override settings is enabled + * {@code mDeviceConfig.getFlagValue(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS)}. + */ + void resetUserAppAspectRatioSettingsEnabled() { + setUserAppAspectRatioSettingsOverrideEnabled(false); + } } diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java index 3b10debaa753..756339701590 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java +++ b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.os.StrictMode.setThreadPolicy; + import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -23,6 +25,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.Environment; +import android.os.StrictMode; +import android.os.StrictMode.ThreadPolicy; import android.util.AtomicFile; import android.util.Slog; @@ -125,7 +129,7 @@ class LetterboxConfigurationPersister { final File prefFiles = new File(configFolder, LETTERBOX_CONFIGURATION_FILENAME); mConfigurationFile = new AtomicFile(prefFiles); mPersisterQueue = persisterQueue; - readCurrentConfiguration(); + runWithDiskReadsThreadPolicy(this::readCurrentConfiguration); } /** @@ -275,6 +279,20 @@ class LetterboxConfigurationPersister { } } + // The LetterboxConfigurationDeviceConfig needs to access the + // file with the current reachability position once when the + // device boots. Because DisplayThread uses allowIo=false + // accessing a file triggers a DiskReadViolation. + // Here we use StrictMode to allow the current thread to read + // the AtomicFile once in the current thread restoring the + // original ThreadPolicy after that. + private void runWithDiskReadsThreadPolicy(Runnable runnable) { + final ThreadPolicy currentPolicy = StrictMode.getThreadPolicy(); + setThreadPolicy(new ThreadPolicy.Builder().permitDiskReads().build()); + runnable.run(); + setThreadPolicy(currentPolicy); + } + private static class UpdateValuesCommand implements PersisterQueue.WriteQueueItem<UpdateValuesCommand> { diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index c2439888db43..a81683829396 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -294,18 +294,15 @@ final class LetterboxUiController { PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); mBooleanPropertyCameraCompatAllowForceRotation = readComponentProperty(packageManager, mActivityRecord.packageName, - () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true), + () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(), PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION); mBooleanPropertyCameraCompatAllowRefresh = readComponentProperty(packageManager, mActivityRecord.packageName, - () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true), + () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(), PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH); mBooleanPropertyCameraCompatEnableRefreshViaPause = readComponentProperty(packageManager, mActivityRecord.packageName, - () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true), + () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(), PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE); mBooleanPropertyAllowOrientationOverride = @@ -697,7 +694,7 @@ final class LetterboxUiController { boolean shouldRefreshActivityForCameraCompat() { return shouldEnableWithOptOutOverrideAndProperty( /* gatingCondition */ () -> mLetterboxConfiguration - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true), + .isCameraCompatTreatmentEnabled(), mIsOverrideCameraCompatDisableRefreshEnabled, mBooleanPropertyCameraCompatAllowRefresh); } @@ -719,7 +716,7 @@ final class LetterboxUiController { boolean shouldRefreshActivityViaPauseForCameraCompat() { return shouldEnableWithOverrideAndProperty( /* gatingCondition */ () -> mLetterboxConfiguration - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true), + .isCameraCompatTreatmentEnabled(), mIsOverrideCameraCompatEnableRefreshViaPauseEnabled, mBooleanPropertyCameraCompatEnableRefreshViaPause); } @@ -738,7 +735,7 @@ final class LetterboxUiController { boolean shouldForceRotateForCameraCompat() { return shouldEnableWithOptOutOverrideAndProperty( /* gatingCondition */ () -> mLetterboxConfiguration - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true), + .isCameraCompatTreatmentEnabled(), mIsOverrideCameraCompatDisableForceRotationEnabled, mBooleanPropertyCameraCompatAllowForceRotation); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index aad12251502d..0059f0b382f5 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -358,22 +358,30 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mTransientLaunches.put(activity, restoreBelow); setTransientLaunchToChanges(activity); - if (restoreBelow != null) { - final Task transientRootTask = activity.getRootTask(); + final Task transientRootTask = activity.getRootTask(); + final WindowContainer<?> parent = restoreBelow != null ? restoreBelow.getParent() + : (transientRootTask != null ? transientRootTask.getParent() : null); + if (parent != null) { // Collect all visible tasks which can be occluded by the transient activity to // make sure they are in the participants so their visibilities can be updated when // finishing transition. - ((WindowContainer<?>) restoreBelow.getParent()).forAllTasks(t -> { + parent.forAllTasks(t -> { + // Skip transient-launch task + if (t == transientRootTask) return false; if (t.isVisibleRequested() && !t.isAlwaysOnTop() && !t.getWindowConfiguration().tasksAreFloating()) { - if (t.isRootTask() && t != transientRootTask) { + if (t.isRootTask()) { mTransientHideTasks.add(t); } if (t.isLeafTask()) { collect(t); } } - return t == restoreBelow; + return restoreBelow != null + // Stop at the restoreBelow task + ? t == restoreBelow + // Or stop at the last visible task if no restore-below (new task) + : (t.isRootTask() && t.fillsParent()); }); // Add FLAG_ABOVE_TRANSIENT_LAUNCH to the tree of transient-hide tasks, // so ChangeInfo#hasChanged() can return true to report the transition info. @@ -397,6 +405,28 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return false; } + boolean canApplyDim(@NonNull Task task) { + if (mTransientLaunches == null) return true; + final Dimmer dimmer = task.getDimmer(); + final WindowContainer<?> dimmerHost = dimmer != null ? dimmer.getHost() : null; + if (dimmerHost == null) return false; + if (isInTransientHide(dimmerHost)) { + // The layer of dimmer is inside transient-hide task, then allow to dim. + return true; + } + // The dimmer host of a translucent task can be a display, then it is not in transient-hide. + for (int i = mTransientLaunches.size() - 1; i >= 0; --i) { + // The transient task is usually the task of recents/home activity. + final Task transientTask = mTransientLaunches.keyAt(i).getTask(); + if (transientTask != null && transientTask.canAffectSystemUiFlags()) { + // It usually means that the recents animation has moved the transient-hide task + // an noticeable distance, then the display level dimmer should not show. + return false; + } + } + return true; + } + boolean hasTransientLaunch() { return mTransientLaunches != null && !mTransientLaunches.isEmpty(); } @@ -957,7 +987,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } if (ar.pictureInPictureArgs != null && ar.pictureInPictureArgs.isAutoEnterEnabled()) { - if (didCommitTransientLaunch()) { + if (!ar.getTask().isVisibleRequested() || didCommitTransientLaunch()) { // force enable pip-on-task-switch now that we've committed to actually launching // to the transient activity. ar.supportsEnterPipOnTaskSwitch = true; @@ -986,7 +1016,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } // Legacy pip-entry (not via isAutoEnterEnabled). - if (didCommitTransientLaunch() && ar.supportsPictureInPicture()) { + if ((!ar.getTask().isVisibleRequested() || didCommitTransientLaunch()) + && ar.supportsPictureInPicture()) { // force enable pip-on-task-switch now that we've committed to actually launching to the // transient activity, and then recalculate whether we can attempt pip. ar.supportsEnterPipOnTaskSwitch = true; @@ -1224,6 +1255,16 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId); dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */); } + if (mTransientLaunches != null) { + for (int i = mTransientLaunches.size() - 1; i >= 0; --i) { + // Reset the ability of controlling SystemUi which might be changed by + // setTransientLaunch or setRecentsAppBehindSystemBars. + final Task task = mTransientLaunches.keyAt(i).getTask(); + if (task != null) { + task.setCanAffectSystemUiFlags(true); + } + } + } for (int i = 0; i < mTargetDisplays.size(); ++i) { final DisplayContent dc = mTargetDisplays.get(i); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 359b353ba336..a539a4893d4f 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -58,6 +58,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.server.FgThread; import java.util.ArrayList; +import java.util.function.Consumer; import java.util.function.LongConsumer; /** @@ -476,6 +477,19 @@ class TransitionController { return false; } + boolean canApplyDim(@Nullable Task task) { + if (task == null) { + // Always allow non-activity window. + return true; + } + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + if (!mPlayingTransitions.get(i).canApplyDim(task)) { + return false; + } + } + return true; + } + /** * During transient-launch, the "behind" app should retain focus during the transition unless * something takes focus from it explicitly (eg. by calling ATMS.setFocusedTask or by another @@ -1314,18 +1328,18 @@ class TransitionController { return transit; } - /** Returns {@code true} if it started collecting, {@code false} if it was queued. */ - boolean startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Runnable applySync) { + /** Starts the sync set if there is no pending or active syncs, otherwise enqueue the sync. */ + void startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Consumer<Boolean> applySync) { if (!mQueuedTransitions.isEmpty() || mSyncEngine.hasActiveSync()) { // Just add to queue since we already have a queue. - mQueuedTransitions.add(new QueuedTransition(syncGroup, (d) -> applySync.run())); + mQueuedTransitions.add(new QueuedTransition(syncGroup, + (deferred) -> applySync.accept(true /* deferred */))); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Queueing legacy sync-set: %s", syncGroup.mSyncId); - return false; + return; } mSyncEngine.startSyncSet(syncGroup); - applySync.run(); - return true; + applySync.accept(false /* deferred */); } interface OnStartCollect { diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index f54a962e4897..0cf4e89571b5 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -21,7 +21,6 @@ import static android.app.WallpaperManager.COMMAND_UNFREEZE; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; @@ -137,7 +136,8 @@ class WallpaperController { }; private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> { - if (!w.mTransitionController.isShellTransitionsEnabled()) { + final boolean useShellTransition = w.mTransitionController.isShellTransitionsEnabled(); + if (!useShellTransition) { if (w.mActivityRecord != null && !w.mActivityRecord.isVisible() && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) { // If this window's app token is hidden and not animating, it is of no interest. @@ -159,32 +159,25 @@ class WallpaperController { final WindowContainer animatingContainer = w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null; - final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null + if (!useShellTransition && animatingContainer != null && animatingContainer.isAnimating(TRANSITION | PARENTS) && AppTransition.isKeyguardGoingAwayTransitOld(animatingContainer.mTransit) && (animatingContainer.mTransitFlags - & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); - - boolean needsShowWhenLockedWallpaper = false; - if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && mService.mPolicy.isKeyguardLocked()) { - final TransitionController tc = w.mTransitionController; - final boolean isInTransition = tc.isShellTransitionsEnabled() - && tc.inTransition(w); - if (mService.mPolicy.isKeyguardOccluded() || mService.mPolicy.isKeyguardUnoccluding() - || isInTransition) { + & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0) { + // Keep the wallpaper visible when Keyguard is going away. + mFindResults.setUseTopWallpaperAsTarget(true); + } + + if (mService.mPolicy.isKeyguardLocked() && w.canShowWhenLocked()) { + if (mService.mPolicy.isKeyguardOccluded() || (useShellTransition + ? w.inTransition() : mService.mPolicy.isKeyguardUnoccluding())) { // The lowest show when locked window decides whether we need to put the wallpaper // behind. - needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs) + mFindResults.mNeedsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs) || (w.mActivityRecord != null && !w.mActivityRecord.fillsParent()); } } - if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) { - // Keep the wallpaper during Keyguard exit but also when it's needed for a - // non-fullscreen show when locked activity. - mFindResults.setUseTopWallpaperAsTarget(true); - } - final boolean animationWallpaper = animatingContainer != null && animatingContainer.getAnimation() != null && animatingContainer.getAnimation().getShowWallpaper(); @@ -691,7 +684,8 @@ class WallpaperController { private void findWallpaperTarget() { mFindResults.reset(); - if (mDisplayContent.getDefaultTaskDisplayArea() + if (mService.mAtmService.mSupportsFreeformWindowManagement + && mDisplayContent.getDefaultTaskDisplayArea() .isRootTaskVisible(WINDOWING_MODE_FREEFORM)) { // In freeform mode we set the wallpaper as its own target, so we don't need an // additional window to make it visible. @@ -700,6 +694,10 @@ class WallpaperController { mDisplayContent.forAllWindows(mFindWallpapers, true /* traverseTopToBottom */); mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */); + if (mFindResults.mNeedsShowWhenLockedWallpaper) { + // Keep wallpaper visible if the show-when-locked activities doesn't fill screen. + mFindResults.setUseTopWallpaperAsTarget(true); + } if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) { mFindResults.setWallpaperTarget( @@ -1084,6 +1082,7 @@ class WallpaperController { } TopWallpaper mTopWallpaper = new TopWallpaper(); + boolean mNeedsShowWhenLockedWallpaper; boolean useTopWallpaperAsTarget = false; WindowState wallpaperTarget = null; boolean isWallpaperTargetForLetterbox = false; @@ -1132,6 +1131,7 @@ class WallpaperController { void reset() { mTopWallpaper.reset(); + mNeedsShowWhenLockedWallpaper = false; wallpaperTarget = null; useTopWallpaperAsTarget = false; isWallpaperTargetForLetterbox = false; diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index a153708c98d5..05e858de8973 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -1006,13 +1006,16 @@ public class WindowManagerShellCommand extends ShellCommand { runSetBooleanFlag(pw, mLetterboxConfiguration ::setTranslucentLetterboxingOverrideEnabled); break; + case "--isUserAppAspectRatioSettingsEnabled": + runSetBooleanFlag(pw, mLetterboxConfiguration + ::setUserAppAspectRatioSettingsOverrideEnabled); + break; case "--isCameraCompatRefreshEnabled": - runSetBooleanFlag(pw, enabled -> mLetterboxConfiguration - .setCameraCompatRefreshEnabled(enabled)); + runSetBooleanFlag(pw, mLetterboxConfiguration::setCameraCompatRefreshEnabled); break; case "--isCameraCompatRefreshCycleThroughStopEnabled": - runSetBooleanFlag(pw, enabled -> mLetterboxConfiguration - .setCameraCompatRefreshCycleThroughStopEnabled(enabled)); + runSetBooleanFlag(pw, + mLetterboxConfiguration::setCameraCompatRefreshCycleThroughStopEnabled); break; default: getErrPrintWriter().println( @@ -1084,6 +1087,9 @@ public class WindowManagerShellCommand extends ShellCommand { case "isTranslucentLetterboxingEnabled": mLetterboxConfiguration.resetTranslucentLetterboxingEnabled(); break; + case "isUserAppAspectRatioSettingsEnabled": + mLetterboxConfiguration.resetUserAppAspectRatioSettingsEnabled(); + break; case "isCameraCompatRefreshEnabled": mLetterboxConfiguration.resetCameraCompatRefreshEnabled(); break; @@ -1194,6 +1200,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled(); mLetterboxConfiguration.resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(); mLetterboxConfiguration.resetTranslucentLetterboxingEnabled(); + mLetterboxConfiguration.resetUserAppAspectRatioSettingsEnabled(); mLetterboxConfiguration.resetCameraCompatRefreshEnabled(); mLetterboxConfiguration.resetCameraCompatRefreshCycleThroughStopEnabled(); } @@ -1249,7 +1256,6 @@ public class WindowManagerShellCommand extends ShellCommand { + mLetterboxConfiguration.isCameraCompatRefreshEnabled()); pw.println(" Refresh using \"stopped -> resumed\" cycle: " + mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled()); - pw.println("Background type: " + LetterboxConfiguration.letterboxBackgroundTypeToString( mLetterboxConfiguration.getLetterboxBackgroundType())); @@ -1259,12 +1265,10 @@ public class WindowManagerShellCommand extends ShellCommand { + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius()); pw.println(" Wallpaper dark scrim alpha: " + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha()); - - if (mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) { - pw.println("Letterboxing for translucent activities: enabled"); - } else { - pw.println("Letterboxing for translucent activities: disabled"); - } + pw.println("Is letterboxing for translucent activities enabled: " + + mLetterboxConfiguration.isTranslucentLetterboxingEnabled()); + pw.println("Is the user aspect ratio settings enabled: " + + mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()); } return 0; } @@ -1462,6 +1466,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" unresizable apps."); pw.println(" --isTranslucentLetterboxingEnabled [true|1|false|0]"); pw.println(" Whether letterboxing for translucent activities is enabled."); + pw.println(" --isUserAppAspectRatioSettingsEnabled [true|1|false|0]"); + pw.println(" Whether user aspect ratio settings are enabled."); pw.println(" --isCameraCompatRefreshEnabled [true|1|false|0]"); pw.println(" Whether camera compatibility refresh is enabled."); pw.println(" --isCameraCompatRefreshCycleThroughStopEnabled [true|1|false|0]"); @@ -1473,7 +1479,7 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier"); pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled"); pw.println(" |isEducationEnabled||defaultPositionMultiplierForHorizontalReachability"); - pw.println(" |isTranslucentLetterboxingEnabled"); + pw.println(" |isTranslucentLetterboxingEnabled|isUserAppAspectRatioSettingsEnabled"); pw.println(" |defaultPositionMultiplierForVerticalReachability]"); pw.println(" Resets overrides to default values for specified properties separated"); pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 8e85e5bca240..a2f7ba499fdf 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -232,8 +232,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final BLASTSyncEngine.SyncGroup syncGroup = prepareSyncWithOrganizer(callback); final int syncId = syncGroup.mSyncId; if (mTransitionController.isShellTransitionsEnabled()) { - mTransitionController.startLegacySyncOrQueue(syncGroup, () -> { - applyTransaction(t, syncId, null /*transition*/, caller); + mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> { + applyTransaction(t, syncId, null /* transition */, caller, deferred); setSyncReady(syncId); }); } else { @@ -304,7 +304,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub (deferred) -> { nextTransition.start(); nextTransition.mLogger.mStartWCT = wct; - applyTransaction(wct, -1 /*syncId*/, nextTransition, caller); + applyTransaction(wct, -1 /* syncId */, nextTransition, caller, + deferred); if (needsSetReady) { nextTransition.setAllReady(); } @@ -456,7 +457,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub transition.abort(); return; } - if (applyTransaction(wct, -1 /* syncId */, transition, caller) + if (applyTransaction(wct, -1 /* syncId */, transition, caller, deferred) == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) { transition.abort(); return; @@ -476,6 +477,23 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return applyTransaction(t, syncId, transition, caller, null /* finishTransition */); } + private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, + @Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) { + if (deferred) { + try { + return applyTransaction(t, syncId, transition, caller); + } catch (RuntimeException e) { + // If the transaction is deferred, the caller could be from TransitionController + // #tryStartCollectFromQueue that executes on system's worker thread rather than + // binder thread. And the operation in the WCT may be outdated that violates the + // current state. So catch the exception to avoid crashing the system. + Slog.e(TAG, "Failed to execute deferred applyTransaction", e); + } + return TRANSACT_EFFECTS_NONE; + } + return applyTransaction(t, syncId, transition, caller); + } + /** * @param syncId If non-null, this will be a sync-transaction. * @param transition A transition to collect changes into. @@ -838,13 +856,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub switch (type) { case HIERARCHY_OP_TYPE_REMOVE_TASK: { final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; + if (wc == null || wc.asTask() == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to remove invalid task: " + wc); + break; + } + final Task task = wc.asTask(); task.remove(true, "Applying remove task Hierarchy Op"); break; } case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to set launch root to a detached container: " + wc); + break; + } + final Task task = wc.asTask(); if (task == null) { throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); } else if (task.getTaskDisplayArea() == null) { @@ -858,7 +884,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: { final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to set launch adjacent to a detached container: " + wc); + break; + } + final Task task = wc.asTask(); final boolean clearRoot = hop.getToTop(); if (task == null) { throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a1473b198cfd..5579e5296cb0 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2987,10 +2987,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override public boolean canShowWhenLocked() { - final boolean showBecauseOfActivity = - mActivityRecord != null && mActivityRecord.canShowWhenLocked(); - final boolean showBecauseOfWindow = (getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0; - return showBecauseOfActivity || showBecauseOfWindow; + if (mActivityRecord != null) { + // It will also check if its windows contain FLAG_SHOW_WHEN_LOCKED. + return mActivityRecord.canShowWhenLocked(); + } + return (mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0; } /** @@ -5123,12 +5124,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private void applyDims() { if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind()) - && isVisibleNow() && !mHidden) { + && isVisibleNow() && !mHidden && mTransitionController.canApplyDim(getTask())) { // Only show the Dimmer when the following is satisfied: // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested // 2. The WindowToken is not hidden so dims aren't shown when the window is exiting. // 3. The WS is considered visible according to the isVisible() method // 4. The WS is not hidden. + // 5. The window is not in a transition or is in a transition that allows to dim. mIsDimming = true; final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0; final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0; diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 1e502e17b00e..b1f84d3d0c36 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -356,7 +356,7 @@ public: void setPointerCapture(const PointerCaptureRequest& request) override; void notifyDropWindow(const sp<IBinder>& token, float x, float y) override; void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, - const std::set<int32_t>& uids) override; + const std::set<gui::Uid>& uids) override; /* --- PointerControllerPolicyInterface implementation --- */ @@ -969,7 +969,7 @@ void NativeInputManager::notifyDropWindow(const sp<IBinder>& token, float x, flo } void NativeInputManager::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, - const std::set<int32_t>& uids) { + const std::set<gui::Uid>& uids) { static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS = sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true); if (!ENABLE_INPUT_DEVICE_USAGE_METRICS) return; @@ -1864,7 +1864,8 @@ static jboolean nativeSetInTouchMode(JNIEnv* env, jobject nativeImplObj, jboolea jint pid, jint uid, jboolean hasPermission, jint displayId) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, pid, uid, + return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, pid, + gui::Uid{static_cast<uid_t>(uid)}, hasPermission, displayId); } @@ -1880,7 +1881,7 @@ static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject i jint timeoutMillis, jint policyFlags) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - const std::optional<int32_t> targetUid = injectIntoUid ? std::make_optional(uid) : std::nullopt; + const auto targetUid = injectIntoUid ? std::make_optional<gui::Uid>(uid) : std::nullopt; // static_cast is safe because the value was already checked at the Java layer InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode); diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index cadee6f06aad..a1199d99f1c3 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN; import static android.content.Context.CREDENTIAL_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -55,6 +56,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.service.credentials.CallingAppInfo; import android.service.credentials.CredentialProviderInfoFactory; +import android.service.credentials.PermissionUtils; import android.text.TextUtils; import android.util.Pair; import android.util.Slog; @@ -546,11 +548,16 @@ public final class CredentialManagerService if (providerSessions.isEmpty()) { try { - // TODO: fix prepareGetCredentialCallback.onResponse( - new PrepareGetCredentialResponseInternal( - false, null, - false, false, null)); + new PrepareGetCredentialResponseInternal(PermissionUtils.hasPermission( + mContext, + callingPackage, + Manifest.permission + .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS), + /*credentialResultTypes=*/null, + /*hasAuthenticationResults=*/false, + /*hasRemoteResults=*/false, + /*pendingIntent=*/null)); } catch (RemoteException e) { Slog.e( TAG, diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index aee4f583eec9..c9e691e199c7 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -28,6 +28,7 @@ import android.credentials.GetCredentialResponse; import android.credentials.IGetCredentialCallback; import android.credentials.ui.ProviderData; import android.credentials.ui.RequestInfo; +import android.os.Binder; import android.os.CancellationSignal; import android.os.RemoteException; import android.service.credentials.CallingAppInfo; @@ -98,8 +99,9 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) { mRequestSessionMetric.collectUiCallStartTime(System.nanoTime()); mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION); - cancelExistingPendingIntent(); + Binder.withCleanCallingIdentity(()-> { try { + cancelExistingPendingIntent(); mPendingIntent = mCredentialManagerUi.createPendingIntent( RequestInfo.newGetRequestInfo( mRequestId, mClientRequest, mClientAppInfo.getPackageName(), @@ -112,9 +114,9 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED); String exception = GetCredentialException.TYPE_UNKNOWN; mRequestSessionMetric.collectFrameworkException(exception); - respondToClientWithErrorAndFinish( - exception, "Unable to instantiate selector"); - } + respondToClientWithErrorAndFinish(exception, "Unable to instantiate selector"); + } + }); } @Override diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java index b0b72bcf67cc..46c90b4bc731 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java @@ -38,11 +38,13 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -116,7 +118,7 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption @NonNull String servicePackageName, @NonNull CredentialOption requestOption) { super(context, requestOption, session, - new ComponentName(servicePackageName, servicePackageName), + new ComponentName(servicePackageName, UUID.randomUUID().toString()), userId, null); mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(userId); mCallingAppInfo = callingAppInfo; @@ -133,7 +135,7 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption @NonNull String servicePackageName, @NonNull CredentialOption requestOption) { super(context, requestOption, session, - new ComponentName(servicePackageName, servicePackageName), + new ComponentName(servicePackageName, UUID.randomUUID().toString()), userId, null); mCredentialDescriptionRegistry = CredentialDescriptionRegistry.forUser(userId); mCallingAppInfo = callingAppInfo; @@ -179,7 +181,9 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption return null; } return new GetCredentialProviderData.Builder( - mComponentName.flattenToString()).setActionChips(null) + mComponentName.flattenToString()) + .setActionChips(Collections.EMPTY_LIST) + .setAuthenticationEntries(Collections.EMPTY_LIST) .setCredentialEntries(prepareUiCredentialEntries( mProviderResponse.stream().flatMap((Function<CredentialDescriptionRegistry .FilterResult, @@ -261,12 +265,12 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption .getFilteredResultForProvider(mCredentialProviderPackageName, mElementKeys); mCredentialEntries = mProviderResponse.stream().flatMap( - (Function<CredentialDescriptionRegistry.FilterResult, - Stream<CredentialEntry>>) filterResult - -> filterResult.mCredentialEntries.stream()) - .collect(Collectors.toList()); + (Function<CredentialDescriptionRegistry.FilterResult, + Stream<CredentialEntry>>) + filterResult -> filterResult.mCredentialEntries.stream()) + .collect(Collectors.toList()); updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED, - /*source=*/ CredentialsSource.REGISTRY); + /*source=*/ CredentialsSource.REGISTRY); mProviderSessionMetric.collectCandidateEntryMetrics(mCredentialEntries); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java index 7c5d96ec5f5a..1fd7c4ac711a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java @@ -131,6 +131,7 @@ public class AsyncProcessStartTest { mRealAms = new ActivityManagerService( new TestInjector(mContext), mServiceThreadRule.getThread()); + mRealAms.mConstants.loadDeviceConfigConstants(); mRealAms.mActivityTaskManager = new ActivityTaskManagerService(mContext); mRealAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); mRealAms.mAtmInternal = mActivityTaskManagerInt; @@ -195,7 +196,9 @@ public class AsyncProcessStartTest { Log.v(TAG, "Intercepting bindApplication() for " + Arrays.toString(invocation.getArguments())); if (!wedge) { - mRealAms.finishAttachApplication(0); + if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) { + mRealAms.finishAttachApplication(0); + } } return null; }).when(thread).bindApplication( @@ -237,9 +240,10 @@ public class AsyncProcessStartTest { */ @Test public void testNormal() throws Exception { - ProcessRecord app = startProcessAndWait(false); - - verify(app, never()).killLocked(any(), anyInt(), anyBoolean()); + if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) { + ProcessRecord app = startProcessAndWait(false); + verify(app, never()).killLocked(any(), anyInt(), anyBoolean()); + } } /** diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index f4238f678f08..6bcc14e97955 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -66,6 +66,7 @@ import static org.mockito.Mockito.times; import android.annotation.NonNull; import android.app.Activity; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; @@ -410,9 +411,9 @@ public final class BroadcastQueueModernImplTest { List.of(makeMockRegisteredReceiver()), false); enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); - queue.setProcessAndUidState(null, false, false); + queue.setProcessAndUidState(mProcess, false, false); final long notCachedRunnableAt = queue.getRunnableAt(); - queue.setProcessAndUidState(null, false, true); + queue.setProcessAndUidState(mProcess, false, true); final long cachedRunnableAt = queue.getRunnableAt(); assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt); assertFalse(queue.isRunnable()); @@ -437,9 +438,9 @@ public final class BroadcastQueueModernImplTest { List.of(makeMockRegisteredReceiver()), false); enqueueOrReplaceBroadcast(queue, airplaneRecord, 0); - queue.setProcessAndUidState(null, false, false); + queue.setProcessAndUidState(mProcess, false, false); final long notCachedRunnableAt = queue.getRunnableAt(); - queue.setProcessAndUidState(null, false, true); + queue.setProcessAndUidState(mProcess, false, true); final long cachedRunnableAt = queue.getRunnableAt(); assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt); assertTrue(queue.isRunnable()); @@ -555,6 +556,33 @@ public final class BroadcastQueueModernImplTest { } @Test + public void testRunnableAt_processTop() { + final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + + doReturn(ActivityManager.PROCESS_STATE_TOP).when(mProcess).getSetProcState(); + queue.setProcessAndUidState(mProcess, false, false); + + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, + List.of(makeMockRegisteredReceiver())); + enqueueOrReplaceBroadcast(queue, timeTickRecord, 0); + + assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime); + assertEquals(BroadcastProcessQueue.REASON_TOP_PROCESS, queue.getRunnableAtReason()); + + doReturn(ActivityManager.PROCESS_STATE_SERVICE).when(mProcess).getSetProcState(); + queue.setProcessAndUidState(mProcess, false, false); + + // The new process state will only be taken into account the next time a broadcast + // is sent to the process. + enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(timeTick, + List.of(makeMockRegisteredReceiver())), 0); + assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason()); + } + + @Test public void testRunnableAt_persistentProc() { final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN)); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index 0f75ea5fb7ef..73eb237fa9e7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -172,6 +172,7 @@ public class BroadcastQueueTest { BroadcastConstants mConstants; private BroadcastSkipPolicy mSkipPolicy; private UidObserver mUidObserver; + private UidObserver mUidCachedStateObserver; /** * Desired behavior of the next @@ -261,6 +262,7 @@ public class BroadcastQueueTest { // Create a different process that will be linked to the // returned process via a predecessor/successor relationship mActiveProcesses.remove(res); + res.setKilled(true); deliverRes = makeActiveProcessRecord(ai, processName, ProcessBehavior.NORMAL, UnaryOperator.identity()); deliverRes.mPredecessor = res; @@ -316,7 +318,13 @@ public class BroadcastQueueTest { doAnswer((invocation) -> { mUidObserver = invocation.getArgument(0); return null; - }).when(mAms).registerUidObserver(any(), anyInt(), anyInt(), any()); + }).when(mAms).registerUidObserver(any(), anyInt(), + eq(ActivityManager.PROCESS_STATE_TOP), any()); + doAnswer((invocation) -> { + mUidCachedStateObserver = invocation.getArgument(0); + return null; + }).when(mAms).registerUidObserver(any(), anyInt(), + eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any()); mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS); mConstants.TIMEOUT = 100; @@ -413,6 +421,12 @@ public class BroadcastQueueTest { UserHandle.USER_SYSTEM); } + private ProcessRecord makeActiveProcessRecord(String packageName, String processName) + throws Exception { + return makeActiveProcessRecord(packageName, processName, ProcessBehavior.NORMAL, + UserHandle.USER_SYSTEM); + } + private ProcessRecord makeActiveProcessRecord(String packageName, ProcessBehavior behavior) throws Exception { return makeActiveProcessRecord(packageName, packageName, behavior, UserHandle.USER_SYSTEM); @@ -615,6 +629,11 @@ public class BroadcastQueueTest { BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); } + private void setProcessFreezable(ProcessRecord app, boolean pendingFreeze, boolean frozen) { + app.mOptRecord.setPendingFreeze(pendingFreeze); + app.mOptRecord.setFrozen(frozen); + } + private void assertHealth() { if (mImpl == Impl.MODERN) { // If this fails, it'll throw a clear reason message @@ -1316,6 +1335,53 @@ public class BroadcastQueueTest { verifyScheduleReceiver(times(1), receiverOrangeApp, timezone); } + /** + * Verify that a broadcast sent to a frozen app, which gets killed as part of unfreezing + * process due to pending sync binder transactions, is delivered as expected. + */ + @Test + public void testDeliveryToFrozenApp_killedWhileUnfreeze() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + + // Mark the app as killed while unfreezing it, which can happen either when we directly + // try to unfreeze it or when it is done as part of OomAdjust computation. + doAnswer(invocation -> { + final ProcessRecord app = invocation.getArgument(0); + if (app == receiverBlueApp) { + app.setKilled(true); + mActiveProcesses.remove(app); + } + return null; + }).when(mAms.mOomAdjuster).unfreezeTemporarily(eq(receiverBlueApp), anyInt()); + doAnswer(invocation -> { + final ProcessRecord app = invocation.getArgument(0); + if (app == receiverBlueApp) { + app.setKilled(true); + mActiveProcesses.remove(app); + } + return null; + }).when(mAms).enqueueOomAdjTargetLocked(eq(receiverBlueApp)); + + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of( + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)))); + + waitForIdle(); + final ProcessRecord restartedReceiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE, + getUidForPackage(PACKAGE_BLUE)); + assertNotEquals(receiverBlueApp, restartedReceiverBlueApp); + // Legacy queue will always try delivering the broadcast even if the process + // has been killed. + if (mImpl == Impl.MODERN) { + verifyScheduleReceiver(never(), receiverBlueApp, airplane); + } else { + verifyScheduleReceiver(times(1), receiverBlueApp, airplane); + } + // Verify that the new process receives the broadcast. + verifyScheduleReceiver(times(1), restartedReceiverBlueApp, airplane); + } + @Test public void testCold_Success() throws Exception { doCold(ProcessStartBehavior.SUCCESS); @@ -1714,8 +1780,10 @@ public class BroadcastQueueTest { final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE); - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true); - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true); + setProcessFreezable(receiverGreenApp, true, false); + mQueue.onProcessFreezableChangedLocked(receiverGreenApp); + setProcessFreezable(receiverBlueApp, false, true); + mQueue.onProcessFreezableChangedLocked(receiverBlueApp); final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); final BroadcastOptions opts = BroadcastOptions.makeBasic() @@ -1759,12 +1827,14 @@ public class BroadcastQueueTest { eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any()); // Shift blue to be active and confirm that deferred broadcast is delivered - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false); + setProcessFreezable(receiverBlueApp, false, false); + mQueue.onProcessFreezableChangedLocked(receiverBlueApp); waitForIdle(); verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick); // Shift green to be active and confirm that deferred broadcast is delivered - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false); + setProcessFreezable(receiverGreenApp, false, false); + mQueue.onProcessFreezableChangedLocked(receiverGreenApp); waitForIdle(); verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick); } @@ -2189,9 +2259,12 @@ public class BroadcastQueueTest { final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true); - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true); - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_YELLOW), false); + setProcessFreezable(receiverGreenApp, true, true); + mQueue.onProcessFreezableChangedLocked(receiverGreenApp); + setProcessFreezable(receiverBlueApp, true, false); + mQueue.onProcessFreezableChangedLocked(receiverBlueApp); + setProcessFreezable(receiverYellowApp, false, false); + mQueue.onProcessFreezableChangedLocked(receiverYellowApp); final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); final BroadcastOptions opts = BroadcastOptions.makeBasic() @@ -2214,11 +2287,50 @@ public class BroadcastQueueTest { verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane); // Shift green to be active and confirm that deferred broadcast is delivered - mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false); + setProcessFreezable(receiverGreenApp, false, false); + mQueue.onProcessFreezableChangedLocked(receiverGreenApp); waitForIdle(); verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane); } + /** + * Verify broadcasts to a runtime receiver in cached process is deferred even when a different + * process in the same package is not cached. + */ + @Test + public void testDeferralPolicy_UntilActive_WithMultiProcessUid() throws Exception { + // Legacy stack doesn't support deferral + Assume.assumeTrue(mImpl == Impl.MODERN); + + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverGreenApp1 = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverGreenApp2 = makeActiveProcessRecord(PACKAGE_GREEN, + PACKAGE_GREEN + "_proc2"); + + setProcessFreezable(receiverGreenApp1, true, true); + mQueue.onProcessFreezableChangedLocked(receiverGreenApp1); + + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + final BroadcastOptions opts = BroadcastOptions.makeBasic() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, opts, + List.of(makeRegisteredReceiver(receiverGreenApp1), + makeRegisteredReceiver(receiverGreenApp2)))); + waitForIdle(); + + // 1st process in Green package is ignored since it is in a cached state + // but the 2nd process should still receive the broadcast. + verifyScheduleRegisteredReceiver(never(), receiverGreenApp1, airplane); + verifyScheduleRegisteredReceiver(times(1), receiverGreenApp2, airplane); + + // Shift the 1st process in Green package to be active and confirm that deferred broadcast + // is delivered + setProcessFreezable(receiverGreenApp1, false, false); + mQueue.onProcessFreezableChangedLocked(receiverGreenApp1); + waitForIdle(); + verifyScheduleRegisteredReceiver(times(1), receiverGreenApp1, airplane); + } + @Test public void testBroadcastDelivery_uidForeground() throws Exception { // Legacy stack doesn't support prioritization to foreground app. diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 770f04a2db5e..1f4563fb2682 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -1971,6 +1971,36 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_DoOne_PendingFinishAttach() { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + app.setPendingFinishAttach(true); + app.mState.setHasForegroundActivities(false); + + sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + updateOomAdj(app); + + assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ, + SCHED_GROUP_DEFAULT); + } + + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoOne_TopApp_PendingFinishAttach() { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + app.setPendingFinishAttach(true); + app.mState.setHasForegroundActivities(true); + + sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + updateOomAdj(app); + + assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, + SCHED_GROUP_TOP_APP); + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_UidIdle_StopService() { final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); @@ -2512,6 +2542,35 @@ public class MockingOomAdjusterTests { assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); } + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_Side_Cycle() { + final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, + MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); + long now = SystemClock.uptimeMillis(); + ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class)); + s.startRequested = true; + s.lastActivity = now; + s = bindService(app2, app3, null, 0, mock(IBinder.class)); + s.lastActivity = now; + s = bindService(app3, app2, null, 0, mock(IBinder.class)); + s.lastActivity = now; + + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.mNumServiceProcs = 3; + updateOomAdj(app, app2, app3); + + assertEquals(SERVICE_ADJ, app.mState.getSetAdj()); + assertTrue(sFirstCachedAdj <= app2.mState.getSetAdj()); + assertTrue(sFirstCachedAdj <= app3.mState.getSetAdj()); + assertTrue(CACHED_APP_MAX_ADJ >= app2.mState.getSetAdj()); + assertTrue(CACHED_APP_MAX_ADJ >= app3.mState.getSetAdj()); + } + private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName, String packageName, boolean hasShownUi) { long now = SystemClock.uptimeMillis(); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index 2942bd2ff963..aaab4033d579 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -845,6 +845,155 @@ public final class DisplayPowerController2Test { } @Test + public void testAutoBrightnessEnabled_DisplayIsOn() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessEnabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessDisabled_ManualBrightnessMode() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController, times(2)) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsOff() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_OFF; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_FollowerDisplay() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mHolder.dpc.setBrightnessToFollow(0.3f, -1, 0, /* slowChange= */ false); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + + // HBM should be allowed for the follower display + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test public void testBrightnessNitsPersistWhenDisplayDeviceChanges() { float brightness = 0.3f; float nits = 500; diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java index 032f8ff364cb..7d26913bd390 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -850,6 +850,155 @@ public final class DisplayPowerControllerTest { } @Test + public void testAutoBrightnessEnabled_DisplayIsOn() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessEnabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessDisabled_ManualBrightnessMode() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController, times(2)) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsOff() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_OFF; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_FollowerDisplay() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mHolder.dpc.setBrightnessToFollow(0.3f, -1, 0, /* slowChange= */ false); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + + // HBM should be allowed for the follower display + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test public void testBrightnessNitsPersistWhenDisplayDeviceChanges() { float brightness = 0.3f; float nits = 500; diff --git a/services/tests/mockingservicestests/src/com/android/server/dreams/DreamManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/dreams/DreamManagerServiceMockingTest.java new file mode 100644 index 000000000000..c02cbd1c9d08 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/dreams/DreamManagerServiceMockingTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 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.server.dreams; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.app.ActivityManagerInternal; +import android.content.ContextWrapper; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.os.PowerManagerInternal; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; + +import androidx.test.InstrumentationRegistry; + +import com.android.server.LocalServices; +import com.android.server.SystemService; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +/** + * Collection of tests for exercising the {@link DreamManagerService} lifecycle. + */ +public class DreamManagerServiceMockingTest { + private ContextWrapper mContextSpy; + private Resources mResourcesSpy; + + @Mock + private ActivityManagerInternal mActivityManagerInternalMock; + + @Mock + private PowerManagerInternal mPowerManagerInternalMock; + + @Mock + private UserManager mUserManagerMock; + + private MockitoSession mMockitoSession; + + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + mResourcesSpy = spy(mContextSpy.getResources()); + when(mContextSpy.getResources()).thenReturn(mResourcesSpy); + + addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); + addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock); + + when(mContextSpy.getSystemService(UserManager.class)).thenReturn(mUserManagerMock); + mMockitoSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .mockStatic(Settings.Secure.class) + .startMocking(); + } + + @After + public void tearDown() throws Exception { + mMockitoSession.finishMocking(); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.removeServiceForTest(PowerManagerInternal.class); + } + + private DreamManagerService createService() { + return new DreamManagerService(mContextSpy); + } + + @Test + public void testSettingsQueryUserChange() { + final DreamManagerService service = createService(); + + final SystemService.TargetUser from = + new SystemService.TargetUser(mock(UserInfo.class)); + final SystemService.TargetUser to = + new SystemService.TargetUser(mock(UserInfo.class)); + + service.onUserSwitching(from, to); + + verify(() -> Settings.Secure.getIntForUser(any(), + eq(Settings.Secure.SCREENSAVER_ENABLED), + anyInt(), + eq(UserHandle.USER_CURRENT))); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java index bc5e72095a1c..eefe5af314a6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -31,7 +31,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER; -import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_CROP; import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.assertEquals; @@ -275,10 +274,10 @@ public class WallpaperManagerServiceTests { assertEquals(testUserId, newWallpaperData.userId); WallpaperData wallpaperData = mService.getWallpaperSafeLocked(testUserId, which); - assertEquals(wallpaperData.cropFile.getAbsolutePath(), - newWallpaperData.cropFile.getAbsolutePath()); - assertEquals(wallpaperData.wallpaperFile.getAbsolutePath(), - newWallpaperData.wallpaperFile.getAbsolutePath()); + assertEquals(wallpaperData.getCropFile().getAbsolutePath(), + newWallpaperData.getCropFile().getAbsolutePath()); + assertEquals(wallpaperData.getWallpaperFile().getAbsolutePath(), + newWallpaperData.getWallpaperFile().getAbsolutePath()); } } @@ -525,7 +524,8 @@ public class WallpaperManagerServiceTests { @Test public void getWallpaperWithFeature_getCropped_returnsCropFile() throws Exception { File cropSystemWallpaperFile = - new File(WallpaperUtils.getWallpaperDir(USER_SYSTEM), WALLPAPER_CROP); + new WallpaperData(USER_SYSTEM, FLAG_SYSTEM).getCropFile(); + cropSystemWallpaperFile.getParentFile().mkdirs(); cropSystemWallpaperFile.createNewFile(); try (FileOutputStream outputStream = new FileOutputStream(cropSystemWallpaperFile)) { outputStream.write("Crop system wallpaper".getBytes()); @@ -547,7 +547,8 @@ public class WallpaperManagerServiceTests { @Test public void getWallpaperWithFeature_notGetCropped_returnsOriginalFile() throws Exception { File originalSystemWallpaperFile = - new File(WallpaperUtils.getWallpaperDir(USER_SYSTEM), WALLPAPER); + new WallpaperData(USER_SYSTEM, FLAG_SYSTEM).getWallpaperFile(); + originalSystemWallpaperFile.getParentFile().mkdirs(); originalSystemWallpaperFile.createNewFile(); try (FileOutputStream outputStream = new FileOutputStream(originalSystemWallpaperFile)) { outputStream.write("Original system wallpaper".getBytes()); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 67be37616d5f..41f7dbcb0ff5 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -18,7 +18,6 @@ package com.android.server.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricManager.Authenticators; -import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI; @@ -183,6 +182,10 @@ public class BiometricServiceTest { .thenReturn(ERROR_HW_UNAVAILABLE); when(mResources.getString(R.string.biometric_not_recognized)) .thenReturn(ERROR_NOT_RECOGNIZED); + when(mResources.getString(R.string.biometric_face_not_recognized)) + .thenReturn(ERROR_NOT_RECOGNIZED); + when(mResources.getString(R.string.fingerprint_error_not_match)) + .thenReturn(ERROR_NOT_RECOGNIZED); when(mResources.getString(R.string.biometric_error_user_canceled)) .thenReturn(ERROR_USER_CANCELED); @@ -903,6 +906,45 @@ public class BiometricServiceTest { } @Test + public void testMultiBiometricAuth_whenLockoutTimed_sendsErrorAndModality() + throws Exception { + testMultiBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_TIMED, + BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT); + } + + @Test + public void testMultiBiometricAuth_whenLockoutPermanent_sendsErrorAndModality() + throws Exception { + testMultiBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_PERMANENT, + BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT_PERMANENT); + } + + private void testMultiBiometricAuth_whenLockout(@LockoutTracker.LockoutMode int lockoutMode, + int biometricPromptError) throws Exception { + final int[] modalities = new int[] { + TYPE_FINGERPRINT, + BiometricAuthenticator.TYPE_FACE, + }; + + final int[] strengths = new int[] { + Authenticators.BIOMETRIC_STRONG, + Authenticators.BIOMETRIC_STRONG, + }; + setupAuthForMultiple(modalities, strengths); + + when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt())) + .thenReturn(lockoutMode); + when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false); + invokeAuthenticate(mBiometricService.mImpl, mReceiver1, + false /* requireConfirmation */, null /* authenticators */); + waitForIdle(); + + // The lockout error should be sent, instead of ERROR_NONE_ENROLLED. See b/286923477. + verify(mReceiver1).onError(eq(TYPE_FINGERPRINT), + eq(biometricPromptError), eq(0) /* vendorCode */); + } + + @Test public void testBiometricOrCredentialAuth_whenBiometricLockout_showsCredential() throws Exception { when(mTrustManager.isDeviceSecure(anyInt(), anyInt())).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index e9d82696affc..89299002a227 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -19,6 +19,8 @@ package com.android.server.biometrics.sensors; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_SUCCESS; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -405,6 +407,59 @@ public class BiometricSchedulerTest { testCancelsEnrollWhenRequestId(10L, 20, false /* started */); } + @Test + public void testCancelAuthenticationClientWithoutStarting() { + final Supplier<Object> lazyDaemon = () -> mock(Object.class); + final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon); + final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); + final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon, + mToken, callback, mBiometricContext); + + //Schedule authentication client to the pending queue + mScheduler.scheduleClientMonitor(client1); + mScheduler.scheduleClientMonitor(client2); + waitForIdle(); + + assertThat(mScheduler.getCurrentClient()).isEqualTo(client1); + + client2.cancel(); + waitForIdle(); + + assertThat(client2.isAlreadyCancelled()).isTrue(); + + client1.getCallback().onClientFinished(client1, false); + waitForIdle(); + + assertThat(mScheduler.getCurrentClient()).isNull(); + } + + @Test + public void testCancelAuthenticationClientWithoutStarting_whenAppCrashes() { + final Supplier<Object> lazyDaemon = () -> mock(Object.class); + final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon); + final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); + final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon, + mToken, callback, mBiometricContext); + + //Schedule authentication client to the pending queue + mScheduler.scheduleClientMonitor(client1); + mScheduler.scheduleClientMonitor(client2); + waitForIdle(); + + assertThat(mScheduler.getCurrentClient()).isEqualTo(client1); + + //App crashes + client2.binderDied(); + waitForIdle(); + + assertThat(client2.isAlreadyCancelled()).isTrue(); + + client1.getCallback().onClientFinished(client1, false); + waitForIdle(); + + assertThat(mScheduler.getCurrentClient()).isNull(); + } + private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId, boolean started) { final Supplier<Object> lazyDaemon = () -> mock(Object.class); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java index 359d711c0750..046b01c831b5 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -152,6 +152,28 @@ public class FaceAuthenticationClientTest { } @Test + public void testLockoutEndsOperation() throws RemoteException { + final FaceAuthenticationClient client = createClient(2); + client.start(mCallback); + client.onLockoutPermanent(); + + verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(), + eq(FACE_ERROR_LOCKOUT_PERMANENT), anyInt()); + verify(mCallback).onClientFinished(client, false); + } + + @Test + public void testTemporaryLockoutEndsOperation() throws RemoteException { + final FaceAuthenticationClient client = createClient(2); + client.start(mCallback); + client.onLockoutTimed(1000); + + verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(), + eq(FACE_ERROR_LOCKOUT), anyInt()); + verify(mCallback).onClientFinished(client, false); + } + + @Test public void notifyHalWhenContextChanges() throws RemoteException { final FaceAuthenticationClient client = createClient(); client.start(mCallback); diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java index dccc26af8024..7b5c57fd1961 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java @@ -35,6 +35,7 @@ public class CallMetadataSyncDataTest { final byte[] appIcon = "appIcon".getBytes(); final String appName = "appName"; final String appIdentifier = "com.google.test"; + final String extendedId = "com.google.test/.InCallService"; final int status = 1; final int direction = android.companion.Telecom.Call.OUTGOING; final int control1 = 2; @@ -43,7 +44,7 @@ public class CallMetadataSyncDataTest { call.setCallerId(callerId); call.setAppIcon(appIcon); final CallMetadataSyncData.CallFacilitator callFacilitator = - new CallMetadataSyncData.CallFacilitator(appName, appIdentifier); + new CallMetadataSyncData.CallFacilitator(appName, appIdentifier, extendedId); call.setFacilitator(callFacilitator); call.setStatus(status); call.setDirection(direction); @@ -59,6 +60,8 @@ public class CallMetadataSyncDataTest { assertThat(reconstructedCall.getAppIcon()).isEqualTo(appIcon); assertThat(reconstructedCall.getFacilitator().getName()).isEqualTo(appName); assertThat(reconstructedCall.getFacilitator().getIdentifier()).isEqualTo(appIdentifier); + assertThat(reconstructedCall.getFacilitator().getExtendedIdentifier()) + .isEqualTo(extendedId); assertThat(reconstructedCall.getStatus()).isEqualTo(status); assertThat(reconstructedCall.getDirection()).isEqualTo(direction); assertThat(reconstructedCall.getControls()).containsExactly(control1, control2); diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java index 201d8f9413a2..a13cc4162cce 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java @@ -308,6 +308,18 @@ public class CrossDeviceCallTest { .isEqualTo(CONTACT_DISPLAY_NAME); } + @Test + public void getSerializedPhoneAccountHandle_serializesCorrectly() { + final CrossDeviceCall crossDeviceCall = new CrossDeviceCall( + InstrumentationRegistry.getTargetContext(), + mUninitializedCallDetails, /* callAudioState= */ null); + + final String result = crossDeviceCall.getSerializedPhoneAccountHandle(); + + assertWithMessage("Wrong phone account handle serialization").that(result) + .isEqualTo("label::com.google.test/com.google.test.Activity"); + } + private Call.Details createCallDetails(int state, int capabilities) { return createCallDetails(state, capabilities, /* hasContactName= */ true); } diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java index 7e392a4261c1..e6cc34351284 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java @@ -195,7 +195,8 @@ public class CrossDeviceSyncControllerTest { new CrossDeviceSyncController.PhoneAccountManager(mMockContext); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addFacilitator( - new CallMetadataSyncData.CallFacilitator("name", "com.google.test")); + new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); phoneAccountManager.updateFacilitators(0, callMetadataSyncData); phoneAccountManager.updateFacilitators(0, callMetadataSyncData); verify(mMockTelecomManager, times(1)).registerPhoneAccount(any()); @@ -208,10 +209,12 @@ public class CrossDeviceSyncControllerTest { new CrossDeviceSyncController.PhoneAccountManager(mMockContext); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addFacilitator( - new CallMetadataSyncData.CallFacilitator("name", "com.google.test")); + new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); phoneAccountManager.updateFacilitators(0, callMetadataSyncData); callMetadataSyncData.addFacilitator( - new CallMetadataSyncData.CallFacilitator("name", "com.google.test2")); + new CallMetadataSyncData.CallFacilitator("name", "com.google.test2", + "com.google.test2/.InCallService")); phoneAccountManager.updateFacilitators(0, callMetadataSyncData); verify(mMockTelecomManager, times(2)).registerPhoneAccount(any()); verify(mMockTelecomManager, times(0)).unregisterPhoneAccount(any()); @@ -223,7 +226,8 @@ public class CrossDeviceSyncControllerTest { new CrossDeviceSyncController.PhoneAccountManager(mMockContext); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addFacilitator( - new CallMetadataSyncData.CallFacilitator("name", "com.google.test")); + new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); phoneAccountManager.updateFacilitators(0, callMetadataSyncData); final CallMetadataSyncData callMetadataSyncData2 = new CallMetadataSyncData(); phoneAccountManager.updateFacilitators(0, callMetadataSyncData2); @@ -236,7 +240,9 @@ public class CrossDeviceSyncControllerTest { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); call.setDirection(android.companion.Telecom.Call.INCOMING); - call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setFacilitator( + new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); final CrossDeviceSyncController.CallManager callManager = @@ -252,7 +258,9 @@ public class CrossDeviceSyncControllerTest { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); call.setDirection(android.companion.Telecom.Call.OUTGOING); - call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setFacilitator( + new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); call.setCallerId("555-555-5555"); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); @@ -269,7 +277,9 @@ public class CrossDeviceSyncControllerTest { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); call.setDirection(android.companion.Telecom.Call.OUTGOING); - call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setFacilitator( + new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); final CrossDeviceSyncController.CallManager callManager = @@ -316,7 +326,8 @@ public class CrossDeviceSyncControllerTest { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc::originalId"); call.setDirection(android.companion.Telecom.Call.INCOMING); - call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); final CrossDeviceSyncController.CallManager callManager = @@ -334,7 +345,8 @@ public class CrossDeviceSyncControllerTest { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc::originalId"); call.setDirection(android.companion.Telecom.Call.OUTGOING); - call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); call.setCallerId("555-555-5555"); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); @@ -352,7 +364,8 @@ public class CrossDeviceSyncControllerTest { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); call.setDirection(android.companion.Telecom.Call.INCOMING); - call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); final CrossDeviceSyncController.CallManager callManager = @@ -369,7 +382,8 @@ public class CrossDeviceSyncControllerTest { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); call.setDirection(android.companion.Telecom.Call.OUTGOING); - call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.google.test", + "com.google.test/.InCallService")); call.setCallerId("555-555-5555"); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 0e775d57bbd7..5db9d1f6f5bd 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -32,6 +32,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; 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.Assert.fail; @@ -60,6 +61,8 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.Rect; +import android.hardware.Sensor; +import android.hardware.SensorManager; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; import android.hardware.display.DisplayManager; @@ -109,8 +112,6 @@ import com.android.server.wm.WindowManagerInternal; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; -import com.google.common.collect.ImmutableMap; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -125,6 +126,7 @@ import org.mockito.MockitoAnnotations; import java.time.Duration; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; @@ -136,6 +138,9 @@ import java.util.stream.LongStream; public class DisplayManagerServiceTest { private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1; private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10; + + private static final float FLOAT_TOLERANCE = 0.01f; + private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display"; private static final String PACKAGE_NAME = "com.android.frameworks.servicestests"; private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED @@ -250,6 +255,10 @@ public class DisplayManagerServiceTest { @Mock IBinder mMockDisplayToken; @Mock SensorManagerInternal mMockSensorManagerInternal; + @Mock SensorManager mSensorManager; + + @Mock DisplayDeviceConfig mMockDisplayDeviceConfig; + @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor; @Before @@ -267,7 +276,7 @@ public class DisplayManagerServiceTest { LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class); LocalServices.addService( VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal); - + // TODO: b/287945043 mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); VirtualDeviceManager vdm = new VirtualDeviceManager(mIVirtualDeviceManager, mContext); @@ -396,7 +405,7 @@ public class DisplayManagerServiceTest { final int size = displayIds.length; assertTrue(size > 0); - Map<Integer, Integer> expectedDisplayTypeToViewPortTypeMapping = ImmutableMap.of( + Map<Integer, Integer> expectedDisplayTypeToViewPortTypeMapping = Map.of( Display.TYPE_INTERNAL, DisplayViewport.VIEWPORT_INTERNAL, Display.TYPE_EXTERNAL, DisplayViewport.VIEWPORT_EXTERNAL ); @@ -1934,6 +1943,74 @@ public class DisplayManagerServiceTest { assertEquals(mode, displayManager.getHdrConversionModeInternal()); } + @Test + public void testReturnsRefreshRateForDisplayAndSensor_proximitySensorSet() { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + displayManager.overrideSensorManager(mSensorManager); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, new float[]{60f}); + displayDevice.mDisplayDeviceConfig = mMockDisplayDeviceConfig; + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + + String testSensorName = "testName"; + String testSensorType = "testType"; + Sensor testSensor = TestUtils.createSensor(testSensorType, testSensorName); + + DisplayDeviceConfig.SensorData sensorData = new DisplayDeviceConfig.SensorData(); + sensorData.type = testSensorType; + sensorData.name = testSensorName; + sensorData.minRefreshRate = 10f; + sensorData.maxRefreshRate = 100f; + + when(mMockDisplayDeviceConfig.getProximitySensor()).thenReturn(sensorData); + when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(Collections.singletonList( + testSensor)); + + SurfaceControl.RefreshRateRange result = localService.getRefreshRateForDisplayAndSensor( + displayId, testSensorName, testSensorType); + + assertNotNull(result); + assertEquals(result.min, sensorData.minRefreshRate, FLOAT_TOLERANCE); + assertEquals(result.max, sensorData.maxRefreshRate, FLOAT_TOLERANCE); + } + + @Test + public void testReturnsRefreshRateForDisplayAndSensor_proximitySensorNotSet() { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + displayManager.overrideSensorManager(mSensorManager); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, new float[]{60f}); + displayDevice.mDisplayDeviceConfig = mMockDisplayDeviceConfig; + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + + String testSensorName = "testName"; + String testSensorType = "testType"; + Sensor testSensor = TestUtils.createSensor(testSensorType, testSensorName); + + DisplayDeviceConfig.SensorData sensorData = new DisplayDeviceConfig.SensorData(); + sensorData.type = testSensorType; + sensorData.name = testSensorName; + sensorData.minRefreshRate = 10f; + sensorData.maxRefreshRate = 100f; + + when(mMockDisplayDeviceConfig.getProximitySensor()).thenReturn(null); + when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(Collections.singletonList( + testSensor)); + + SurfaceControl.RefreshRateRange result = localService.getRefreshRateForDisplayAndSensor( + displayId, testSensorName, testSensorType); + + assertNull(result); + } + private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) throws Exception { DisplayManagerService displayManager = diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java index 90d9baed53d7..8b45145b160f 100644 --- a/services/tests/servicestests/src/com/android/server/display/TestUtils.java +++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java @@ -18,6 +18,7 @@ package com.android.server.display; import android.hardware.Sensor; import android.hardware.SensorEvent; +import android.hardware.input.InputSensorInfo; import android.os.Parcel; import android.os.SystemClock; import android.view.DisplayAddress; @@ -75,6 +76,12 @@ public final class TestUtils { return sensor; } + public static Sensor createSensor(String type, String name) { + return new Sensor(new InputSensorInfo( + name, "vendor", 0, 0, 0, 1f, 1f, 1, 1, 1, 1, + type, "", 0, 0, 0)); + } + /** * Create a custom {@link DisplayAddress} to ensure we're not relying on any specific * display-address implementation in our code. Intentionally uses default object (reference) diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index f68d34477e49..b652576a75c8 100644 --- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -96,32 +96,101 @@ public class AutomaticBrightnessStrategyTest { } @Test - public void setAutoBrightnessWhenDisabled() { + public void testAutoBrightnessState_AutoBrightnessDisabled() { mAutomaticBrightnessStrategy.setUseAutoBrightness(false); int targetDisplayState = Display.STATE_ON; boolean allowAutoBrightnessWhileDozing = false; - float brightnessState = Float.NaN; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController) + .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + mBrightnessConfiguration, + lastUserSetBrightness, + userSetBrightnessChanged, 0.5f, + false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); + } + + @Test + public void testAutoBrightnessState_DisplayIsOff() { + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + int targetDisplayState = Display.STATE_OFF; + boolean allowAutoBrightnessWhileDozing = false; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController) + .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + mBrightnessConfiguration, + lastUserSetBrightness, + userSetBrightnessChanged, 0.5f, + false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); + } + + @Test + public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesNotAllow() { + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + int targetDisplayState = Display.STATE_DOZE; + boolean allowAutoBrightnessWhileDozing = false; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController) + .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + mBrightnessConfiguration, + lastUserSetBrightness, + userSetBrightnessChanged, 0.5f, + false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); + } + + @Test + public void testAutoBrightnessState_BrightnessReasonIsOverride() { + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + int targetDisplayState = Display.STATE_ON; + boolean allowAutoBrightnessWhileDozing = false; int brightnessReason = BrightnessReason.REASON_OVERRIDE; int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; float lastUserSetBrightness = 0.2f; boolean userSetBrightnessChanged = true; mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, - allowAutoBrightnessWhileDozing, brightnessState, brightnessReason, policy, - lastUserSetBrightness, userSetBrightnessChanged); + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, mBrightnessConfiguration, lastUserSetBrightness, userSetBrightnessChanged, 0.5f, false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); } @Test - public void setAutoBrightnessWhenEnabledAndDisplayIsDozing() { + public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() { mAutomaticBrightnessStrategy.setUseAutoBrightness(true); int targetDisplayState = Display.STATE_DOZE; - float brightnessState = Float.NaN; boolean allowAutoBrightnessWhileDozing = true; int brightnessReason = BrightnessReason.REASON_DOZE; int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; @@ -131,23 +200,24 @@ public class AutomaticBrightnessStrategyTest { Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f); mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(false); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, - allowAutoBrightnessWhileDozing, brightnessState, brightnessReason, policy, - lastUserSetBrightness, userSetBrightnessChanged); + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, lastUserSetBrightness, userSetBrightnessChanged, 0.4f, true, policy, true); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); } @Test - public void setAutoBrightnessWhenEnabledAndDisplayIsOn() { + public void testAutoBrightnessState_DisplayIsOn() { mAutomaticBrightnessStrategy.setUseAutoBrightness(true); int targetDisplayState = Display.STATE_ON; - float brightnessState = Float.NaN; boolean allowAutoBrightnessWhileDozing = false; - int brightnessReason = BrightnessReason.REASON_OVERRIDE; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; float lastUserSetBrightness = 0.2f; boolean userSetBrightnessChanged = true; int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; @@ -156,14 +226,16 @@ public class AutomaticBrightnessStrategyTest { Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment); mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(false); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, - allowAutoBrightnessWhileDozing, brightnessState, brightnessReason, policy, - lastUserSetBrightness, userSetBrightnessChanged); + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, lastUserSetBrightness, userSetBrightnessChanged, pendingBrightnessAdjustment, true, policy, true); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index b91a6cbf05a0..c42928eba85f 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -344,7 +344,7 @@ public class MediaProjectionManagerServiceTest { // Second start - so not valid. projection.start(mIMediaProjectionCallback); - assertThrows(IllegalStateException.class, projection::isValid); + assertThrows(SecurityException.class, projection::isValid); } // TODO(269273190): Test flag using compat annotations instead. diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index e65229f188fc..1ba14623f04e 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -93,8 +93,6 @@ public class BatteryStatsHistoryTest { @Test public void testAtraceBinaryState1() { - mHistory.forceRecordAllHistory(); - InOrder inOrder = Mockito.inOrder(mTracer); Mockito.when(mTracer.tracingEnabled()).thenReturn(true); @@ -112,8 +110,6 @@ public class BatteryStatsHistoryTest { @Test public void testAtraceBinaryState2() { - mHistory.forceRecordAllHistory(); - InOrder inOrder = Mockito.inOrder(mTracer); Mockito.when(mTracer.tracingEnabled()).thenReturn(true); @@ -131,8 +127,6 @@ public class BatteryStatsHistoryTest { @Test public void testAtraceNumericalState() { - mHistory.forceRecordAllHistory(); - InOrder inOrder = Mockito.inOrder(mTracer); Mockito.when(mTracer.tracingEnabled()).thenReturn(true); @@ -150,8 +144,6 @@ public class BatteryStatsHistoryTest { @Test public void testAtraceInstantEvent() { - mHistory.forceRecordAllHistory(); - InOrder inOrder = Mockito.inOrder(mTracer); Mockito.when(mTracer.tracingEnabled()).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index aad373fdaf95..aca96ad20385 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -445,14 +445,14 @@ public class SystemConfigTest { + " <library \n" + " name=\"foo\"\n" + " file=\"" + mFooJar + "\"\n" - + " on-bootclasspath-before=\"A\"\n" + + " on-bootclasspath-before=\"Q\"\n" + " on-bootclasspath-since=\"W\"\n" + " />\n\n" + " </permissions>"; parseSharedLibraries(contents); assertFooIsOnlySharedLibrary(); SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); - assertThat(entry.onBootclasspathBefore).isEqualTo("A"); + assertThat(entry.onBootclasspathBefore).isEqualTo("Q"); assertThat(entry.onBootclasspathSince).isEqualTo("W"); } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 37e5da5f5eaf..9c793752bdf9 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -96,6 +96,19 @@ android:theme="@style/WhiteBackgroundTheme" android:exported="true"/> + <activity android:name="com.android.server.wm.TrustedPresentationCallbackTest$TestActivity" + android:exported="true" + android:showWhenLocked="true" + android:turnScreenOn="true" /> + + <activity + android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService" android:foregroundServiceType="mediaProjection" android:enabled="true"> diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java index 07d8b8a0c8df..51255948ed4a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java @@ -17,17 +17,16 @@ package com.android.server.wm; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import android.content.Context; import android.content.res.Resources; import android.hardware.devicestate.DeviceStateManager; import android.platform.test.annotations.Presubmit; +import android.util.Pair; import androidx.test.filters.SmallTest; @@ -38,6 +37,8 @@ import com.google.common.util.concurrent.MoreExecutors; import org.junit.Before; import org.junit.Test; +import java.util.List; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -58,6 +59,7 @@ public class DeviceStateControllerTests { private DeviceStateController.DeviceState mCurrentState = DeviceStateController.DeviceState.UNKNOWN; private Consumer<DeviceStateController.DeviceState> mDelegate; + private Executor mExecutor = MoreExecutors.directExecutor(); @Before public void setUp() { @@ -124,13 +126,31 @@ public class DeviceStateControllerTests { mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); - // The callback should not receive state change when the it is unregistered. + // The callback should not receive state change when it is unregistered. mTarget.unregisterDeviceStateCallback(mDelegate); assertTrue(mTarget.mDeviceStateCallbacks.isEmpty()); mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */, mCurrentState); } + @Test + public void testCopyDeviceStateCallbacks() { + initialize(true /* supportFold */, true /* supportHalfFolded */); + assertEquals(1, mTarget.mDeviceStateCallbacks.size()); + assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate)); + + List<Pair<Consumer<DeviceStateController.DeviceState>, Executor>> entries = + mTarget.copyDeviceStateCallbacks(); + mTarget.unregisterDeviceStateCallback(mDelegate); + + // In contrast to List<Map.Entry> where the entries are tied to changes in the backing map, + // List<Pair> should still contain non-null callbacks and executors even though they were + // removed from the backing map via the unregister method above. + assertEquals(1, entries.size()); + assertEquals(mDelegate, entries.get(0).first); + assertEquals(mExecutor, entries.get(0).second); + } + private final int[] mFoldedStates = {0}; private final int[] mOpenDeviceStates = {1}; private final int[] mHalfFoldedStates = {2}; @@ -194,7 +214,7 @@ public class DeviceStateControllerTests { when(mMockContext.getResources()).thenReturn((mockRes)); mockFold(mSupportFold, mSupportHalfFold); mTarget = new DeviceStateController(mMockContext, new WindowManagerGlobalLock()); - mTarget.registerDeviceStateCallback(mDelegate, MoreExecutors.directExecutor()); + mTarget.registerDeviceStateCallback(mDelegate, mExecutor); } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 8e015d4d228d..769a309cf5a7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -103,8 +103,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { public void setUp() throws Exception { mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration; spyOn(mLetterboxConfiguration); - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ anyBoolean())) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(true); when(mLetterboxConfiguration.isCameraCompatRefreshEnabled()) .thenReturn(true); @@ -177,8 +176,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { @Test public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() { - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ anyBoolean())) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(false); spyOn(mDisplayRotationCompatPolicy); @@ -238,8 +236,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { @Test public void testTreatmentNotEnabled_noForceRotationOrRefresh() throws Exception { - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ anyBoolean())) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(false); configureActivity(SCREEN_ORIENTATION_PORTRAIT); @@ -253,8 +250,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { @Test public void testTreatmentDisabledViaDeviceConfig_noForceRotationOrRefresh() throws Exception { - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true)) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(false); configureActivity(SCREEN_ORIENTATION_PORTRAIT); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java index d29b18f89f77..b1057032eb36 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java @@ -31,7 +31,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; import android.platform.test.annotations.Presubmit; import android.view.Surface; @@ -79,8 +78,11 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas when(mDisplayContent.getIgnoreOrientationRequest()).thenReturn(true); mMockLetterboxConfiguration = mock(LetterboxConfiguration.class); - when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled( - /* checkDeviceConfig */ anyBoolean())).thenReturn(true); + when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled()) + .thenReturn(true); + when(mMockLetterboxConfiguration + .isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime()) + .thenReturn(true); mPolicy = DisplayRotationImmersiveAppCompatPolicy.createIfNeeded( mMockLetterboxConfiguration, createDisplayRotationMock(), @@ -204,8 +206,8 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas @Test public void testRotationChoiceEnforcedOnly_featureFlagDisabled_lockNotEnforced() { - when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled( - /* checkDeviceConfig */ true)).thenReturn(false); + when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled()) + .thenReturn(false); assertIsRotationLockEnforcedReturnsFalseForAllRotations(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 34a13bfa855c..81a37943505f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -63,7 +63,6 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; @@ -147,7 +146,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() { - doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean()); + doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(); + doReturn(true).when(mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); // Recreate DisplayContent with DisplayRotationCompatPolicy mActivity = setUpActivityWithComponent(); @@ -306,7 +307,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test public void testShouldRefreshActivityForCameraCompat_flagIsDisabled_returnsFalse() { doReturn(false).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldRefreshActivityForCameraCompat()); } @@ -315,7 +316,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) public void testShouldRefreshActivityForCameraCompat_overrideEnabled_returnsFalse() { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldRefreshActivityForCameraCompat()); } @@ -325,7 +326,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityForCameraCompat_propertyIsTrueAndOverride_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -337,7 +338,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityForCameraCompat_propertyIsFalse_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ false); mController = new LetterboxUiController(mWm, mActivity); @@ -349,7 +350,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityForCameraCompat_propertyIsTrue_returnsTrue() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -363,7 +364,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE}) public void testShouldRefreshActivityViaPauseForCameraCompat_flagIsDisabled_returnsFalse() { doReturn(false).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldRefreshActivityViaPauseForCameraCompat()); } @@ -372,7 +373,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE}) public void testShouldRefreshActivityViaPauseForCameraCompat_overrideEnabled_returnsTrue() { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertTrue(mController.shouldRefreshActivityViaPauseForCameraCompat()); } @@ -382,7 +383,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityViaPauseForCameraCompat_propertyIsFalseAndOverride_returnFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, /* value */ false); mController = new LetterboxUiController(mWm, mActivity); @@ -394,7 +395,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityViaPauseForCameraCompat_propertyIsTrue_returnsTrue() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -407,7 +408,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test public void testShouldForceRotateForCameraCompat_flagIsDisabled_returnsFalse() { doReturn(false).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldForceRotateForCameraCompat()); } @@ -416,7 +417,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION}) public void testShouldForceRotateForCameraCompat_overrideEnabled_returnsFalse() { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldForceRotateForCameraCompat()); } @@ -426,7 +427,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldForceRotateForCameraCompat_propertyIsTrueAndOverride_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -438,7 +439,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldForceRotateForCameraCompat_propertyIsFalse_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ false); mController = new LetterboxUiController(mWm, mActivity); @@ -450,7 +451,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldForceRotateForCameraCompat_propertyIsTrue_returnsTrue() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -741,7 +742,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA}) public void testOverrideOrientationIfNeeded_whenCameraNotActive_returnsUnchanged() { - doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean()); + doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(); + doReturn(true).when(mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); // Recreate DisplayContent with DisplayRotationCompatPolicy mActivity = setUpActivityWithComponent(); @@ -759,7 +762,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA}) public void testOverrideOrientationIfNeeded_whenCameraActive_returnsPortrait() { - doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean()); + doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(); + doReturn(true).when(mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); // Recreate DisplayContent with DisplayRotationCompatPolicy mActivity = setUpActivityWithComponent(); @@ -1060,7 +1065,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test public void testgetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() { doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(anyBoolean()); + .isCameraCompatTreatmentEnabled(); + doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration) .isCameraCompatSplitScreenAspectRatioEnabled(); doReturn(false).when(mActivity.mWmService.mLetterboxConfiguration) diff --git a/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java b/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java index ecab62f72f69..7d44e119b0b2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java @@ -16,9 +16,13 @@ package com.android.server.wm; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import android.app.ActivityThread; import android.platform.test.annotations.Presubmit; @@ -68,8 +72,6 @@ public class SynchedDeviceConfigTests { .addDeviceConfigEntry(/* key */ "key2", /* default */ false, /* enabled */ true) .addDeviceConfigEntry(/* key */ "key3", /* default */ true, /* enabled */ false) .addDeviceConfigEntry(/* key */ "key4", /* default */ false, /* enabled */ false) - .addDeviceConfigEntry(/* key */ "key5", /* default */ true, /* enabled */ false) - .addDeviceConfigEntry(/* key */ "key6", /* default */ false, /* enabled */ false) .build(); } @@ -84,8 +86,6 @@ public class SynchedDeviceConfigTests { assertFlagValue(/* key */ "key2", /* expected */ false); // enabled assertFlagValue(/* key */ "key3", /* expected */ false); // disabled assertFlagValue(/* key */ "key4", /* expected */ false); // disabled - assertFlagValue(/* key */ "key5", /* expected */ false); // disabled - assertFlagValue(/* key */ "key6", /* expected */ false); // disabled } @Test @@ -94,18 +94,17 @@ public class SynchedDeviceConfigTests { assertFlagEnabled(/* key */ "key2", /* expected */ true); assertFlagEnabled(/* key */ "key3", /* expected */ false); assertFlagEnabled(/* key */ "key4", /* expected */ false); - assertFlagEnabled(/* key */ "key5", /* expected */ false); - assertFlagEnabled(/* key */ "key6", /* expected */ false); } @Test public void testWhenUpdated_onlyEnabledChanges() { final CountDownLatch countDownLatch = new CountDownLatch(4); - final DeviceConfig.OnPropertiesChangedListener countDownLatchListener = - properties -> countDownLatch.countDown(); - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_FOR_TEST, mExecutor, - countDownLatchListener); - + spyOn(mDeviceConfig); + doAnswer(invocation -> { + invocation.callRealMethod(); + countDownLatch.countDown(); + return null; + }).when(mDeviceConfig).onPropertiesChanged(any()); try { // We update all the keys updateProperty(/* key */ "key1", /* value */ false); @@ -123,58 +122,9 @@ public class SynchedDeviceConfigTests { assertFlagValue(/* key */ "key4", /* expected */ false); // disabled } catch (InterruptedException e) { Assert.fail(e.getMessage()); - } finally { - DeviceConfig.removeOnPropertiesChangedListener(countDownLatchListener); } } - @Test - public void testWhenEnabled_updatesAreUsed() { - final CountDownLatch countDownLatchBefore = new CountDownLatch(2); - final CountDownLatch countDownLatchAfter = new CountDownLatch(2); - final DeviceConfig.OnPropertiesChangedListener countDownLatchBeforeListener = - properties -> countDownLatchBefore.countDown(); - final DeviceConfig.OnPropertiesChangedListener countDownLatchAfterListener = - properties -> countDownLatchAfter.countDown(); - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_FOR_TEST, mExecutor, - countDownLatchBeforeListener); - - try { - // We update disabled values - updateProperty(/* key */ "key3", /* value */ false); - updateProperty(/* key */ "key4", /* value */ true); - - assertThat(countDownLatchBefore.await( - WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); - - // We check they haven't been updated - assertFlagValue(/* key */ "key3", /* expected */ false); - assertFlagValue(/* key */ "key4", /* expected */ false); - - - DeviceConfig.removeOnPropertiesChangedListener(countDownLatchBeforeListener); - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_FOR_TEST, mExecutor, - countDownLatchAfterListener); - - // We update enabled flags - updateProperty(/* key */ "key1", /* value */ false); - updateProperty(/* key */ "key2", /* value */ true); - - assertThat(countDownLatchAfter.await( - WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); - - // Value have been updated - assertFlagValue(/* key */ "key1", /* expected */ false); - assertFlagValue(/* key */ "key2", /* expected */ true); - - } catch (InterruptedException e) { - Assert.fail(e.getMessage()); - } finally { - DeviceConfig.removeOnPropertiesChangedListener(countDownLatchAfterListener); - } - } - - private void assertFlagValue(String key, boolean expectedValue) { assertEquals(/* message */"Flag " + key + " value is not " + expectedValue, /* expected */ expectedValue, /* actual */ mDeviceConfig.getFlagValue(key)); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 11267268a6fa..ed0c8ef489e5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1452,6 +1452,11 @@ public class TransitionTests extends WindowTestsBase { } }); assertTrue(activity1.isVisible()); + doReturn(false).when(task1).isTranslucent(null); + assertTrue(controller.canApplyDim(task1)); + doReturn(true).when(task1).isTranslucent(null); + assertFalse(controller.canApplyDim(task1)); + controller.finishTransition(closeTransition); assertTrue(wasInFinishingTransition[0]); assertNull(controller.mFinishingTransition); @@ -2191,8 +2196,11 @@ public class TransitionTests extends WindowTestsBase { BLASTSyncEngine.SyncGroup legacySync = mSyncEngine.prepareSyncSet( mock(BLASTSyncEngine.TransactionReadyListener.class), "test"); - final boolean[] applyLegacy = new boolean[]{false}; - controller.startLegacySyncOrQueue(legacySync, () -> applyLegacy[0] = true); + final boolean[] applyLegacy = new boolean[2]; + controller.startLegacySyncOrQueue(legacySync, (deferred) -> { + applyLegacy[0] = true; + applyLegacy[1] = deferred; + }); assertFalse(applyLegacy[0]); waitUntilHandlersIdle(); @@ -2208,6 +2216,7 @@ public class TransitionTests extends WindowTestsBase { assertTrue(transitA.isPlaying()); // legacy sync should start now assertTrue(applyLegacy[0]); + assertTrue(applyLegacy[1]); // transitB must wait assertTrue(transitB.isPending()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java new file mode 100644 index 000000000000..df11a44bb1b5 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java @@ -0,0 +1,133 @@ +/* + * 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.server.wm; + +import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule; +import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.platform.test.annotations.Presubmit; +import android.util.Log; +import android.view.SurfaceControl; +import android.view.SurfaceControl.TrustedPresentationThresholds; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * TODO (b/287076178): Move these tests to + * {@link android.view.surfacecontrol.cts.TrustedPresentationCallbackTest} when API is made public + */ +@Presubmit +public class TrustedPresentationCallbackTest { + private static final String TAG = "TrustedPresentationCallbackTest"; + private static final int STABILITY_REQUIREMENT_MS = 500; + private static final long WAIT_TIME_MS = HW_TIMEOUT_MULTIPLIER * 2000L; + + private static final float FRACTION_VISIBLE = 0.1f; + + @Rule + public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule( + TestActivity.class); + + private TestActivity mActivity; + + @Before + public void setup() { + mActivityRule.getScenario().onActivity(activity -> mActivity = activity); + } + + @Test + public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException { + boolean[] results = new boolean[1]; + CountDownLatch receivedResults = new CountDownLatch(1); + TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( + 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, + Runnable::run, inTrustedPresentationState -> { + Log.d(TAG, "onTrustedPresentationChanged " + inTrustedPresentationState); + results[0] = inTrustedPresentationState; + receivedResults.countDown(); + }); + t.apply(); + + assertTrue("Timed out waiting for results", + receivedResults.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); + assertTrue(results[0]); + } + + @Test + public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException { + final Object resultsLock = new Object(); + boolean[] results = new boolean[1]; + boolean[] receivedResults = new boolean[1]; + TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( + 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); + Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> { + synchronized (resultsLock) { + results[0] = inTrustedPresentationState; + receivedResults[0] = true; + resultsLock.notify(); + } + }; + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, + Runnable::run, trustedPresentationCallback); + t.apply(); + + synchronized (resultsLock) { + if (!receivedResults[0]) { + resultsLock.wait(WAIT_TIME_MS); + } + // Make sure we received the results and not just timed out + assertTrue("Timed out waiting for results", receivedResults[0]); + assertTrue(results[0]); + + // reset the state + receivedResults[0] = false; + } + + mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t, + trustedPresentationCallback); + t.apply(); + + synchronized (resultsLock) { + if (!receivedResults[0]) { + resultsLock.wait(WAIT_TIME_MS); + } + // Ensure we waited the full time and never received a notify on the result from the + // callback. + assertFalse("Should never have received a callback", receivedResults[0]); + // results shouldn't have changed. + assertTrue(results[0]); + } + } + + public static class TestActivity extends Activity { + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index f3d8114b9e94..4afcd0539b94 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -288,6 +288,32 @@ public class WallpaperControllerTests extends WindowTestsBase { assertEquals(homeWin, dc.mWallpaperController.getWallpaperTarget()); } + @Test + public void testShowWhenLockedWallpaperTarget() { + final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent); + wallpaperWindow.mToken.asWallpaperToken().setShowWhenLocked(true); + final WindowState behind = createWindow(null, TYPE_BASE_APPLICATION, "behind"); + final WindowState topTranslucent = createWindow(null, TYPE_BASE_APPLICATION, + "topTranslucent"); + behind.mAttrs.width = behind.mAttrs.height = topTranslucent.mAttrs.width = + topTranslucent.mAttrs.height = WindowManager.LayoutParams.MATCH_PARENT; + topTranslucent.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; + doReturn(true).when(behind.mActivityRecord).fillsParent(); + doReturn(false).when(topTranslucent.mActivityRecord).fillsParent(); + + spyOn(mWm.mPolicy); + doReturn(true).when(mWm.mPolicy).isKeyguardLocked(); + doReturn(true).when(mWm.mPolicy).isKeyguardOccluded(); + mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + // Wallpaper is visible because the show-when-locked activity is translucent. + assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(wallpaperWindow)); + + behind.mActivityRecord.setShowWhenLocked(true); + mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + // Wallpaper is invisible because the lowest show-when-locked activity is opaque. + assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(null)); + } + /** * Tests that the windowing mode of the wallpaper window must always be fullscreen. */ diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 26590c4704c3..a72f7806d3ea 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -2097,7 +2097,10 @@ public class TelecomManager { * For a self-managed {@link ConnectionService}, a {@link SecurityException} will be thrown if * the {@link PhoneAccount} has {@link PhoneAccount#CAPABILITY_SELF_MANAGED} and the calling app * does not have {@link android.Manifest.permission#MANAGE_OWN_CALLS}. - * + * <p> + * <p> + * <b>Note</b>: {@link android.app.Notification.CallStyle} notifications should be posted after + * the call is added to Telecom in order for the notification to be non-dismissible. * @param phoneAccount A {@link PhoneAccountHandle} registered with * {@link #registerPhoneAccount}. * @param extras A bundle that will be passed through to @@ -2345,7 +2348,10 @@ public class TelecomManager { * {@link PhoneAccount} with the {@link PhoneAccount#CAPABILITY_PLACE_EMERGENCY_CALLS} * capability, depending on external factors, such as network conditions and Modem/SIM status. * </p> - * + * <p> + * <p> + * <b>Note</b>: {@link android.app.Notification.CallStyle} notifications should be posted after + * the call is placed in order for the notification to be non-dismissible. * @param address The address to make the call to. * @param extras Bundle of extras to use with the call. */ @@ -2679,9 +2685,11 @@ public class TelecomManager { /** * Add a call to the Android system service Telecom. This allows the system to start tracking an - * incoming or outgoing call with the specified {@link CallAttributes}. Once the call is ready - * to be disconnected, use the {@link CallControl#disconnect(DisconnectCause, Executor, - * OutcomeReceiver)} which is provided by the {@code pendingControl#onResult(CallControl)}. + * incoming or outgoing call with the specified {@link CallAttributes}. Once a call is added, + * a {@link android.app.Notification.CallStyle} notification should be posted and when the + * call is ready to be disconnected, use {@link CallControl#disconnect(DisconnectCause, + * Executor, OutcomeReceiver)} which is provided by the + * {@code pendingControl#onResult(CallControl)}. * <p> * <p> * <p> diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5b1c6b1c88b8..dc5b75ad6f14 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9730,6 +9730,27 @@ public class CarrierConfigManager { public static final String KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY = "iwlan_handover_policy_string_array"; + /** + * Score table for {@link TelephonyManager#MOBILE_DATA_POLICY_AUTO_DATA_SWITCH}. The score is + * used in conjunction with a tolerance value defined in resource config + * {@code auto_data_switch_score_tolerance}, greater than which device will switch to the sub + * with higher score. + * Possible keys are network type name string(also see {@link #KEY_BANDWIDTH_STRING_ARRAY}). + * Value should be "score_level_0, score_level_1, score_level_2, score_level_3,score_level_4". + * Each network type must have 5 scores correspond to {@link CellSignalStrength}, where score is + * a non-negative integer. A score of 0 is treated the same as data out of service. + * + * For NR (5G), the following network names should be used: + * - NR_NSA: NR NSA, sub-6 frequencies + * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies + * - NR_SA: NR SA, sub-6 frequencies + * - NR_SA_MMWAVE: NR SA, mmwave frequencies + * + * @hide + */ + public static final String KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE = + "auto_data_switch_rat_signal_score_string_bundle"; + /** The default value for every variable. */ private static final PersistableBundle sDefaults; @@ -9876,9 +9897,9 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml"); sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200); sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS, - new String[]{"default", "mms", "dun", "supl"}); + new String[]{"default", "mms", "dun", "supl", "enterprise"}); sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS, - new String[]{"default", "mms", "dun", "supl"}); + new String[]{"default", "mms", "dun", "supl", "enterprise"}); sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY, new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A, @@ -10404,6 +10425,51 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{ "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, " + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"}); + PersistableBundle auto_data_switch_rat_signal_score_string_bundle = new PersistableBundle(); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_SA_MMWAVE", new int[]{10000, 13227, 16000, 18488, 20017}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_NSA_MMWAVE", new int[]{8000, 10227, 12488, 15017, 15278}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "LTE", new int[]{3731, 5965, 8618, 11179, 13384}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_SA", new int[]{5288, 6795, 6955, 7562, 9713}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_NSA", new int[]{5463, 6827, 8029, 9007, 9428}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "UMTS", new int[]{100, 169, 183, 192, 300}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "eHRPD", new int[]{10, 400, 600, 800, 1000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "TD_SCDMA", new int[]{1, 100, 500, 1000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "iDEN", new int[]{1, 2, 10, 50, 100}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EvDo_B", new int[]{1000, 1495, 2186, 2532, 2600}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSPA+", new int[]{1619, 2500, 3393, 4129, 4212}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSPA", new int[]{1000, 1495, 2186, 2532, 2544}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSUPA", new int[]{1500, 1919, 2132, 2362, 2704}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSDPA", new int[]{1500, 1732, 4000, 7000, 8000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EvDo_A", new int[]{600, 840, 1200, 1300, 1400}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EvDo_0", new int[]{300, 600, 1000, 1500, 2000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "1xRTT", new int[]{50, 60, 70, 80}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EDGE", new int[]{154, 169, 183, 192, 267}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "GPRS", new int[]{15, 30, 40, 45, 50}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "CDMA", new int[]{1, 50, 100, 300, 2000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "GSM", new int[]{1, 2, 10, 50, 100}); + sDefaults.putPersistableBundle(KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE, + auto_data_switch_rat_signal_score_string_bundle); sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT, SubscriptionManager.USAGE_SETTING_UNKNOWN); // Default data stall recovery configurations. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt index efd9b004f8fd..7c9c05d7da85 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -21,7 +21,7 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerBuilderProvider import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation @@ -37,7 +37,7 @@ import org.junit.Test abstract class BaseTest @JvmOverloads constructor( - protected val flicker: FlickerTest, + protected val flicker: LegacyFlickerTest, protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), protected val tapl: LauncherInstrumentation = LauncherInstrumentation() ) { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index ed9e14fd86da..1fdbe7fd14a9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -23,14 +23,14 @@ import android.tools.common.flicker.subject.region.RegionSubject import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.IComponentNameMatcher import android.tools.common.traces.wm.WindowManagerTrace -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.helpers.WindowUtils /** * Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in all * WM trace entries */ -fun FlickerTest.statusBarWindowIsAlwaysVisible() { +fun LegacyFlickerTest.statusBarWindowIsAlwaysVisible() { assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.STATUS_BAR) } } @@ -38,7 +38,7 @@ fun FlickerTest.statusBarWindowIsAlwaysVisible() { * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows in all WM * trace entries */ -fun FlickerTest.navBarWindowIsAlwaysVisible() { +fun LegacyFlickerTest.navBarWindowIsAlwaysVisible() { assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) } } @@ -46,7 +46,7 @@ fun FlickerTest.navBarWindowIsAlwaysVisible() { * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the * start and end of the WM trace */ -fun FlickerTest.navBarWindowIsVisibleAtStartAndEnd() { +fun LegacyFlickerTest.navBarWindowIsVisibleAtStartAndEnd() { this.navBarWindowIsVisibleAtStart() this.navBarWindowIsVisibleAtEnd() } @@ -55,7 +55,7 @@ fun FlickerTest.navBarWindowIsVisibleAtStartAndEnd() { * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the * start of the WM trace */ -fun FlickerTest.navBarWindowIsVisibleAtStart() { +fun LegacyFlickerTest.navBarWindowIsVisibleAtStart() { assertWmStart { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) } } @@ -63,7 +63,7 @@ fun FlickerTest.navBarWindowIsVisibleAtStart() { * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the end * of the WM trace */ -fun FlickerTest.navBarWindowIsVisibleAtEnd() { +fun LegacyFlickerTest.navBarWindowIsVisibleAtEnd() { assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) } } @@ -71,7 +71,7 @@ fun FlickerTest.navBarWindowIsVisibleAtEnd() { * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM * trace entries */ -fun FlickerTest.taskBarWindowIsAlwaysVisible() { +fun LegacyFlickerTest.taskBarWindowIsAlwaysVisible() { assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) } } @@ -79,7 +79,7 @@ fun FlickerTest.taskBarWindowIsAlwaysVisible() { * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM * trace entries */ -fun FlickerTest.taskBarWindowIsVisibleAtEnd() { +fun LegacyFlickerTest.taskBarWindowIsVisibleAtEnd() { assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) } } @@ -93,43 +93,45 @@ fun FlickerTest.taskBarWindowIsVisibleAtEnd() { * @param allStates if all states should be checked, othersie, just initial and final */ @JvmOverloads -fun FlickerTest.entireScreenCovered(allStates: Boolean = true) { +fun LegacyFlickerTest.entireScreenCovered(allStates: Boolean = true) { if (allStates) { assertLayers { this.invoke("entireScreenCovered") { entry -> - entry.entry.displays.filter { it.isOn }.forEach { display -> - entry.visibleRegion().coversAtLeast(display.layerStackSpace) - } + entry.entry.displays + .filter { it.isOn } + .forEach { display -> + entry.visibleRegion().coversAtLeast(display.layerStackSpace) + } } } } else { assertLayersStart { - this.entry.displays.filter { it.isOn }.forEach { display -> - this.visibleRegion().coversAtLeast(display.layerStackSpace) - } + this.entry.displays + .filter { it.isOn } + .forEach { display -> this.visibleRegion().coversAtLeast(display.layerStackSpace) } } assertLayersEnd { - this.entry.displays.filter { it.isOn }.forEach { display -> - this.visibleRegion().coversAtLeast(display.layerStackSpace) - } + this.entry.displays + .filter { it.isOn } + .forEach { display -> this.visibleRegion().coversAtLeast(display.layerStackSpace) } } } } /** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start of the SF trace */ -fun FlickerTest.navBarLayerIsVisibleAtStart() { +fun LegacyFlickerTest.navBarLayerIsVisibleAtStart() { assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) } } /** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the end of the SF trace */ -fun FlickerTest.navBarLayerIsVisibleAtEnd() { +fun LegacyFlickerTest.navBarLayerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) } } /** * Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start and end of the SF trace */ -fun FlickerTest.navBarLayerIsVisibleAtStartAndEnd() { +fun LegacyFlickerTest.navBarLayerIsVisibleAtStartAndEnd() { this.navBarLayerIsVisibleAtStart() this.navBarLayerIsVisibleAtEnd() } @@ -137,18 +139,18 @@ fun FlickerTest.navBarLayerIsVisibleAtStartAndEnd() { /** * Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start and end of the SF trace */ -fun FlickerTest.taskBarLayerIsVisibleAtStartAndEnd() { +fun LegacyFlickerTest.taskBarLayerIsVisibleAtStartAndEnd() { this.taskBarLayerIsVisibleAtStart() this.taskBarLayerIsVisibleAtEnd() } /** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start of the SF trace */ -fun FlickerTest.taskBarLayerIsVisibleAtStart() { +fun LegacyFlickerTest.taskBarLayerIsVisibleAtStart() { assertLayersStart { this.isVisible(ComponentNameMatcher.TASK_BAR) } } /** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the SF trace */ -fun FlickerTest.taskBarLayerIsVisibleAtEnd() { +fun LegacyFlickerTest.taskBarLayerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) } } @@ -156,7 +158,7 @@ fun FlickerTest.taskBarLayerIsVisibleAtEnd() { * Checks that [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end of the SF * trace */ -fun FlickerTest.statusBarLayerIsVisibleAtStartAndEnd() { +fun LegacyFlickerTest.statusBarLayerIsVisibleAtStartAndEnd() { assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) } assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) } } @@ -165,7 +167,7 @@ fun FlickerTest.statusBarLayerIsVisibleAtStartAndEnd() { * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start of * the SF trace */ -fun FlickerTest.navBarLayerPositionAtStart() { +fun LegacyFlickerTest.navBarLayerPositionAtStart() { assertLayersStart { val display = this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!") @@ -180,7 +182,7 @@ fun FlickerTest.navBarLayerPositionAtStart() { * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the end of * the SF trace */ -fun FlickerTest.navBarLayerPositionAtEnd() { +fun LegacyFlickerTest.navBarLayerPositionAtEnd() { assertLayersEnd { val display = this.entry.displays.minByOrNull { it.id } @@ -196,7 +198,7 @@ fun FlickerTest.navBarLayerPositionAtEnd() { * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start and * end of the SF trace */ -fun FlickerTest.navBarLayerPositionAtStartAndEnd() { +fun LegacyFlickerTest.navBarLayerPositionAtStartAndEnd() { navBarLayerPositionAtStart() navBarLayerPositionAtEnd() } @@ -205,7 +207,7 @@ fun FlickerTest.navBarLayerPositionAtStartAndEnd() { * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start * of the SF trace */ -fun FlickerTest.statusBarLayerPositionAtStart( +fun LegacyFlickerTest.statusBarLayerPositionAtStart( wmTrace: WindowManagerTrace? = this.reader.readWmTrace() ) { // collect navbar position for the equivalent WM state @@ -221,7 +223,7 @@ fun FlickerTest.statusBarLayerPositionAtStart( * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the end of * the SF trace */ -fun FlickerTest.statusBarLayerPositionAtEnd( +fun LegacyFlickerTest.statusBarLayerPositionAtEnd( wmTrace: WindowManagerTrace? = this.reader.readWmTrace() ) { // collect navbar position for the equivalent WM state @@ -237,7 +239,7 @@ fun FlickerTest.statusBarLayerPositionAtEnd( * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start * and end of the SF trace */ -fun FlickerTest.statusBarLayerPositionAtStartAndEnd() { +fun LegacyFlickerTest.statusBarLayerPositionAtStartAndEnd() { statusBarLayerPositionAtStart() statusBarLayerPositionAtEnd() } @@ -246,7 +248,9 @@ fun FlickerTest.statusBarLayerPositionAtStartAndEnd() { * Asserts that the visibleRegion of the [ComponentNameMatcher.SNAPSHOT] layer can cover the * visibleRegion of the given app component exactly */ -fun FlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(component: IComponentNameMatcher) { +fun LegacyFlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp( + component: IComponentNameMatcher +) { assertLayers { invoke("snapshotStartingWindowLayerCoversExactlyOnApp") { val snapshotLayers = @@ -307,7 +311,7 @@ fun FlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(component: ICompon * otherwise we won't and the layer must appear immediately. * ``` */ -fun FlickerTest.replacesLayer( +fun LegacyFlickerTest.replacesLayer( originalLayer: IComponentNameMatcher, newLayer: IComponentNameMatcher, ignoreEntriesWithRotationLayer: Boolean = false, diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt index 7ef4d939fee2..45cd65d9776c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt @@ -16,12 +16,12 @@ package com.android.server.wm.flicker.activityembedding -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.Before -abstract class ActivityEmbeddingTestBase(flicker: FlickerTest) : BaseTest(flicker) { +abstract class ActivityEmbeddingTestBase(flicker: LegacyFlickerTest) : BaseTest(flicker) { val testApp = ActivityEmbeddingAppHelper(instrumentation) @Before diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt index 236c44e89f41..4bc17ed12b77 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt @@ -19,8 +19,8 @@ package com.android.server.wm.flicker.activityembedding import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.FixMethodOrder @@ -32,10 +32,9 @@ import org.junit.runners.Parameterized /** * Test launching a placeholder split over a normal split, both splits are configured in RTL. * - * Setup: From A launch a split in RTL - resulting in B|A. - * Transitions: - * From A start PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. - * Expect split PlaceholderSecondary|PlaceholderPrimary covering split B|A. + * Setup: From A launch a split in RTL - resulting in B|A. Transitions: From A start + * PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. Expect split + * PlaceholderSecondary|PlaceholderPrimary covering split B|A. * * To run this test: `atest FlickerTests:RTLStartSecondaryWithPlaceholderTest` */ @@ -43,169 +42,165 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class RTLStartSecondaryWithPlaceholderTest(flicker: FlickerTest) : - ActivityEmbeddingTestBase(flicker) { +class RTLStartSecondaryWithPlaceholderTest(flicker: LegacyFlickerTest) : + ActivityEmbeddingTestBase(flicker) { - /** {@inheritDoc} */ - override val transition: FlickerBuilder.() -> Unit = { - setup { - tapl.setExpectedRotationCheckEnabled(false) - testApp.launchViaIntent(wmHelper) - testApp.launchSecondaryActivityRTL(wmHelper) + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivityRTL(wmHelper) + } + transitions { testApp.launchPlaceholderSplitRTL(wmHelper) } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } } - transitions { - testApp.launchPlaceholderSplitRTL(wmHelper) - } - teardown { - tapl.goHome() - testApp.exit(wmHelper) - } - } - /** - * Main activity and Secondary activity will become invisible because they are covered by - * PlaceholderPrimary activity and PlaceholderSecondary activity. - */ - @Presubmit - @Test - fun assertWindowVisibilities() { - flicker.assertWm { - isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - .then() - .isAppWindowInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - } - flicker.assertWm { - isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - .then() - .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - } - flicker.assertWm { - isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) - .then() - .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) - } - flicker.assertWm { - isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) - .then() - .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) - } - } - - /** - * Main activity and Secondary activity will become invisible because they are covered by - * PlaceholderPrimary activity and PlaceholderSecondary activity. - */ - @Presubmit - @Test - fun assertLayerVisibilities() { - flicker.assertLayers { - this.isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - .then() - .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - } - flicker.assertLayers { - this.isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - .then() - .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - } - flicker.assertLayers { - isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) - .then() - .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) - } - flicker.assertLayers { - isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) - .then() - .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + /** + * Main activity and Secondary activity will become invisible because they are covered by + * PlaceholderPrimary activity and PlaceholderSecondary activity. + */ + @Presubmit + @Test + fun assertWindowVisibilities() { + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + flicker.assertWm { + isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + } + flicker.assertWm { + isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + } } - } - /** Main activity and Secondary activity split is in right-to-left layout direction. */ - @Presubmit - @Test - fun assertWMRTLBeforeTransition() { - flicker.assertWmStart { - val mainActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - val secondaryActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - mainActivityRegion.notOverlaps(secondaryActivityRegion.region) - // secondary activity is on the left, main activity is on the right. - check { "isRTLBeforeTransition" } - .that(mainActivityRegion.region.bounds.left) - .isEqual(secondaryActivityRegion.region.bounds.right) + /** + * Main activity and Secondary activity will become invisible because they are covered by + * PlaceholderPrimary activity and PlaceholderSecondary activity. + */ + @Presubmit + @Test + fun assertLayerVisibilities() { + flicker.assertLayers { + this.isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertLayers { + this.isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + } + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + } } - } - /** Main activity and Secondary activity split is in right-to-left layout direction. */ - @Presubmit - @Test - fun assertLayerRTLBeforeTransition() { - flicker.assertLayersStart { - val mainActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - val secondaryActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - mainActivityRegion.notOverlaps(secondaryActivityRegion.region) - // secondary activity is on the left, main activity is on the right. - check { "isRTLBeforeTransition" } - .that(mainActivityRegion.region.bounds.left) - .isEqual(secondaryActivityRegion.region.bounds.right) + /** Main activity and Secondary activity split is in right-to-left layout direction. */ + @Presubmit + @Test + fun assertWMRTLBeforeTransition() { + flicker.assertWmStart { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } } - } - /** - * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left - * layout direction. - */ - @Presubmit - @Test - fun assertWMRTLAfterTransition() { - flicker.assertWmEnd { - val mainActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - val secondaryActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - mainActivityRegion.notOverlaps(secondaryActivityRegion.region) - // secondary activity is on the left, main activity is on the right. - check { "isRTLBeforeTransition" } - .that(mainActivityRegion.region.bounds.left) - .isEqual(secondaryActivityRegion.region.bounds.right) + /** Main activity and Secondary activity split is in right-to-left layout direction. */ + @Presubmit + @Test + fun assertLayerRTLBeforeTransition() { + flicker.assertLayersStart { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } } - } - /** - * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left - * layout direction. - */ - @Presubmit - @Test - fun assertLayerRTLAfterTransition() { - flicker.assertLayersEnd { - val mainActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) - val secondaryActivityRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) - mainActivityRegion.notOverlaps(secondaryActivityRegion.region) - // Placeholder secondary activity is on the left, placeholder primary activity is on the - // right. - check { "isRTLAfterTransition" } - .that(mainActivityRegion.region.bounds.left) - .isEqual(secondaryActivityRegion.region.bounds.right) + /** + * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left + * layout direction. + */ + @Presubmit + @Test + fun assertWMRTLAfterTransition() { + flicker.assertWmEnd { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } } - } - companion object { /** - * Creates the test configurations. - * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. + * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left + * layout direction. */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() + @Presubmit + @Test + fun assertLayerRTLAfterTransition() { + flicker.assertLayersEnd { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // Placeholder secondary activity is on the left, placeholder primary activity is on the + // right. + check { "isRTLAfterTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } - } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt index c0c738b16c2a..7a582f7de200 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt @@ -21,8 +21,8 @@ import android.tools.common.datatypes.Rect import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.FixMethodOrder @@ -34,8 +34,8 @@ import org.junit.runners.Parameterized /** * Test closing a secondary activity in a split. * - * Setup: Launch A|B in split with B being the secondary activity. - * Transitions: Finish B and expect A to become fullscreen. + * Setup: Launch A|B in split with B being the secondary activity. Transitions: Finish B and expect + * A to become fullscreen. * * To run this test: `atest FlickerTests:CloseSecondaryActivityInSplitTest` */ @@ -43,95 +43,93 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseSecondaryActivityInSplitTest(flicker: FlickerTest) : - ActivityEmbeddingTestBase(flicker) { +class CloseSecondaryActivityInSplitTest(flicker: LegacyFlickerTest) : + ActivityEmbeddingTestBase(flicker) { - override val transition: FlickerBuilder.() -> Unit = { - setup { - tapl.setExpectedRotationCheckEnabled(false) - // Launches fullscreen A. - testApp.launchViaIntent(wmHelper) - // Launches a split A|B and waits for both activities to show. - testApp.launchSecondaryActivity(wmHelper) - // Get fullscreen bounds - startDisplayBounds = - wmHelper.currentState.layerState.physicalDisplayBounds ?: - error("Can't get display bounds") + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + // Launches fullscreen A. + testApp.launchViaIntent(wmHelper) + // Launches a split A|B and waits for both activities to show. + testApp.launchSecondaryActivity(wmHelper) + // Get fullscreen bounds + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds + ?: error("Can't get display bounds") + } + transitions { + // Finish secondary activity B. + testApp.finishSecondaryActivity(wmHelper) + // Expect the main activity A to expand into fullscreen. + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() + } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } } - transitions { - // Finish secondary activity B. - testApp.finishSecondaryActivity(wmHelper) - // Expect the main activity A to expand into fullscreen. - wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() - } - teardown { - tapl.goHome() - testApp.exit(wmHelper) - } - } - /** Main activity is always visible and becomes fullscreen in the end. */ - @Presubmit - @Test - fun mainActivityWindowBecomesFullScreen() { - flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } - flicker.assertWmEnd { - this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - .coversExactly(startDisplayBounds) + /** Main activity is always visible and becomes fullscreen in the end. */ + @Presubmit + @Test + fun mainActivityWindowBecomesFullScreen() { + flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + flicker.assertWmEnd { + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .coversExactly(startDisplayBounds) + } } - } - /** Main activity surface is animated from split to fullscreen. */ - @Presubmit - @Test - fun mainActivityLayerIsAlwaysVisible() { - flicker.assertLayers { - isVisible( - ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT.or( - ComponentNameMatcher.TRANSITION_SNAPSHOT - ) - ) - } - flicker.assertLayersEnd { - isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - .isInvisible(ComponentNameMatcher.TRANSITION_SNAPSHOT) + /** Main activity surface is animated from split to fullscreen. */ + @Presubmit + @Test + fun mainActivityLayerIsAlwaysVisible() { + flicker.assertLayers { + isVisible( + ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT.or( + ComponentNameMatcher.TRANSITION_SNAPSHOT + ) + ) + } + flicker.assertLayersEnd { + isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .isInvisible(ComponentNameMatcher.TRANSITION_SNAPSHOT) + } } - } - /** Secondary activity should destroy and become invisible. */ - @Presubmit - @Test - fun secondaryActivityWindowFinishes() { - flicker.assertWm { - contains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - .then() - .notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + /** Secondary activity should destroy and become invisible. */ + @Presubmit + @Test + fun secondaryActivityWindowFinishes() { + flicker.assertWm { + contains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } } - } - @Presubmit - @Test - fun secondaryActivityLayerFinishes() { - flicker.assertLayers { - isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - .then() - .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + @Presubmit + @Test + fun secondaryActivityLayerFinishes() { + flicker.assertLayers { + isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } } - } - companion object { - /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY - /** - * Creates the test configurations. - * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() + companion object { + /** {@inheritDoc} */ + private var startDisplayBounds = Rect.EMPTY + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } - } - }
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt index 00316ea249b7..48aaebd14f56 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt @@ -16,11 +16,13 @@ package com.android.server.wm.flicker.activityembedding +import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.Rect import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.FixMethodOrder @@ -28,14 +30,12 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -import android.tools.common.datatypes.Rect /** * Test launching an activity with AlwaysExpand rule. * - * Setup: Launch A|B in split with B being the secondary activity. - * Transitions: - * A start C with alwaysExpand=true, expect C to launch in fullscreen and cover split A|B. + * Setup: Launch A|B in split with B being the secondary activity. Transitions: A start C with + * alwaysExpand=true, expect C to launch in fullscreen and cover split A|B. * * To run this test: `atest FlickerTests:MainActivityStartsSecondaryWithAlwaysExpandTest` */ @@ -43,98 +43,109 @@ import android.tools.common.datatypes.Rect @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: FlickerTest) : - ActivityEmbeddingTestBase(flicker) { +class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: LegacyFlickerTest) : + ActivityEmbeddingTestBase(flicker) { - /** {@inheritDoc} */ - override val transition: FlickerBuilder.() -> Unit = { - setup { - tapl.setExpectedRotationCheckEnabled(false) - // Launch a split - testApp.launchViaIntent(wmHelper) - testApp.launchSecondaryActivity(wmHelper) - startDisplayBounds = - wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") - } - transitions { - // Launch C with alwaysExpand - testApp.launchAlwaysExpandActivity(wmHelper) - } - teardown { - tapl.goHome() - testApp.exit(wmHelper) + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + // Launch a split + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivity(wmHelper) + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") + } + transitions { + // Launch C with alwaysExpand + testApp.launchAlwaysExpandActivity(wmHelper) + } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } } - } - /** Transition begins with a split. */ - @Presubmit - @Test - fun startsWithSplit() { - flicker.assertWmStart { - this.isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - } - flicker.assertWmStart { - this.isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - } - } + @FlakyTest(bugId = 286952194) + @Presubmit + @Test + override fun navBarWindowIsVisibleAtStartAndEnd() {} + + @FlakyTest(bugId = 286952194) + @Presubmit + @Test + override fun statusBarWindowIsAlwaysVisible() {} + @FlakyTest(bugId = 286952194) + @Presubmit + @Test + override fun statusBarLayerPositionAtStartAndEnd() {} - /** Main activity should become invisible after being covered by always expand activity. */ - @Presubmit - @Test - fun mainActivityLayerBecomesInvisible() { - flicker.assertLayers { - isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - .then() - .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + /** Transition begins with a split. */ + @Presubmit + @Test + fun startsWithSplit() { + flicker.assertWmStart { + this.isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertWmStart { + this.isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } } - } - /** Secondary activity should become invisible after being covered by always expand activity. */ - @Presubmit - @Test - fun secondaryActivityLayerBecomesInvisible() { - flicker.assertLayers { - isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - .then() - .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + /** Main activity should become invisible after being covered by always expand activity. */ + @Presubmit + @Test + fun mainActivityLayerBecomesInvisible() { + flicker.assertLayers { + isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } } - } - /** At the end of transition always expand activity is in fullscreen. */ - @Presubmit - @Test - fun endsWithAlwaysExpandActivityCoveringFullScreen() { - flicker.assertWmEnd { - this.visibleRegion(ActivityEmbeddingAppHelper.ALWAYS_EXPAND_ACTIVITY_COMPONENT) - .coversExactly(startDisplayBounds) + /** Secondary activity should become invisible after being covered by always expand activity. */ + @Presubmit + @Test + fun secondaryActivityLayerBecomesInvisible() { + flicker.assertLayers { + isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } } - } - /** Always expand activity is on top of the split. */ - @Presubmit - @Test - fun endsWithAlwaysExpandActivityOnTop() { - flicker.assertWmEnd { - this.isAppWindowOnTop( - ActivityEmbeddingAppHelper.ALWAYS_EXPAND_ACTIVITY_COMPONENT) + /** At the end of transition always expand activity is in fullscreen. */ + @Presubmit + @Test + fun endsWithAlwaysExpandActivityCoveringFullScreen() { + flicker.assertWmEnd { + this.visibleRegion(ActivityEmbeddingAppHelper.ALWAYS_EXPAND_ACTIVITY_COMPONENT) + .coversExactly(startDisplayBounds) + } } - } - companion object { - /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY - /** - * Creates the test configurations. - * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() + /** Always expand activity is on top of the split. */ + @FlakyTest(bugId = 286952194) + @Presubmit + @Test + fun endsWithAlwaysExpandActivityOnTop() { + flicker.assertWmEnd { + this.isAppWindowOnTop(ActivityEmbeddingAppHelper.ALWAYS_EXPAND_ACTIVITY_COMPONENT) + } } - } -} + companion object { + /** {@inheritDoc} */ + private var startDisplayBounds = Rect.EMPTY + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt index ed17059e79e7..27a5bd0da628 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt @@ -19,8 +19,8 @@ package com.android.server.wm.flicker.activityembedding import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.FixMethodOrder @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenActivityEmbeddingPlaceholderSplitTest(flicker: FlickerTest) : +class OpenActivityEmbeddingPlaceholderSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) { /** {@inheritDoc} */ @@ -117,13 +117,11 @@ class OpenActivityEmbeddingPlaceholderSplitTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt index 863828881d36..16dbfce15ab5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.FixMethodOrder @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenActivityEmbeddingSecondaryToSplitTest(flicker: FlickerTest) : +class OpenActivityEmbeddingSecondaryToSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) { /** {@inheritDoc} */ @@ -110,13 +110,11 @@ class OpenActivityEmbeddingSecondaryToSplitTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt index 39ae8e2d9799..856c9e2abd07 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt @@ -18,13 +18,13 @@ package com.android.server.wm.flicker.activityembedding import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher -import com.android.server.wm.flicker.rotation.RotationTransition import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import com.android.server.wm.flicker.rotation.RotationTransition import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -34,8 +34,8 @@ import org.junit.runners.Parameterized /** * Tests rotating two activities in an Activity Embedding split. * - * Setup: Launch A|B in split with B being the secondary activity. - * Transitions: Rotate display, and expect A and B to split evenly in new rotation. + * Setup: Launch A|B in split with B being the secondary activity. Transitions: Rotate display, and + * expect A and B to split evenly in new rotation. * * To run this test: `atest FlickerTests:RotateSplitNoChangeTest` */ @@ -43,100 +43,104 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class RotateSplitNoChangeTest(flicker: FlickerTest) : RotationTransition(flicker) { +open class RotateSplitNoChangeTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) { - override val testApp = ActivityEmbeddingAppHelper(instrumentation) - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - setup { - testApp.launchViaIntent(wmHelper) - testApp.launchSecondaryActivity(wmHelper) - } - } + override val testApp = ActivityEmbeddingAppHelper(instrumentation) + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + setup { + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivity(wmHelper) + } + } - /** - * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't - * flicker, and disappears before the transition is complete - */ - @Presubmit - @Test - fun rotationLayerAppearsAndVanishes() { - flicker.assertLayers { - this.isVisible(testApp) - .then() - .isVisible(ComponentNameMatcher.ROTATION) - .then() - .isVisible(testApp) - .isInvisible(ComponentNameMatcher.ROTATION) + /** + * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't + * flicker, and disappears before the transition is complete + */ + @Presubmit + @Test + fun rotationLayerAppearsAndVanishes() { + flicker.assertLayers { + this.isVisible(testApp) + .then() + .isVisible(ComponentNameMatcher.ROTATION) + .then() + .isVisible(testApp) + .isInvisible(ComponentNameMatcher.ROTATION) + } } - } - /** - * Overrides inherited assertion because in AE Split, the main and secondary activity are separate - * layers, each covering up exactly half of the display. - */ - @Presubmit - @Test - override fun appLayerRotates_StartingPos() { - flicker.assertLayersStart { - this.entry.displays.map { display -> - val leftLayerRegion = this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - val rightLayerRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - // Compare dimensions of two splits, given we're using default split attributes, - // both activities take up the same visible size on the display. - check{"height"}.that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height) - check{"width"}.that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width) - leftLayerRegion.notOverlaps(rightLayerRegion.region) - // Layers of two activities sum to be fullscreen size on display. - leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) - } + /** + * Overrides inherited assertion because in AE Split, the main and secondary activity are + * separate layers, each covering up exactly half of the display. + */ + @Presubmit + @Test + override fun appLayerRotates_StartingPos() { + flicker.assertLayersStart { + this.entry.displays.map { display -> + val leftLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val rightLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + // Compare dimensions of two splits, given we're using default split attributes, + // both activities take up the same visible size on the display. + check { "height" } + .that(leftLayerRegion.region.height) + .isEqual(rightLayerRegion.region.height) + check { "width" } + .that(leftLayerRegion.region.width) + .isEqual(rightLayerRegion.region.width) + leftLayerRegion.notOverlaps(rightLayerRegion.region) + // Layers of two activities sum to be fullscreen size on display. + leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) + } + } } - } - /** - * Verifies dimensions of both split activities hold their invariance after transition too. - */ - @Presubmit - @Test - override fun appLayerRotates_EndingPos() { - flicker.assertLayersEnd { - this.entry.displays.map { display -> - val leftLayerRegion = this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - val rightLayerRegion = - this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - check{"height"}.that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height) - check{"width"}.that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width) - leftLayerRegion.notOverlaps(rightLayerRegion.region) - leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) - } + /** Verifies dimensions of both split activities hold their invariance after transition too. */ + @Presubmit + @Test + override fun appLayerRotates_EndingPos() { + flicker.assertLayersEnd { + this.entry.displays.map { display -> + val leftLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val rightLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + check { "height" } + .that(leftLayerRegion.region.height) + .isEqual(rightLayerRegion.region.height) + check { "width" } + .that(leftLayerRegion.region.width) + .isEqual(rightLayerRegion.region.width) + leftLayerRegion.notOverlaps(rightLayerRegion.region) + leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) + } + } } - } - /** Both activities in split should remain visible during rotation. */ - @Presubmit - @Test - fun bothActivitiesAreAlwaysVisible() { - flicker.assertWm { - isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - } - flicker.assertWm { - isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + /** Both activities in split should remain visible during rotation. */ + @Presubmit + @Test + fun bothActivitiesAreAlwaysVisible() { + flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } } - } - companion object { - /** - * Creates the test configurations. - * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() + companion object { + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.rotationTests() } - } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index 10b71ff1efd7..865d5b4b771d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.FlakyTest import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test @@ -76,7 +76,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseAppBackButtonTest(flicker: FlickerTest) : CloseAppTransition(flicker) { +open class CloseAppBackButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { @@ -96,13 +96,11 @@ open class CloseAppBackButtonTest(flicker: FlickerTest) : CloseAppTransition(fli /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt index 9fa840190fbf..c1086332a656 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.close import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -29,18 +29,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppBackButtonTestCfArm(flicker: FlickerTest) : CloseAppBackButtonTest(flicker) { +class CloseAppBackButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppBackButtonTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index e5bd350019c8..ea9710c633b1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.FlakyTest import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test @@ -76,7 +76,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseAppHomeButtonTest(flicker: FlickerTest) : CloseAppTransition(flicker) { +open class CloseAppHomeButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { @@ -101,8 +101,6 @@ open class CloseAppHomeButtonTest(flicker: FlickerTest) : CloseAppTransition(fli /** Creates the test configurations. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt index 136995a78fd7..d65555a97d78 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.close import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -29,13 +29,11 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppHomeButtonTestCfArm(flicker: FlickerTest) : CloseAppHomeButtonTest(flicker) { +class CloseAppHomeButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppHomeButtonTest(flicker) { companion object { /** Creates the test configurations. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index 4570fa23dc43..8737edb445f1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -16,11 +16,14 @@ package com.android.server.wm.flicker.close +import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.helpers.setRotation @@ -28,7 +31,7 @@ import com.android.server.wm.flicker.replacesLayer import org.junit.Test /** Base test class for transitions that close an app back to the launcher screen */ -abstract class CloseAppTransition(flicker: FlickerTest) : BaseTest(flicker) { +abstract class CloseAppTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) /** {@inheritDoc} */ @@ -71,4 +74,21 @@ abstract class CloseAppTransition(flicker: FlickerTest) : BaseTest(flicker) { ignoreEntriesWithRotationLayer = flicker.scenario.isLandscapeOrSeascapeAtStart ) } + + /** {@inheritDoc} */ + @Presubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + flicker.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(ComponentNameMatcher.NAV_BAR) + ) + } + } + + @FlakyTest(bugId = 288369951) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntryWithNavBar() { + flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() } + } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt index 793c68ea376d..351eb1e5d2c3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt @@ -75,7 +75,7 @@ constructor( .StateSyncBuilder() .withActivityRemoved(SECONDARY_ACTIVITY_COMPONENT) .waitForAndVerify() - } + } /** * Clicks the button to launch a secondary activity with alwaysExpand enabled, which will launch @@ -83,21 +83,19 @@ constructor( */ fun launchAlwaysExpandActivity(wmHelper: WindowManagerStateHelper) { val launchButton = - uiDevice.wait( - Until.findObject( - By.res(getPackage(), - "launch_always_expand_activity_button")), - FIND_TIMEOUT - ) + uiDevice.wait( + Until.findObject(By.res(getPackage(), "launch_always_expand_activity_button")), + FIND_TIMEOUT + ) require(launchButton != null) { "Can't find launch always expand activity button on screen." } launchButton.click() wmHelper - .StateSyncBuilder() - .withActivityState(ALWAYS_EXPAND_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) - .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_PAUSED) - .waitForAndVerify() + .StateSyncBuilder() + .withActivityState(ALWAYS_EXPAND_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_PAUSED) + .waitForAndVerify() } /** @@ -106,21 +104,19 @@ constructor( */ fun launchSecondaryActivityRTL(wmHelper: WindowManagerStateHelper) { val launchButton = - uiDevice.wait( - Until.findObject( - By.res(getPackage(), - "launch_secondary_activity_rtl_button")), - FIND_TIMEOUT - ) + uiDevice.wait( + Until.findObject(By.res(getPackage(), "launch_secondary_activity_rtl_button")), + FIND_TIMEOUT + ) require(launchButton != null) { "Can't find launch secondary activity rtl button on screen." } launchButton.click() wmHelper - .StateSyncBuilder() - .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) - .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) - .waitForAndVerify() + .StateSyncBuilder() + .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .waitForAndVerify() } /** @@ -148,21 +144,17 @@ constructor( */ fun launchPlaceholderSplitRTL(wmHelper: WindowManagerStateHelper) { val launchButton = - uiDevice.wait( - Until.findObject( - By.res(getPackage(), - "launch_placeholder_split_rtl_button")), - FIND_TIMEOUT - ) - require(launchButton != null) { - "Can't find launch placeholder split button on screen." - } + uiDevice.wait( + Until.findObject(By.res(getPackage(), "launch_placeholder_split_rtl_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { "Can't find launch placeholder split button on screen." } launchButton.click() wmHelper - .StateSyncBuilder() - .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED) - .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED) - .waitForAndVerify() + .StateSyncBuilder() + .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED) + .waitForAndVerify() } companion object { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index 9227e07f5b11..5c8cbe49d7cf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt @@ -19,7 +19,7 @@ package com.android.server.wm.flicker.helpers import android.tools.common.Rotation -import android.tools.device.flicker.legacy.IFlickerTestData +import android.tools.device.flicker.legacy.FlickerTestData import android.tools.device.flicker.rules.ChangeDisplayOrientationRule /** @@ -27,7 +27,7 @@ import android.tools.device.flicker.rules.ChangeDisplayOrientationRule * * @param rotation New device rotation */ -fun IFlickerTestData.setRotation(rotation: Rotation) = +fun FlickerTestData.setRotation(rotation: Rotation) = ChangeDisplayOrientationRule.setRotation( rotation, instrumentation, diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt index 34581bbb4516..d83b6d39fcd8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation -import android.tools.common.datatypes.Region import android.tools.common.datatypes.Rect +import android.tools.common.datatypes.Region import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.helpers.FIND_TIMEOUT @@ -81,15 +81,20 @@ constructor( displayBounds: Rect, right: Boolean ) { - wmHelper.StateSyncBuilder().add("letterboxAppRepositioned") { - val letterboxAppWindow = getWindowRegion(wmHelper) - val appRegionBounds = letterboxAppWindow.bounds - val appWidth = appRegionBounds.width - return@add if (right) appRegionBounds.left == displayBounds.right - appWidth && - appRegionBounds.right == displayBounds.right - else appRegionBounds.left == displayBounds.left && - appRegionBounds.right == displayBounds.left + appWidth - }.waitForAndVerify() + wmHelper + .StateSyncBuilder() + .add("letterboxAppRepositioned") { + val letterboxAppWindow = getWindowRegion(wmHelper) + val appRegionBounds = letterboxAppWindow.bounds + val appWidth = appRegionBounds.width + return@add if (right) + appRegionBounds.left == displayBounds.right - appWidth && + appRegionBounds.right == displayBounds.right + else + appRegionBounds.left == displayBounds.left && + appRegionBounds.right == displayBounds.left + appWidth + } + .waitForAndVerify() } fun waitForAppToMoveVerticallyTo( @@ -98,15 +103,20 @@ constructor( navBarHeight: Int, bottom: Boolean ) { - wmHelper.StateSyncBuilder().add("letterboxAppRepositioned") { - val letterboxAppWindow = getWindowRegion(wmHelper) - val appRegionBounds = letterboxAppWindow.bounds - val appHeight = appRegionBounds.height - return@add if (bottom) appRegionBounds.bottom == displayBounds.bottom && - appRegionBounds.top == (displayBounds.bottom - appHeight + navBarHeight) - else appRegionBounds.top == displayBounds.top && - appRegionBounds.bottom == displayBounds.top + appHeight - }.waitForAndVerify() + wmHelper + .StateSyncBuilder() + .add("letterboxAppRepositioned") { + val letterboxAppWindow = getWindowRegion(wmHelper) + val appRegionBounds = letterboxAppWindow.bounds + val appHeight = appRegionBounds.height + return@add if (bottom) + appRegionBounds.bottom == displayBounds.bottom && + appRegionBounds.top == (displayBounds.bottom - appHeight + navBarHeight) + else + appRegionBounds.top == displayBounds.top && + appRegionBounds.bottom == displayBounds.top + appHeight + } + .waitForAndVerify() } private fun getWindowRegion(wmHelper: WindowManagerStateHelper): Region { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt index 7e0632d216ea..98446c1a76b2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt @@ -22,8 +22,8 @@ import android.tools.common.flicker.subject.region.RegionSubject import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper @@ -37,7 +37,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeOnDismissPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker) { +open class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation) /** {@inheritDoc} */ @@ -101,10 +101,9 @@ open class CloseImeOnDismissPopupDialogTest(flicker: FlickerTest) : BaseTest(fli companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt index c355e2708657..d87a1daebd79 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.ime import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,15 +28,14 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeOnDismissPopupDialogTestCfArm(flicker: FlickerTest) : +class CloseImeOnDismissPopupDialogTestCfArm(flicker: LegacyFlickerTest) : CloseImeOnDismissPopupDialogTest(flicker) { companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt index 122a6cbbb98f..b995d3df8055 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt @@ -16,15 +16,14 @@ package com.android.server.wm.flicker.ime -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper @@ -42,7 +41,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) { +open class CloseImeOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeAppHelper(instrumentation) /** {@inheritDoc} */ @@ -104,7 +103,6 @@ open class CloseImeOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) { @Presubmit @Test @PlatinumTest(focusArea = "ime") - @IwTest(focusArea = "ime") override fun cujCompleted() { super.cujCompleted() imeLayerBecomesInvisible() @@ -117,10 +115,9 @@ open class CloseImeOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) { companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt index 0fe52df5bb25..3b5bfa986119 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,4 +26,4 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeOnGoHomeTestCfArm(flicker: FlickerTest) : CloseImeOnGoHomeTest(flicker) +class CloseImeOnGoHomeTestCfArm(flicker: LegacyFlickerTest) : CloseImeOnGoHomeTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt index cbe03dcd932a..765bb4cf3067 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt @@ -21,8 +21,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper @@ -49,7 +49,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeShownOnAppStartOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) { +open class CloseImeShownOnAppStartOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) /** {@inheritDoc} */ @@ -94,11 +94,10 @@ open class CloseImeShownOnAppStartOnGoHomeTest(flicker: FlickerTest) : BaseTest( companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // b/190352379 (IME doesn't show on app launch in 90 degrees) supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt index 5aacb3011e79..58411cc63004 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeShownOnAppStartOnGoHomeTestCfArm(flicker: FlickerTest) : +open class CloseImeShownOnAppStartOnGoHomeTestCfArm(flicker: LegacyFlickerTest) : CloseImeShownOnAppStartOnGoHomeTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt index 82c390b77d59..c87217b30783 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt @@ -21,8 +21,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper @@ -49,7 +49,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker) { +open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: LegacyFlickerTest) : + BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) /** {@inheritDoc} */ @@ -88,11 +89,10 @@ open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: FlickerTest) : B companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( // b/190352379 (IME doesn't show on app launch in 90 degrees) supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt index eb81aed35011..41b06c0b4bd3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeShownOnAppStartToAppOnPressBackTestCfArm(flicker: FlickerTest) : +class CloseImeShownOnAppStartToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) : CloseImeShownOnAppStartToAppOnPressBackTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt index 5aa43820e5cb..c74870bef102 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt @@ -16,14 +16,13 @@ package com.android.server.wm.flicker.ime -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper @@ -43,7 +42,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker) { +open class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeAppHelper(instrumentation) /** {@inheritDoc} */ @@ -105,7 +104,6 @@ open class CloseImeToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker @Presubmit @Test @PlatinumTest(focusArea = "ime") - @IwTest(focusArea = "ime") override fun cujCompleted() { super.cujCompleted() imeLayerBecomesInvisible() @@ -117,8 +115,6 @@ open class CloseImeToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt index db1440b0c5b8..104af225ee2e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeToAppOnPressBackTestCfArm(flicker: FlickerTest) : +class CloseImeToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) : CloseImeToAppOnPressBackTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt index 6731089f8903..21fd590025bc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt @@ -17,14 +17,13 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper @@ -45,7 +44,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(flicker) { +open class CloseImeToHomeOnFinishActivityTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val simpleApp = SimpleAppHelper(instrumentation) private val testApp = ImeAppHelper(instrumentation) @@ -73,7 +72,6 @@ open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(f @Presubmit @Test @PlatinumTest(focusArea = "ime") - @IwTest(focusArea = "ime") override fun cujCompleted() { runAndIgnoreAssumptionViolation { entireScreenCovered() } runAndIgnoreAssumptionViolation { statusBarLayerIsVisibleAtStartAndEnd() } @@ -92,10 +90,9 @@ open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(f companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt index 405ab6bcd9d6..0e1838570bab 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeToHomeOnFinishActivityTestCfArm(flicker: FlickerTest) : +open class CloseImeToHomeOnFinishActivityTestCfArm(flicker: LegacyFlickerTest) : CloseImeToHomeOnFinishActivityTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt index 8e3371986056..777231e001f7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt @@ -19,21 +19,21 @@ package com.android.server.wm.flicker.ime import android.tools.common.traces.component.ComponentNameMatcher -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest -fun FlickerTest.imeLayerBecomesVisible() { +fun LegacyFlickerTest.imeLayerBecomesVisible() { assertLayers { this.isInvisible(ComponentNameMatcher.IME).then().isVisible(ComponentNameMatcher.IME) } } -fun FlickerTest.imeLayerBecomesInvisible() { +fun LegacyFlickerTest.imeLayerBecomesInvisible() { assertLayers { this.isVisible(ComponentNameMatcher.IME).then().isInvisible(ComponentNameMatcher.IME) } } -fun FlickerTest.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) { +fun LegacyFlickerTest.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { assertWm { this.isNonAppWindowVisible(ComponentNameMatcher.IME) @@ -47,7 +47,7 @@ fun FlickerTest.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) { } } -fun FlickerTest.imeWindowBecomesVisible() { +fun LegacyFlickerTest.imeWindowBecomesVisible() { assertWm { this.isNonAppWindowInvisible(ComponentNameMatcher.IME) .then() @@ -55,7 +55,7 @@ fun FlickerTest.imeWindowBecomesVisible() { } } -fun FlickerTest.imeWindowBecomesInvisible() { +fun LegacyFlickerTest.imeWindowBecomesInvisible() { assertWm { this.isNonAppWindowVisible(ComponentNameMatcher.IME) .then() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt index 19bbf0cb92b0..976ac82c8bb5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt @@ -22,8 +22,8 @@ import android.tools.common.flicker.subject.region.RegionSubject import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest @@ -42,7 +42,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenImeWindowToFixedPortraitAppTest(flicker: FlickerTest) : BaseTest(flicker) { +class OpenImeWindowToFixedPortraitAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) /** {@inheritDoc} */ @@ -99,19 +99,18 @@ class OpenImeWindowToFixedPortraitAppTest(flicker: FlickerTest) : BaseTest(flick /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf( Rotation.ROTATION_90, ), supportedNavigationModes = listOf(NavBar.MODE_3BUTTON, NavBar.MODE_GESTURAL) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt index 03f21f95e61e..db80001ccd78 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeOnAppStartWhenLaunchingAppCfArmTest(flicker: FlickerTest) : +class ShowImeOnAppStartWhenLaunchingAppCfArmTest(flicker: LegacyFlickerTest) : ShowImeOnAppStartWhenLaunchingAppTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt index 496165ab5b09..44bd8c8688e9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt @@ -16,17 +16,17 @@ package com.android.server.wm.flicker.ime +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.common.Rotation -import android.platform.test.annotations.Postsubmit import android.tools.common.Timestamp -import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.flicker.subject.exceptions.ExceptionMessageBuilder import android.tools.common.flicker.subject.exceptions.InvalidPropertyException +import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: FlickerTest) : +open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val imeTestApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) @@ -93,33 +93,35 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Fl layerTraceEntries.zipWithNext { prev, next -> val prevSnapshotLayerVisible = - ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers) + ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers) val nextSnapshotLayerVisible = - ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers) + ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers) - if (imeSnapshotRemovedTimestamp == null && - (prevSnapshotLayerVisible && !nextSnapshotLayerVisible)) { + if ( + imeSnapshotRemovedTimestamp == null && + (prevSnapshotLayerVisible && !nextSnapshotLayerVisible) + ) { imeSnapshotRemovedTimestamp = next.timestamp } } // if so, make an assertion imeSnapshotRemovedTimestamp?.let { timestamp -> - val stateAfterSnapshot = layerTrace?.getEntryAt(timestamp) - ?: error("State not found for $timestamp") + val stateAfterSnapshot = + layerTrace?.getEntryAt(timestamp) ?: error("State not found for $timestamp") - val imeLayers = ComponentNameMatcher.IME - .filterLayers(stateAfterSnapshot.visibleLayers.toList()) + val imeLayers = + ComponentNameMatcher.IME.filterLayers(stateAfterSnapshot.visibleLayers.toList()) require(imeLayers.isNotEmpty()) { "IME layer not found" } if (imeLayers.any { it.color.a != 1.0f }) { - val errorMsgBuilder = ExceptionMessageBuilder() + val errorMsgBuilder = + ExceptionMessageBuilder() .setTimestamp(timestamp) .forInvalidProperty("IME layer alpha") .setExpected("is 1.0") .setActual("not 1.0") - .addExtraDescription("Filter", - ComponentNameMatcher.IME.toLayerIdentifier()) + .addExtraDescription("Filter", ComponentNameMatcher.IME.toLayerIdentifier()) throw InvalidPropertyException(errorMsgBuilder) } } @@ -129,15 +131,14 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Fl /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_90) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt index 3aca2a07f7cd..6d9ea2209d91 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm(flicker: FlickerTest) : +class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm(flicker: LegacyFlickerTest) : ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt index f64ad53bf2f7..ae05e37a84cc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt @@ -21,8 +21,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.reopenAppFromOverview import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest @@ -41,7 +41,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: FlickerTest) : +open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) @@ -140,10 +140,9 @@ open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: FlickerTes companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt index e1aa4182c331..92b3968216e5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.ime import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,16 +28,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm(flicker: FlickerTest) : +class ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) : ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker) { companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt index 11bc7b9f29c0..c991651a9d08 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt @@ -22,8 +22,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper @@ -44,7 +44,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Presubmit -open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: FlickerTest) : +open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = SimpleAppHelper(instrumentation) private val imeTestApp = @@ -121,12 +121,11 @@ open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: Flicker companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), supportedRotations = listOf(Rotation.ROTATION_0) ) - } private const val TAG_IME_VISIBLE = "imeVisible" private const val TAG_IME_INVISIBLE = "imeInVisible" diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt index 0f57467c0449..09bfacc582e5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt @@ -18,7 +18,7 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,5 +28,5 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Presubmit -open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm(flicker: FlickerTest) : +open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm(flicker: LegacyFlickerTest) : ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt index 46967db55086..976352146fc0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt @@ -21,8 +21,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper @@ -79,7 +79,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: FlickerTest) : BaseTest(flicker) { +open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) private val initializeApp = ImeStateInitializeHelper(instrumentation) @@ -123,15 +123,14 @@ open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: FlickerTest) : BaseTes /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt index c70e3cb773d4..7bd58252d3ba 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt @@ -16,14 +16,13 @@ package com.android.server.wm.flicker.ime -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper @@ -38,7 +37,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest(flicker) { +open class ShowImeWhenFocusingOnInputFieldTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeAppHelper(instrumentation) /** {@inheritDoc} */ @@ -54,7 +53,6 @@ open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest( @Presubmit @Test @PlatinumTest(focusArea = "ime") - @IwTest(focusArea = "ime") override fun cujCompleted() { super.cujCompleted() imeWindowBecomesVisible() @@ -81,10 +79,9 @@ open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest( companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt index f5b22949e6b4..f8c814988200 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeWhenFocusingOnInputFieldTestCfArm(flicker: FlickerTest) : +class ShowImeWhenFocusingOnInputFieldTestCfArm(flicker: LegacyFlickerTest) : ShowImeWhenFocusingOnInputFieldTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt index 277b91558416..0b168baa6622 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt @@ -21,8 +21,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars @@ -45,7 +45,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeWhileDismissingThemedPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker) { +open class ShowImeWhileDismissingThemedPopupDialogTest(flicker: LegacyFlickerTest) : + BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) /** {@inheritDoc} */ @@ -84,15 +85,14 @@ open class ShowImeWhileDismissingThemedPopupDialogTest(flicker: FlickerTest) : B /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt index 8891d26c9e54..ad41b81a6664 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.ime import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,21 +28,20 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeWhileDismissingThemedPopupDialogTestCfArm(flicker: FlickerTest) : +class ShowImeWhileDismissingThemedPopupDialogTestCfArm(flicker: LegacyFlickerTest) : ShowImeWhileDismissingThemedPopupDialogTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt index 9275d6a2f17f..7722c934d11b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt @@ -21,8 +21,8 @@ import android.tools.common.traces.ConditionsFactory import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest @@ -45,7 +45,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeWhileEnteringOverviewTest(flicker: FlickerTest) : BaseTest(flicker) { +open class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val imeTestApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) @@ -205,13 +205,11 @@ open class ShowImeWhileEnteringOverviewTest(flicker: FlickerTest) : BaseTest(fli /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt index fc3971351db7..90fbbf9bb240 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt @@ -17,7 +17,7 @@ package com.android.server.wm.flicker.ime import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -26,5 +26,5 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeWhileEnteringOverviewTestCfArm(flicker: FlickerTest) : +class ShowImeWhileEnteringOverviewTestCfArm(flicker: LegacyFlickerTest) : ShowImeWhileEnteringOverviewTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt index a87fae857509..3461907f71dd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest @@ -57,7 +57,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ActivityTransitionTest(flicker: FlickerTest) : BaseTest(flicker) { +open class ActivityTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation) /** {@inheritDoc} */ @@ -119,13 +119,11 @@ open class ActivityTransitionTest(flicker: FlickerTest) : BaseTest(flicker) { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt index 85344a156d7a..6bbf40ea10f9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.launch import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -27,18 +27,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ActivityTransitionTestCfArm(flicker: FlickerTest) : ActivityTransitionTest(flicker) { +class ActivityTransitionTestCfArm(flicker: LegacyFlickerTest) : ActivityTransitionTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt index 575206591e59..ae1f78a5a4ef 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt @@ -20,8 +20,8 @@ import android.platform.test.annotations.FlakyTest import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder @@ -55,7 +55,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIconColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { +open class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) : + OpenAppFromLauncherTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { @@ -207,13 +208,11 @@ open class OpenAppFromIconColdTest(flicker: FlickerTest) : OpenAppFromLauncherTr /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt index d453c1ae908a..2563bfb03f61 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt @@ -19,8 +19,8 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.FlakyTest import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -32,7 +32,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIconColdTestCfArm(flicker: FlickerTest) : OpenAppFromIconColdTest(flicker) { +class OpenAppFromIconColdTestCfArm(flicker: LegacyFlickerTest) : OpenAppFromIconColdTest(flicker) { @Test @FlakyTest override fun visibleLayersShownMoreThanOneConsecutiveEntry() { @@ -43,13 +43,11 @@ class OpenAppFromIconColdTestCfArm(flicker: FlickerTest) : OpenAppFromIconColdTe /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt index e74731555642..c95c548e7221 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt @@ -19,8 +19,8 @@ package com.android.server.wm.flicker.launch import android.tools.device.apphelpers.CameraAppHelper import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIntentColdAfterCameraTest(flicker: FlickerTest) : +open class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { private val cameraApp = CameraAppHelper(instrumentation) /** {@inheritDoc} */ @@ -62,13 +62,11 @@ open class OpenAppFromIntentColdAfterCameraTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt index 177ad7dc09d1..117cff203ff7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.launch import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -27,19 +27,17 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: FlickerTest) : +class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: LegacyFlickerTest) : OpenAppFromIntentColdAfterCameraTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt index f45f728664cf..33302face72a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation @@ -58,7 +58,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIntentColdTest(flicker: FlickerTest) : +open class OpenAppFromIntentColdTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -83,13 +83,11 @@ open class OpenAppFromIntentColdTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt index 0d695f306c00..45fb453f5df8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt @@ -19,8 +19,8 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.FlakyTest import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -31,7 +31,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIntentColdTestCfArm(flicker: FlickerTest) : OpenAppFromIntentColdTest(flicker) { +class OpenAppFromIntentColdTestCfArm(flicker: LegacyFlickerTest) : + OpenAppFromIntentColdTest(flicker) { @FlakyTest(bugId = 273696733) @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() @@ -40,13 +41,11 @@ class OpenAppFromIntentColdTestCfArm(flicker: FlickerTest) : OpenAppFromIntentCo /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt index a42bff5bf170..12ee7d0bff78 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder @@ -58,7 +58,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIntentWarmTest(flicker: FlickerTest) : +open class OpenAppFromIntentWarmTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit @@ -94,13 +94,11 @@ open class OpenAppFromIntentWarmTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt index b6ffcb3df9f3..1371fd7502d6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.launch import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -29,18 +29,17 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIntentWarmTestCfArm(flicker: FlickerTest) : OpenAppFromIntentWarmTest(flicker) { +class OpenAppFromIntentWarmTestCfArm(flicker: LegacyFlickerTest) : + OpenAppFromIntentWarmTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt index 3d5a8e309b10..62fb5704b2e8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt @@ -18,12 +18,13 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.replacesLayer import org.junit.Test /** Base class for app launch tests */ -abstract class OpenAppFromLauncherTransition(flicker: FlickerTest) : OpenAppTransition(flicker) { +abstract class OpenAppFromLauncherTransition(flicker: LegacyFlickerTest) : + OpenAppTransition(flicker) { /** Checks that the focus changes from the [ComponentNameMatcher.LAUNCHER] to [testApp] */ @Presubmit diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt index fd4272600d55..74563a28017f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt @@ -21,8 +21,8 @@ import android.platform.test.rule.SettingOverrideRule import android.provider.Settings import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.ClassRule import org.junit.FixMethodOrder @@ -44,7 +44,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -open class OpenAppFromLockscreenNotificationColdTest(flicker: FlickerTest) : +open class OpenAppFromLockscreenNotificationColdTest(flicker: LegacyFlickerTest) : OpenAppFromNotificationColdTest(flicker) { override val openingNotificationsFromLockScreen = true @@ -111,14 +111,12 @@ open class OpenAppFromLockscreenNotificationColdTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() /** * Ensures that posted notifications will be visible on the lockscreen and not suppressed diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt index fd051d50d032..2f922064d60f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt @@ -23,8 +23,8 @@ import android.provider.Settings import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.statusBarLayerPositionAtEnd import org.junit.ClassRule @@ -46,7 +46,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromLockscreenNotificationWarmTest(flicker: FlickerTest) : +class OpenAppFromLockscreenNotificationWarmTest(flicker: LegacyFlickerTest) : OpenAppFromNotificationWarmTest(flicker) { override val openingNotificationsFromLockScreen = true @@ -143,14 +143,12 @@ class OpenAppFromLockscreenNotificationWarmTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() /** * Ensures that posted notifications will be visible on the lockscreen and not suppressed diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt index 37afa8d0caba..0ae514a83da6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt @@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.wakeUpAndGoToHomeScreen import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper @@ -46,7 +46,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: FlickerTest) : +class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlickerTest) : OpenAppFromLockscreenNotificationColdTest(flicker) { private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation) @@ -126,13 +126,11 @@ class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: FlickerTest) /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt index 30c3ec205bb1..687bc1958a5a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt @@ -20,7 +20,7 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.navBarLayerPositionAtEnd import com.android.server.wm.flicker.statusBarLayerPositionAtEnd import org.junit.Assume @@ -28,7 +28,8 @@ import org.junit.Ignore import org.junit.Test /** Base class for app launch tests from lock screen */ -abstract class OpenAppFromLockscreenTransition(flicker: FlickerTest) : OpenAppTransition(flicker) { +abstract class OpenAppFromLockscreenTransition(flicker: LegacyFlickerTest) : + OpenAppTransition(flicker) { /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt index 924d03f6d302..46eb2571165a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt @@ -23,8 +23,8 @@ import android.tools.common.Rotation import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import org.junit.Assume @@ -63,7 +63,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromLockscreenViaIntentTest(flicker: FlickerTest) : +open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : OpenAppFromLockscreenTransition(flicker) { override val testApp = NonResizeableAppHelper(instrumentation) @@ -210,16 +210,15 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt index d873ec5e83e2..6f99ad2fccc3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.statusBarLayerPositionAtEnd import org.junit.FixMethodOrder @@ -42,7 +42,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -open class OpenAppFromNotificationColdTest(flicker: FlickerTest) : +open class OpenAppFromNotificationColdTest(flicker: LegacyFlickerTest) : OpenAppFromNotificationWarmTest(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -99,13 +99,11 @@ open class OpenAppFromNotificationColdTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt index fb2a48c2ad7f..4ec02096f9a3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Postsubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -29,19 +29,17 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -class OpenAppFromNotificationColdTestCfArm(flicker: FlickerTest) : +class OpenAppFromNotificationColdTestCfArm(flicker: LegacyFlickerTest) : OpenAppFromNotificationColdTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt index 99668ecd0b68..3b6c7bd87aa5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.wakeUpAndGoToHomeScreen import android.view.WindowInsets import android.view.WindowManager @@ -53,7 +53,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromNotificationWarmTest(flicker: FlickerTest) : OpenAppTransition(flicker) { +open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) : + OpenAppTransition(flicker) { override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation) open val openingNotificationsFromLockScreen = false @@ -193,13 +194,11 @@ open class OpenAppFromNotificationWarmTest(flicker: FlickerTest) : OpenAppTransi /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt index 2a2597e1ebe8..b5db3b06b7cb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.launch import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -27,19 +27,17 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromNotificationWarmTestCfArm(flicker: FlickerTest) : +class OpenAppFromNotificationWarmTestCfArm(flicker: LegacyFlickerTest) : OpenAppFromNotificationWarmTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 8e1b059b2bc7..1497e50641eb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -22,8 +22,8 @@ import android.tools.common.Rotation import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder @@ -60,7 +60,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromOverviewTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { +open class OpenAppFromOverviewTest(flicker: LegacyFlickerTest) : + OpenAppFromLauncherTransition(flicker) { /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit @@ -106,13 +107,11 @@ open class OpenAppFromOverviewTest(flicker: FlickerTest) : OpenAppFromLauncherTr /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt index ff24190a7aef..9b6c136f54b3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.launch import android.tools.device.flicker.annotation.FlickerServiceCompatible import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -30,18 +30,17 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromOverviewTestCfArm(flicker: FlickerTest) : OpenAppFromOverviewTest(flicker) { +open class OpenAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) : + OpenAppFromOverviewTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index 87a14c69e3ac..bb11be5bb520 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -20,7 +20,7 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.SimpleAppHelper @@ -28,7 +28,7 @@ import com.android.server.wm.flicker.helpers.setRotation import org.junit.Test /** Base class for app launch tests */ -abstract class OpenAppTransition(flicker: FlickerTest) : BaseTest(flicker) { +abstract class OpenAppTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt index 6ee8ae69924a..4a1bd7e165ae 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt @@ -22,8 +22,8 @@ import android.tools.device.apphelpers.CameraAppHelper import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import android.view.KeyEvent import androidx.test.filters.RequiresDevice @@ -60,7 +60,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: FlickerTest) : +class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { private val cameraApp = CameraAppHelper(instrumentation) override val testApp: StandardAppHelper @@ -155,13 +155,11 @@ class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: FlickerTest) : /** * Creates the test configurations. * - * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt index b48611edd738..98e3646f4083 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt @@ -25,8 +25,8 @@ import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerBuilderProvider import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import android.tools.device.helpers.wakeUpAndGoToHomeScreen import androidx.test.filters.RequiresDevice @@ -54,7 +54,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OverrideTaskTransitionTest(val flicker: FlickerTest) { +class OverrideTaskTransitionTest(val flicker: LegacyFlickerTest) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val testApp = SimpleAppHelper(instrumentation) @@ -121,8 +121,6 @@ class OverrideTaskTransitionTest(val flicker: FlickerTest) { companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index d0fd73207c42..39c8ca089dbd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -28,8 +28,8 @@ import android.tools.common.traces.component.ComponentSplashScreenMatcher import android.tools.common.traces.component.IComponentMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.filters.RequiresDevice @@ -58,7 +58,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class TaskTransitionTest(flicker: FlickerTest) : BaseTest(flicker) { +class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val launchNewTaskApp = NewTasksAppHelper(instrumentation) private val simpleApp = SimpleAppHelper(instrumentation) private val wallpaper by lazy { getWallpaperPackage(instrumentation) } @@ -214,8 +214,6 @@ class TaskTransitionTest(flicker: FlickerTest) : BaseTest(flicker) { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests() - } + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt index a8b80ad1d16c..7883910323de 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt @@ -23,8 +23,8 @@ import android.tools.common.datatypes.Rect import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.NonResizeableAppHelper @@ -52,7 +52,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsBackTest(flicker: FlickerTest) : BaseTest(flicker) { +open class QuickSwitchBetweenTwoAppsBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) @@ -243,10 +243,9 @@ open class QuickSwitchBetweenTwoAppsBackTest(flicker: FlickerTest) : BaseTest(fl @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt index f970a79abcb8..f68cd5c4293c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.quickswitch import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,15 +28,14 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: FlickerTest) : +class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: LegacyFlickerTest) : QuickSwitchBetweenTwoAppsBackTest(flicker) { companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt index 96cd8ffa3eff..1c4c7cd89e42 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt @@ -23,8 +23,8 @@ import android.tools.common.datatypes.Rect import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.NonResizeableAppHelper @@ -53,7 +53,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsForwardTest(flicker: FlickerTest) : BaseTest(flicker) { +open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) @@ -261,10 +261,9 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: FlickerTest) : BaseTest @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt index 9f48cdae20f1..3de58acccd79 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.quickswitch import android.tools.common.NavBar import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -28,15 +28,14 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: FlickerTest) : +class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: LegacyFlickerTest) : QuickSwitchBetweenTwoAppsForwardTest(flicker) { companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt index 7e935f03aeb1..641745693187 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt @@ -24,8 +24,8 @@ import android.tools.common.datatypes.Rect import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.SimpleAppHelper @@ -52,7 +52,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchFromLauncherTest(flicker: FlickerTest) : BaseTest(flicker) { +open class QuickSwitchFromLauncherTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = SimpleAppHelper(instrumentation) /** {@inheritDoc} */ @@ -272,12 +272,11 @@ open class QuickSwitchFromLauncherTest(flicker: FlickerTest) : BaseTest(flicker) @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), // TODO: Test with 90 rotation supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt index af671df194ae..84fc75445175 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt @@ -19,8 +19,8 @@ package com.android.server.wm.flicker.quickswitch import android.tools.common.NavBar import android.tools.common.Rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -29,17 +29,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchFromLauncherTestCfArm(flicker: FlickerTest) : +open class QuickSwitchFromLauncherTestCfArm(flicker: LegacyFlickerTest) : QuickSwitchFromLauncherTest(flicker) { companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.nonRotationTests( + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), // TODO: Test with 90 rotation supportedRotations = listOf(Rotation.ROTATION_0) ) - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 7fbcfec40121..842ece38c282 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -16,14 +16,13 @@ package com.android.server.wm.flicker.rotation -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder @@ -35,7 +34,7 @@ import org.junit.runners.Parameterized /** * Test opening an app and cycling through app rotations * - * Currently runs: + * Currently, runs: * ``` * 0 -> 90 degrees * 90 -> 0 degrees @@ -86,7 +85,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ChangeAppRotationTest(flicker: FlickerTest) : RotationTransition(flicker) { +open class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) { override val testApp = SimpleAppHelper(instrumentation) override val transition: FlickerBuilder.() -> Unit get() = { @@ -131,7 +130,6 @@ open class ChangeAppRotationTest(flicker: FlickerTest) : RotationTransition(flic @Test @PlatinumTest(focusArea = "framework") - @IwTest(focusArea = "framework") override fun cujCompleted() { super.cujCompleted() focusChanges() @@ -142,13 +140,11 @@ open class ChangeAppRotationTest(flicker: FlickerTest) : RotationTransition(flic /** * Creates the test configurations. * - * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation - * modes. + * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and + * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() - } + fun getParams() = LegacyFlickerTestFactory.rotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt index 0e6b20fc21c6..1ab5c5ad77b5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -27,18 +27,16 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ChangeAppRotationTestCfArm(flicker: FlickerTest) : ChangeAppRotationTest(flicker) { +class ChangeAppRotationTestCfArm(flicker: LegacyFlickerTest) : ChangeAppRotationTest(flicker) { companion object { /** * Creates the test configurations. * - * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation - * modes. + * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and + * navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() - } + fun getParams() = LegacyFlickerTestFactory.rotationTests() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index fe9da335a675..b0ca4d230e12 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -20,13 +20,13 @@ import android.platform.test.annotations.Presubmit import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTest import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.setRotation import org.junit.Test /** Base class for app rotation tests */ -abstract class RotationTransition(flicker: FlickerTest) : BaseTest(flicker) { +abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { protected abstract val testApp: StandardAppHelper /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index 44ae14a8e4bb..b6ad3cc7fc50 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -16,15 +16,15 @@ package com.android.server.wm.flicker.rotation -import android.platform.test.annotations.IwTest import android.platform.test.annotations.PlatinumTest import android.platform.test.annotations.Presubmit import android.tools.common.ScenarioBuilder +import android.tools.common.ScenarioImpl import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.view.WindowManager import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper @@ -92,7 +92,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(flicker) { +open class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) { override val testApp = SeamlessRotationAppHelper(instrumentation) /** {@inheritDoc} */ @@ -215,7 +215,6 @@ open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(fl @Test @PlatinumTest(focusArea = "framework") - @IwTest(focusArea = "framework") override fun cujCompleted() { appWindowFullScreen() appWindowSeamlessRotation() @@ -238,43 +237,50 @@ open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(fl } companion object { - private val FlickerTest.starveUiThread + private val LegacyFlickerTest.starveUiThread get() = - getConfigValue<Boolean>(ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD) + scenario.getConfigValue<Boolean>( + ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD + ) ?: false @JvmStatic protected fun createConfig( - sourceConfig: FlickerTest, + sourceConfig: LegacyFlickerTest, starveUiThread: Boolean - ): FlickerTest { - val originalScenario = sourceConfig.initialize("createConfig") + ): LegacyFlickerTest { + val originalScenario = sourceConfig.initialize("createConfig") as ScenarioImpl val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else "" val newConfig = ScenarioBuilder() - .fromScenario(originalScenario) + .forClass(originalScenario.testClass) + .withStartRotation(originalScenario.startRotation) + .withEndRotation(originalScenario.endRotation) + .withNavBarMode(originalScenario.navBarMode) + .withExtraConfigs(originalScenario.extraConfig) + .withDescriptionOverride(originalScenario.description) .withExtraConfig( ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD, starveUiThread ) .withDescriptionOverride("${originalScenario.description}$nameExt") - return FlickerTest(newConfig) + return LegacyFlickerTest(newConfig) } /** * Creates the test configurations for seamless rotation based on the default rotation tests - * from [FlickerTestFactory.rotationTests], but adding a flag ( + * from [LegacyFlickerTestFactory.rotationTests], but adding a flag ( * [ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should * starve the UI thread of not */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests().flatMap { sourceConfig -> - val defaultRun = createConfig(sourceConfig, starveUiThread = false) - val busyUiRun = createConfig(sourceConfig, starveUiThread = true) + fun getParams() = + LegacyFlickerTestFactory.rotationTests().flatMap { sourceCfg -> + val legacyCfg = sourceCfg as LegacyFlickerTest + val defaultRun = createConfig(legacyCfg, starveUiThread = false) + val busyUiRun = createConfig(legacyCfg, starveUiThread = true) listOf(defaultRun, busyUiRun) } - } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt index b236d87616ab..592be05c8182 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.rotation import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.FlickerTest -import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import com.android.server.wm.flicker.testapp.ActivityOptions import org.junit.FixMethodOrder import org.junit.runner.RunWith @@ -29,22 +29,23 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SeamlessAppRotationTestCfArm(flicker: FlickerTest) : SeamlessAppRotationTest(flicker) { +open class SeamlessAppRotationTestCfArm(flicker: LegacyFlickerTest) : + SeamlessAppRotationTest(flicker) { companion object { /** * Creates the test configurations for seamless rotation based on the default rotation tests - * from [FlickerTestFactory.rotationTests], but adding a flag ( + * from [LegacyFlickerTestFactory.rotationTests], but adding a flag ( * [ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should * starve the UI thread of not */ @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests().flatMap { sourceConfig -> - val defaultRun = createConfig(sourceConfig, starveUiThread = false) - val busyUiRun = createConfig(sourceConfig, starveUiThread = true) + fun getParams() = + LegacyFlickerTestFactory.rotationTests().flatMap { sourceCfg -> + val legacyCfg = sourceCfg as LegacyFlickerTest + val defaultRun = createConfig(legacyCfg, starveUiThread = false) + val busyUiRun = createConfig(legacyCfg, starveUiThread = true) listOf(defaultRun, busyUiRun) } - } } } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 0f5c003f12fd..52106189840d 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -186,6 +186,8 @@ public class ActivityOptions { public static final String LABEL = "SplitScreenPrimaryActivity"; public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".SplitScreenActivity"); + + public static final String EXTRA_LAUNCH_ADJACENT = "launch_adjacent"; } public static class Secondary { diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java index 70196aee9ff1..8a27252e3483 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java @@ -16,9 +16,14 @@ package com.android.server.wm.flicker.testapp; +import static com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary.EXTRA_LAUNCH_ADJACENT; + import android.app.Activity; +import android.content.Intent; import android.os.Bundle; +import androidx.annotation.Nullable; + public class SplitScreenActivity extends Activity { @Override @@ -26,4 +31,17 @@ public class SplitScreenActivity extends Activity { super.onCreate(icicle); setContentView(R.layout.activity_splitscreen); } + + @Override + protected void onPostCreate(@Nullable Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + final boolean startSecondaryActivity = getIntent().hasExtra(EXTRA_LAUNCH_ADJACENT); + if (startSecondaryActivity) { + final Intent intent = new Intent(this, SplitScreenSecondaryActivity.class); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + startActivity(intent); + } + } } diff --git a/tests/UiBench/Android.bp b/tests/UiBench/Android.bp index 0d2f2ef46cab..90e61c52da68 100644 --- a/tests/UiBench/Android.bp +++ b/tests/UiBench/Android.bp @@ -24,5 +24,6 @@ android_test { "androidx.recyclerview_recyclerview", "androidx.leanback_leanback", ], + certificate: "platform", test_suites: ["device-tests"], } diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml index 4fc6ec71f29c..47211c5fbad1 100644 --- a/tests/UiBench/AndroidManifest.xml +++ b/tests/UiBench/AndroidManifest.xml @@ -18,6 +18,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.android.test.uibench"> + <uses-permission android:name="android.permission.INJECT_EVENTS" /> <application android:allowBackup="false" android:theme="@style/Theme.AppCompat.Light.DarkActionBar" |